diff --git a/autotests/kservicetest.cpp b/autotests/kservicetest.cpp index 276a8ff..abdf99f 100644 --- a/autotests/kservicetest.cpp +++ b/autotests/kservicetest.cpp @@ -1,823 +1,823 @@ /* * Copyright (C) 2006 David Faure * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Library General Public * License version 2 as published by the Free Software Foundation; * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Library General Public License for more details. * * You should have received a copy of the GNU Library General Public License * along with this library; see the file COPYING.LIB. If not, write to * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, * Boston, MA 02110-1301, USA. */ #include "kservicetest.h" #include #include #include #include #include #include #include #include <../src/services/kserviceutil_p.h> #include #include #include #include #include #include #include #include #include #include #include #include #include QTEST_MAIN(KServiceTest) extern KSERVICE_EXPORT int ksycoca_ms_between_checks; static void eraseProfiles() { QString profilerc = QStandardPaths::writableLocation(QStandardPaths::GenericConfigLocation) + "/profilerc"; if (!profilerc.isEmpty()) { QFile::remove(profilerc); } profilerc = QStandardPaths::writableLocation(QStandardPaths::GenericConfigLocation) + "/servicetype_profilerc"; if (!profilerc.isEmpty()) { QFile::remove(profilerc); } } void KServiceTest::initTestCase() { QStandardPaths::enableTestMode(true); QLoggingCategory::setFilterRules(QStringLiteral("kf5.kcoreaddons.kdirwatch.debug=true")); // A non-C locale is necessary for some tests. // This locale must have the following properties: // - some character other than dot as decimal separator // If it cannot be set, locale-dependent tests are skipped. setlocale(LC_ALL, "fr_FR.utf8"); - m_hasNonCLocale = (setlocale(LC_ALL, NULL) == QByteArray("fr_FR.utf8")); + m_hasNonCLocale = (setlocale(LC_ALL, nullptr) == QByteArray("fr_FR.utf8")); if (!m_hasNonCLocale) { qDebug() << "Setting locale to fr_FR.utf8 failed"; } m_hasKde5Konsole = false; eraseProfiles(); if (!KSycoca::isAvailable()) { runKBuildSycoca(); } // Create some fake services for the tests below, and ensure they are in ksycoca. bool mustUpdateKSycoca = false; // fakeservice: deleted and recreated by testDeletingService, don't use in other tests const QString fakeServiceDeleteMe = QStandardPaths::writableLocation(QStandardPaths::GenericDataLocation) + QLatin1String("/kservices5/fakeservice_deleteme.desktop"); if (!QFile::exists(fakeServiceDeleteMe)) { mustUpdateKSycoca = true; createFakeService(QStringLiteral("fakeservice_deleteme.desktop"), QString()); } // fakeservice: a plugin that implements FakePluginType const QString fakeService = QStandardPaths::writableLocation(QStandardPaths::GenericDataLocation) + QLatin1String("/kservices5/fakeservice.desktop"); if (!QFile::exists(fakeService)) { mustUpdateKSycoca = true; createFakeService(QStringLiteral("fakeservice.desktop"), QStringLiteral("FakePluginType")); } // fakepart: a readwrite part, like katepart const QString fakePart = QStandardPaths::writableLocation(QStandardPaths::GenericDataLocation) + QLatin1String("/kservices5/") + "fakepart.desktop"; if (!QFile::exists(fakePart)) { mustUpdateKSycoca = true; KDesktopFile file(fakePart); KConfigGroup group = file.desktopGroup(); group.writeEntry("Name", "FakePart"); group.writeEntry("Type", "Service"); group.writeEntry("X-KDE-Library", "fakepart"); group.writeEntry("X-KDE-Protocols", "http,ftp"); group.writeEntry("X-KDE-ServiceTypes", "FakeBasePart,FakeDerivedPart"); group.writeEntry("MimeType", "text/plain;text/html;"); group.writeEntry("X-KDE-FormFactors", "tablet,handset"); } const QString fakePart2 = QStandardPaths::writableLocation(QStandardPaths::GenericDataLocation) + QLatin1String("/kservices5/") + "fakepart2.desktop"; if (!QFile::exists(fakePart2)) { mustUpdateKSycoca = true; KDesktopFile file(fakePart2); KConfigGroup group = file.desktopGroup(); group.writeEntry("Name", "FakePart2"); group.writeEntry("Type", "Service"); group.writeEntry("X-KDE-Library", "fakepart2"); group.writeEntry("X-KDE-ServiceTypes", "FakeBasePart"); group.writeEntry("MimeType", "text/plain;"); group.writeEntry("X-KDE-TestList", QStringList() << QStringLiteral("item1") << QStringLiteral("item2")); group.writeEntry("X-KDE-FormFactors", "tablet,handset"); } const QString preferredPart = QStandardPaths::writableLocation(QStandardPaths::GenericDataLocation) + QLatin1String("/kservices5/") + "preferredpart.desktop"; if (!QFile::exists(preferredPart)) { mustUpdateKSycoca = true; KDesktopFile file(preferredPart); KConfigGroup group = file.desktopGroup(); group.writeEntry("Name", "PreferredPart"); group.writeEntry("Type", "Service"); group.writeEntry("X-KDE-Library", "preferredpart"); group.writeEntry("X-KDE-ServiceTypes", "FakeBasePart"); group.writeEntry("MimeType", "text/plain;"); } const QString otherPart = QStandardPaths::writableLocation(QStandardPaths::GenericDataLocation) + QLatin1String("/kservices5/") + "otherpart.desktop"; if (!QFile::exists(otherPart)) { mustUpdateKSycoca = true; KDesktopFile file(otherPart); KConfigGroup group = file.desktopGroup(); group.writeEntry("Name", "OtherPart"); group.writeEntry("Type", "Service"); group.writeEntry("X-KDE-Library", "otherpart"); group.writeEntry("X-KDE-ServiceTypes", "FakeBasePart"); group.writeEntry("MimeType", "text/plain;"); } // faketextplugin: a ktexteditor plugin const QString fakeTextplugin = QStandardPaths::writableLocation(QStandardPaths::GenericDataLocation) + QLatin1String("/kservices5/") + "faketextplugin.desktop"; if (!QFile::exists(fakeTextplugin)) { mustUpdateKSycoca = true; KDesktopFile file(fakeTextplugin); KConfigGroup group = file.desktopGroup(); group.writeEntry("Name", "FakeTextPlugin"); group.writeEntry("Type", "Service"); group.writeEntry("X-KDE-Library", "faketextplugin"); group.writeEntry("X-KDE-ServiceTypes", "FakePluginType"); group.writeEntry("MimeType", "text/plain;"); } // fakeplugintype: a servicetype const QString fakePluginType = QStandardPaths::writableLocation(QStandardPaths::GenericDataLocation) + QLatin1String("/kservicetypes5/") + "fakeplugintype.desktop"; if (!QFile::exists(fakePluginType)) { mustUpdateKSycoca = true; KDesktopFile file(fakePluginType); KConfigGroup group = file.desktopGroup(); group.writeEntry("Comment", "Fake Text Plugin"); group.writeEntry("Type", "ServiceType"); group.writeEntry("X-KDE-ServiceType", "FakePluginType"); file.group("PropertyDef::X-KDE-Version").writeEntry("Type", "double"); // like in ktexteditorplugin.desktop group.writeEntry("X-KDE-FormFactors", "tablet,handset"); } // fakebasepart: a servicetype (like ReadOnlyPart) const QString fakeBasePart = QStandardPaths::writableLocation(QStandardPaths::GenericDataLocation) + QLatin1String("/kservicetypes5/") + "fakebasepart.desktop"; if (!QFile::exists(fakeBasePart)) { mustUpdateKSycoca = true; KDesktopFile file(fakeBasePart); KConfigGroup group = file.desktopGroup(); group.writeEntry("Comment", "Fake Base Part"); group.writeEntry("Type", "ServiceType"); group.writeEntry("X-KDE-ServiceType", "FakeBasePart"); KConfigGroup listGroup(&file, "PropertyDef::X-KDE-TestList"); listGroup.writeEntry("Type", "QStringList"); } // fakederivedpart: a servicetype deriving from FakeBasePart (like ReadWritePart derives from ReadOnlyPart) const QString fakeDerivedPart = QStandardPaths::writableLocation(QStandardPaths::GenericDataLocation) + QLatin1String("/kservicetypes5/") + "fakederivedpart.desktop"; if (!QFile::exists(fakeDerivedPart)) { mustUpdateKSycoca = true; KDesktopFile file(fakeDerivedPart); KConfigGroup group = file.desktopGroup(); group.writeEntry("Comment", "Fake Derived Part"); group.writeEntry("Type", "ServiceType"); group.writeEntry("X-KDE-ServiceType", "FakeDerivedPart"); group.writeEntry("X-KDE-Derived", "FakeBasePart"); } // fakekdedmodule const QString fakeKdedModule = QStandardPaths::writableLocation(QStandardPaths::GenericDataLocation) + QLatin1String("/kservicetypes5/") + "fakekdedmodule.desktop"; if (!QFile::exists(fakeKdedModule)) { const QString src = QFINDTESTDATA("fakekdedmodule.desktop"); QVERIFY(QFile::copy(src, fakeKdedModule)); mustUpdateKSycoca = true; } if (mustUpdateKSycoca) { // Update ksycoca in ~/.qttest after creating the above runKBuildSycoca(true); } QVERIFY(KServiceType::serviceType(QStringLiteral("FakePluginType"))); QVERIFY(KServiceType::serviceType(QStringLiteral("FakeBasePart"))); QVERIFY(KServiceType::serviceType(QStringLiteral("FakeDerivedPart"))); } void KServiceTest::runKBuildSycoca(bool noincremental) { QSignalSpy spy(KSycoca::self(), SIGNAL(databaseChanged(QStringList))); KBuildSycoca builder; QVERIFY(builder.recreate(!noincremental)); if (spy.isEmpty()) { qDebug() << "waiting for signal"; QVERIFY(spy.wait(10000)); qDebug() << "got signal"; } } void KServiceTest::cleanupTestCase() { // If I want the konqueror unit tests to work, then I better not have a non-working part // as the preferred part for text/plain... QStringList services; services << QStringLiteral("fakeservice.desktop") << QStringLiteral("fakepart.desktop") << QStringLiteral("faketextplugin.desktop") << QStringLiteral("fakeservice_querymustrebuild.desktop"); Q_FOREACH (const QString &service, services) { const QString fakeService = QStandardPaths::writableLocation(QStandardPaths::GenericDataLocation) + QLatin1String("/kservices5/") + service; QFile::remove(fakeService); } QStringList serviceTypes; serviceTypes << QStringLiteral("fakeplugintype.desktop"); Q_FOREACH (const QString &serviceType, serviceTypes) { const QString fakeServiceType = QStandardPaths::writableLocation(QStandardPaths::GenericDataLocation) + QLatin1String("/kservicetypes5/") + serviceType; //QFile::remove(fakeServiceType); } KBuildSycoca builder; builder.recreate(); } void KServiceTest::testByName() { if (!KSycoca::isAvailable()) { QSKIP("ksycoca not available"); } KServiceType::Ptr s0 = KServiceType::serviceType(QStringLiteral("FakeBasePart")); QVERIFY2(s0, "KServiceType::serviceType(\"FakeBasePart\") failed!"); QCOMPARE(s0->name(), QStringLiteral("FakeBasePart")); KService::Ptr myService = KService::serviceByDesktopPath(QStringLiteral("fakepart.desktop")); QVERIFY(myService); QCOMPARE(myService->name(), QStringLiteral("FakePart")); } void KServiceTest::testProperty() { ksycoca_ms_between_checks = 0; // Let's try creating a desktop file and ensuring it's noticed by the timestamp check QTest::qWait(1000); const QString fakeCookie = QStandardPaths::writableLocation(QStandardPaths::GenericDataLocation) + QLatin1String("/kservices5/kded/") + "fakekcookiejar.desktop"; if (!QFile::exists(fakeCookie)) { KDesktopFile file(fakeCookie); KConfigGroup group = file.desktopGroup(); group.writeEntry("Name", "OtherPart"); group.writeEntry("Type", "Service"); group.writeEntry("X-KDE-ServiceTypes", "FakeKDEDModule"); group.writeEntry("X-KDE-Library", "kcookiejar"); group.writeEntry("X-KDE-DBus-ModuleName", "kcookiejar"); group.writeEntry("X-KDE-Kded-autoload", "false"); group.writeEntry("X-KDE-Kded-load-on-demand", "true"); qDebug() << "created" << fakeCookie; } KService::Ptr kdedkcookiejar = KService::serviceByDesktopPath(QStringLiteral("kded/fakekcookiejar.desktop")); QVERIFY(kdedkcookiejar); QCOMPARE(kdedkcookiejar->entryPath(), QStringLiteral("kded/fakekcookiejar.desktop")); QCOMPARE(kdedkcookiejar->property(QStringLiteral("ServiceTypes")).toStringList().join(QLatin1Char(',')), QString("FakeKDEDModule")); QCOMPARE(kdedkcookiejar->property(QStringLiteral("X-KDE-Kded-autoload")).toBool(), false); QCOMPARE(kdedkcookiejar->property(QStringLiteral("X-KDE-Kded-load-on-demand")).toBool(), true); QVERIFY(!kdedkcookiejar->property(QStringLiteral("Name")).toString().isEmpty()); QVERIFY(!kdedkcookiejar->property(QStringLiteral("Name[fr]"), QVariant::String).isValid()); // TODO: for this we must install a servicetype desktop file... //KService::Ptr kjavaappletviewer = KService::serviceByDesktopPath("kjavaappletviewer.desktop"); //QVERIFY(kjavaappletviewer); //QCOMPARE(kjavaappletviewer->property("X-KDE-BrowserView-PluginsInfo").toString(), QString("kjava/pluginsinfo")); // Test property("X-KDE-Protocols"), which triggers the KServiceReadProperty code. KService::Ptr fakePart = KService::serviceByDesktopPath(QStringLiteral("fakepart.desktop")); QVERIFY(fakePart); // see initTestCase; it should be found. QVERIFY(fakePart->propertyNames().contains(QStringLiteral("X-KDE-Protocols"))); QCOMPARE(fakePart->mimeTypes(), QStringList() << QStringLiteral("text/plain") << QStringLiteral("text/html")); // okular relies on subclasses being kept here const QStringList protocols = fakePart->property(QStringLiteral("X-KDE-Protocols")).toStringList(); QCOMPARE(protocols, QStringList() << QStringLiteral("http") << QStringLiteral("ftp")); // Restore value ksycoca_ms_between_checks = 1500; } void KServiceTest::testAllServiceTypes() { if (!KSycoca::isAvailable()) { QSKIP("ksycoca not available"); } const KServiceType::List allServiceTypes = KServiceType::allServiceTypes(); // A bit of checking on the allServiceTypes list itself KServiceType::List::ConstIterator stit = allServiceTypes.begin(); const KServiceType::List::ConstIterator stend = allServiceTypes.end(); for (; stit != stend; ++stit) { const KServiceType::Ptr servtype = (*stit); const QString name = servtype->name(); QVERIFY(!name.isEmpty()); QVERIFY(servtype->sycocaType() == KST_KServiceType); } } void KServiceTest::testAllServices() { if (!KSycoca::isAvailable()) { QSKIP("ksycoca not available"); } const KService::List lst = KService::allServices(); QVERIFY(!lst.isEmpty()); for (KService::List::ConstIterator it = lst.begin(); it != lst.end(); ++it) { const KService::Ptr service = (*it); QVERIFY(service->isType(KST_KService)); const QString name = service->name(); const QString entryPath = service->entryPath(); //qDebug() << name << "entryPath=" << entryPath << "menuId=" << service->menuId(); QVERIFY(!name.isEmpty()); QVERIFY(!entryPath.isEmpty()); KService::Ptr lookedupService = KService::serviceByDesktopPath(entryPath); QVERIFY(lookedupService); // not null QCOMPARE(lookedupService->entryPath(), entryPath); if (service->isApplication()) { const QString menuId = service->menuId(); if (menuId.isEmpty()) { qWarning("%s has an empty menuId!", qPrintable(entryPath)); } else if (menuId == "org.kde.konsole.desktop") { m_hasKde5Konsole = true; } QVERIFY(!menuId.isEmpty()); lookedupService = KService::serviceByMenuId(menuId); QVERIFY(lookedupService); // not null QCOMPARE(lookedupService->menuId(), menuId); } } } // Helper method for all the trader tests static bool offerListHasService(const KService::List &offers, const QString &entryPath) { bool found = false; KService::List::const_iterator it = offers.begin(); for (; it != offers.end(); ++it) { if ((*it)->entryPath() == entryPath) { if (found) { // should be there only once qWarning("ERROR: %s was found twice in the list", qPrintable(entryPath)); return false; // make test fail } found = true; } } return found; } void KServiceTest::testDBUSStartupType() { if (!KSycoca::isAvailable()) { QSKIP("ksycoca not available"); } if (!m_hasKde5Konsole) { QSKIP("org.kde.konsole.desktop not available"); } //KService::Ptr konsole = KService::serviceByMenuId( "org.kde.konsole.desktop" ); KService::Ptr konsole = KService::serviceByDesktopName(QStringLiteral("org.kde.konsole")); QVERIFY(konsole); QCOMPARE(konsole->menuId(), QStringLiteral("org.kde.konsole.desktop")); //qDebug() << konsole->entryPath(); QCOMPARE(int(konsole->dbusStartupType()), int(KService::DBusUnique)); } void KServiceTest::testByStorageId() { if (!KSycoca::isAvailable()) { QSKIP("ksycoca not available"); } if (QStandardPaths::locate(QStandardPaths::ApplicationsLocation, QStringLiteral("org.kde.konsole.desktop")).isEmpty()) { QSKIP("org.kde.konsole.desktop not available"); } QVERIFY(KService::serviceByMenuId(QStringLiteral("org.kde.konsole.desktop"))); QVERIFY(!KService::serviceByMenuId(QStringLiteral("org.kde.konsole"))); // doesn't work, extension mandatory QVERIFY(!KService::serviceByMenuId(QStringLiteral("konsole.desktop"))); // doesn't work, full filename mandatory QVERIFY(KService::serviceByStorageId(QStringLiteral("org.kde.konsole.desktop"))); QVERIFY(KService::serviceByStorageId("org.kde.konsole")); // This one fails here; probably because there are two such files, so this would be too // ambiguous... According to the testAllServices output, the entryPaths are // entryPath="/d/kde/inst/kde5/share/applications/org.kde.konsole.desktop" // entryPath= "/usr/share/applications/org.kde.konsole.desktop" // //QVERIFY(KService::serviceByDesktopPath("org.kde.konsole.desktop")); QVERIFY(KService::serviceByDesktopName(QStringLiteral("org.kde.konsole"))); QCOMPARE(KService::serviceByDesktopName(QStringLiteral("org.kde.konsole"))->menuId(), QString("org.kde.konsole.desktop")); } void KServiceTest::testServiceTypeTraderForReadOnlyPart() { if (!KSycoca::isAvailable()) { QSKIP("ksycoca not available"); } // Querying trader for services associated with FakeBasePart KService::List offers = KServiceTypeTrader::self()->query(QStringLiteral("FakeBasePart")); QVERIFY(offers.count() > 0); if (!offerListHasService(offers, QStringLiteral("fakepart.desktop")) || !offerListHasService(offers, QStringLiteral("fakepart2.desktop")) || !offerListHasService(offers, QStringLiteral("otherpart.desktop")) || !offerListHasService(offers, QStringLiteral("preferredpart.desktop"))) { foreach (KService::Ptr service, offers) { qDebug("%s %s", qPrintable(service->name()), qPrintable(service->entryPath())); } } m_firstOffer = offers[0]->entryPath(); QVERIFY(offerListHasService(offers, QStringLiteral("fakepart.desktop"))); QVERIFY(offerListHasService(offers, QStringLiteral("fakepart2.desktop"))); QVERIFY(offerListHasService(offers, QStringLiteral("otherpart.desktop"))); QVERIFY(offerListHasService(offers, QStringLiteral("preferredpart.desktop"))); // Check ordering according to InitialPreference int lastPreference = -1; bool lastAllowedAsDefault = true; Q_FOREACH (KService::Ptr service, offers) { const QString path = service->entryPath(); const int preference = service->initialPreference(); // ## might be wrong if we use per-servicetype preferences... //qDebug( "%s has preference %d, allowAsDefault=%d", qPrintable( path ), preference, service->allowAsDefault() ); if (lastAllowedAsDefault && !service->allowAsDefault()) { // first "not allowed as default" offer lastAllowedAsDefault = false; lastPreference = -1; // restart } if (lastPreference != -1) { QVERIFY(preference <= lastPreference); } lastPreference = preference; } // Now look for any FakePluginType offers = KServiceTypeTrader::self()->query(QStringLiteral("FakePluginType")); QVERIFY(offerListHasService(offers, QStringLiteral("fakeservice.desktop"))); QVERIFY(offerListHasService(offers, QStringLiteral("faketextplugin.desktop"))); } void KServiceTest::testTraderConstraints() { if (!KSycoca::isAvailable()) { QSKIP("ksycoca not available"); } KService::List offers; // Baseline: no constraints offers = KServiceTypeTrader::self()->query(QStringLiteral("FakePluginType")); QCOMPARE(offers.count(), 2); QVERIFY(offerListHasService(offers, QStringLiteral("faketextplugin.desktop"))); QVERIFY(offerListHasService(offers, QStringLiteral("fakeservice.desktop"))); // String-based constraint offers = KServiceTypeTrader::self()->query(QStringLiteral("FakePluginType"), QStringLiteral("Library == 'faketextplugin'")); QCOMPARE(offers.count(), 1); QVERIFY(offerListHasService(offers, QStringLiteral("faketextplugin.desktop"))); // Match case insensitive offers = KServiceTypeTrader::self()->query(QStringLiteral("FakePluginType"), QStringLiteral("Library =~ 'fAkEteXtpLuGin'")); QCOMPARE(offers.count(), 1); QVERIFY(offerListHasService(offers, QStringLiteral("faketextplugin.desktop"))); // "contains" offers = KServiceTypeTrader::self()->query(QStringLiteral("FakePluginType"), QStringLiteral("'textplugin' ~ Library")); // note: "is contained in", not "contains"... QCOMPARE(offers.count(), 1); QVERIFY(offerListHasService(offers, QStringLiteral("faketextplugin.desktop"))); // "contains" case insensitive offers = KServiceTypeTrader::self()->query(QStringLiteral("FakePluginType"), QStringLiteral("'teXtPluGin' ~~ Library")); // note: "is contained in", not "contains"... QCOMPARE(offers.count(), 1); QVERIFY(offerListHasService(offers, QStringLiteral("faketextplugin.desktop"))); if (m_hasNonCLocale) { // Test float parsing, must use dot as decimal separator independent of locale. offers = KServiceTypeTrader::self()->query(QStringLiteral("FakePluginType"), QStringLiteral("([X-KDE-Version] > 4.559) and ([X-KDE-Version] < 4.561)")); QCOMPARE(offers.count(), 1); QVERIFY(offerListHasService(offers, QStringLiteral("fakeservice.desktop"))); } // A test with an invalid query, to test for memleaks offers = KServiceTypeTrader::self()->query(QStringLiteral("FakePluginType"), QStringLiteral("A == B OR C == D AND OR Foo == 'Parse Error'")); QVERIFY(offers.isEmpty()); } void KServiceTest::testHasServiceType1() // with services constructed with a full path (rare) { QString fakepartPath = QStandardPaths::locate(QStandardPaths::GenericDataLocation, QLatin1String("kservices5/") + "fakepart.desktop"); QVERIFY(!fakepartPath.isEmpty()); KService fakepart(fakepartPath); QVERIFY(fakepart.hasServiceType(QStringLiteral("FakeBasePart"))); QVERIFY(fakepart.hasServiceType(QStringLiteral("FakeDerivedPart"))); QCOMPARE(fakepart.mimeTypes(), QStringList() << QStringLiteral("text/plain") << QStringLiteral("text/html")); QString faketextPluginPath = QStandardPaths::locate(QStandardPaths::GenericDataLocation, QLatin1String("kservices5/") + "faketextplugin.desktop"); QVERIFY(!faketextPluginPath.isEmpty()); KService faketextPlugin(faketextPluginPath); QVERIFY(faketextPlugin.hasServiceType(QStringLiteral("FakePluginType"))); QVERIFY(!faketextPlugin.hasServiceType(QStringLiteral("FakeBasePart"))); } void KServiceTest::testHasServiceType2() // with services coming from ksycoca { KService::Ptr fakepart = KService::serviceByDesktopPath(QStringLiteral("fakepart.desktop")); QVERIFY(fakepart); QVERIFY(fakepart->hasServiceType(QStringLiteral("FakeBasePart"))); QVERIFY(fakepart->hasServiceType(QStringLiteral("FakeDerivedPart"))); QCOMPARE(fakepart->mimeTypes(), QStringList() << QStringLiteral("text/plain") << QStringLiteral("text/html")); KService::Ptr faketextPlugin = KService::serviceByDesktopPath(QStringLiteral("faketextplugin.desktop")); QVERIFY(faketextPlugin); QVERIFY(faketextPlugin->hasServiceType(QStringLiteral("FakePluginType"))); QVERIFY(!faketextPlugin->hasServiceType(QStringLiteral("FakeBasePart"))); } void KServiceTest::testWriteServiceTypeProfile() { const QString serviceType = QStringLiteral("FakeBasePart"); KService::List services, disabledServices; services.append(KService::serviceByDesktopPath(QStringLiteral("preferredpart.desktop"))); services.append(KService::serviceByDesktopPath(QStringLiteral("fakepart.desktop"))); disabledServices.append(KService::serviceByDesktopPath(QStringLiteral("fakepart2.desktop"))); Q_FOREACH (const KService::Ptr &serv, services) { QVERIFY(serv); } Q_FOREACH (const KService::Ptr &serv, disabledServices) { QVERIFY(serv); } KServiceTypeProfile::writeServiceTypeProfile(serviceType, services, disabledServices); // Check that the file got written QString profilerc = QStandardPaths::writableLocation(QStandardPaths::GenericConfigLocation) + "/servicetype_profilerc"; QVERIFY(!profilerc.isEmpty()); QVERIFY(QFile::exists(profilerc)); KService::List offers = KServiceTypeTrader::self()->query(serviceType); QVERIFY(offers.count() > 0); // not empty //foreach( KService::Ptr service, offers ) // qDebug( "%s %s", qPrintable( service->name() ), qPrintable( service->entryPath() ) ); QVERIFY(offers.count() >= 2); QCOMPARE(offers[0]->entryPath(), QStringLiteral("preferredpart.desktop")); QCOMPARE(offers[1]->entryPath(), QStringLiteral("fakepart.desktop")); QVERIFY(offerListHasService(offers, QStringLiteral("otherpart.desktop"))); // should still be somewhere in there QVERIFY(!offerListHasService(offers, QStringLiteral("fakepart2.desktop"))); // it got disabled above } void KServiceTest::testDefaultOffers() { // Now that we have a user-profile, let's see if defaultOffers indeed gives us the default ordering. const QString serviceType = QStringLiteral("FakeBasePart"); KService::List offers = KServiceTypeTrader::self()->defaultOffers(serviceType); QVERIFY(offers.count() > 0); // not empty QVERIFY(offerListHasService(offers, QStringLiteral("fakepart2.desktop"))); // it's here even though it's disabled in the profile QVERIFY(offerListHasService(offers, QStringLiteral("otherpart.desktop"))); if (m_firstOffer.isEmpty()) { QSKIP("testServiceTypeTraderForReadOnlyPart not run"); } QCOMPARE(offers[0]->entryPath(), m_firstOffer); } void KServiceTest::testDeleteServiceTypeProfile() { const QString serviceType = QStringLiteral("FakeBasePart"); KServiceTypeProfile::deleteServiceTypeProfile(serviceType); KService::List offers = KServiceTypeTrader::self()->query(serviceType); QVERIFY(offers.count() > 0); // not empty QVERIFY(offerListHasService(offers, QStringLiteral("fakepart2.desktop"))); // it's back if (m_firstOffer.isEmpty()) { QSKIP("testServiceTypeTraderForReadOnlyPart not run"); } QCOMPARE(offers[0]->entryPath(), m_firstOffer); } void KServiceTest::testActionsAndDataStream() { if (QStandardPaths::locate(QStandardPaths::ApplicationsLocation, QStringLiteral("org.kde.konsole.desktop")).isEmpty()) { QSKIP("org.kde.konsole.desktop not available"); } KService::Ptr service = KService::serviceByStorageId(QStringLiteral("org.kde.konsole.desktop")); QVERIFY(service); QVERIFY(!service->property(QStringLiteral("Name[fr]"), QVariant::String).isValid()); const QList actions = service->actions(); QCOMPARE(actions.count(), 2); // NewWindow, NewTab const KServiceAction newTabAction = actions.at(1); QCOMPARE(newTabAction.name(), QStringLiteral("NewTab")); QCOMPARE(newTabAction.exec(), QStringLiteral("konsole --new-tab")); QVERIFY(newTabAction.icon().isEmpty()); QCOMPARE(newTabAction.noDisplay(), false); QVERIFY(!newTabAction.isSeparator()); } void KServiceTest::testServiceGroups() { KServiceGroup::Ptr root = KServiceGroup::root(); QVERIFY(root); qDebug() << root->groupEntries().count(); KServiceGroup::Ptr group = root; QVERIFY(group); const KServiceGroup::List list = group->entries(true /* sorted */, true /* exclude no display entries */, false /* allow separators */, true /* sort by generic name */); qDebug() << list.count(); Q_FOREACH (KServiceGroup::SPtr s, list) { qDebug() << s->name() << s->entryPath(); } // No unit test here yet, but at least this can be valgrinded for errors. } void KServiceTest::testDeletingService() { const QString serviceName = QStringLiteral("fakeservice_deleteme.desktop"); KService::Ptr fakeService = KService::serviceByDesktopPath(serviceName); QVERIFY(fakeService); // see initTestCase; it should be found. // Test deleting a service const QString servPath = QStandardPaths::writableLocation(QStandardPaths::GenericDataLocation) + QLatin1String("/kservices5/") + serviceName; QVERIFY(QFile::exists(servPath)); QFile::remove(servPath); runKBuildSycoca(); ksycoca_ms_between_checks = 0; // need it to check the ksycoca mtime QVERIFY(!KService::serviceByDesktopPath(serviceName)); // not in ksycoca anymore // Restore value ksycoca_ms_between_checks = 1500; QVERIFY(fakeService); // the whole point of refcounting is that this KService instance is still valid. QVERIFY(!QFile::exists(servPath)); // workaround inotify issue (in CI only...) QTest::qWait(100); // Recreate it, for future tests createFakeService(serviceName, QString()); QVERIFY(QFile::exists(servPath)); qDebug() << "executing kbuildsycoca (2)"; runKBuildSycoca(); if (QThread::currentThread() != QCoreApplication::instance()->thread()) { m_sycocaUpdateDone.ref(); } } void KServiceTest::createFakeService(const QString &filename, const QString& serviceType) { const QString fakeService = QStandardPaths::writableLocation(QStandardPaths::GenericDataLocation) + QLatin1String("/kservices5/") + filename; KDesktopFile file(fakeService); KConfigGroup group = file.desktopGroup(); group.writeEntry("Name", "FakePlugin"); group.writeEntry("Type", "Service"); group.writeEntry("X-KDE-Library", "fakeservice"); group.writeEntry("X-KDE-Version", "4.56"); group.writeEntry("ServiceTypes", serviceType); group.writeEntry("MimeType", "text/plain;"); } #include #include #include // Testing for concurrent access to ksycoca from multiple threads // It's especially interesting to run this test as ./kservicetest testThreads // so that even the ksycoca initialization is happening from N threads at the same time. // Use valgrind --tool=helgrind to see the race conditions. void KServiceTest::testReaderThreads() { QThreadPool::globalInstance()->setMaxThreadCount(10); QFutureSynchronizer sync; sync.addFuture(QtConcurrent::run(this, &KServiceTest::testAllServices)); sync.addFuture(QtConcurrent::run(this, &KServiceTest::testAllServices)); sync.addFuture(QtConcurrent::run(this, &KServiceTest::testAllServices)); sync.addFuture(QtConcurrent::run(this, &KServiceTest::testHasServiceType1)); sync.addFuture(QtConcurrent::run(this, &KServiceTest::testAllServices)); sync.addFuture(QtConcurrent::run(this, &KServiceTest::testAllServices)); sync.waitForFinished(); QThreadPool::globalInstance()->setMaxThreadCount(1); // delete those threads } void KServiceTest::testThreads() { QThreadPool::globalInstance()->setMaxThreadCount(10); QFutureSynchronizer sync; sync.addFuture(QtConcurrent::run(this, &KServiceTest::testAllServices)); sync.addFuture(QtConcurrent::run(this, &KServiceTest::testHasServiceType1)); sync.addFuture(QtConcurrent::run(this, &KServiceTest::testDeletingService)); sync.addFuture(QtConcurrent::run(this, &KServiceTest::testTraderConstraints)); // process events (DBus, inotify...), until we get all expected signals QTRY_COMPARE_WITH_TIMEOUT(m_sycocaUpdateDone.load(), 1, 15000); // not using a bool, just to silence helgrind qDebug() << "Joining all threads"; sync.waitForFinished(); } void KServiceTest::testOperatorKPluginName() { KService fservice(QFINDTESTDATA("fakeplugin.desktop")); KPluginName fname(fservice); QVERIFY(fname.isValid()); QCOMPARE(fname.name(), QStringLiteral("fakeplugin")); KPluginLoader fplugin(fservice); QVERIFY(fplugin.factory()); // make sure constness doesn't break anything const KService const_fservice(QFINDTESTDATA("fakeplugin.desktop")); KPluginName const_fname(const_fservice); QVERIFY(const_fname.isValid()); QCOMPARE(const_fname.name(), QStringLiteral("fakeplugin")); KPluginLoader const_fplugin(const_fservice); QVERIFY(const_fplugin.factory()); KService nservice(QFINDTESTDATA("noplugin.desktop")); KPluginName nname(nservice); QVERIFY(!nname.isValid()); QVERIFY2(nname.name().isEmpty(), qPrintable(nname.name())); QVERIFY(!nname.errorString().isEmpty()); KPluginLoader nplugin(nservice); QVERIFY(!nplugin.factory()); KService iservice(QStringLiteral("idonotexist.desktop")); KPluginName iname(iservice); QVERIFY(!iname.isValid()); QVERIFY2(iname.name().isEmpty(), qPrintable(iname.name())); QVERIFY(!iname.errorString().isEmpty()); KPluginLoader iplugin(iservice); QVERIFY(!iplugin.factory()); } void KServiceTest::testKPluginInfoQuery() { KPluginInfo info(KPluginMetaData(QStandardPaths::writableLocation(QStandardPaths::GenericDataLocation) + QLatin1String("/kservices5/") + "fakepart2.desktop")); QCOMPARE(info.property(QStringLiteral("X-KDE-TestList")).toStringList().size(), 2); } void KServiceTest::testCompleteBaseName() { QCOMPARE(KServiceUtilPrivate::completeBaseName(QStringLiteral("/home/x/.qttest/share/kservices5/fakepart2.desktop")), QStringLiteral("fakepart2")); // dots in filename before .desktop extension: QCOMPARE(KServiceUtilPrivate::completeBaseName(QStringLiteral("/home/x/.qttest/share/kservices5/org.kde.fakeapp.desktop")), QStringLiteral("org.kde.fakeapp")); } void KServiceTest::testEntryPathToName() { QCOMPARE(KService(QStringLiteral("c.desktop")).name(), QStringLiteral("c")); QCOMPARE(KService(QStringLiteral("a.b.c.desktop")).name(), QStringLiteral("a.b.c")); // dots in filename before .desktop extension QCOMPARE(KService(QStringLiteral("/hallo/a.b.c.desktop")).name(), QStringLiteral("a.b.c")); } void KServiceTest::testKPluginMetaData() { const QString fakePart = QStandardPaths::writableLocation(QStandardPaths::GenericDataLocation) + QLatin1String("/kservices5/") + "fakepart.desktop"; KPluginMetaData md(fakePart); KService::Ptr service(new KService(fakePart)); KPluginInfo info(service); auto info_md = info.toMetaData(); QCOMPARE(info_md.formFactors(), md.formFactors()); } void KServiceTest::testTraderQueryMustRebuildSycoca() { QVERIFY(!KServiceTypeProfile::hasProfile(QStringLiteral("FakeBasePart"))); QTest::qWait(1000); createFakeService(QStringLiteral("fakeservice_querymustrebuild.desktop"), QString()); // just to touch the dir KService::List offers = KServiceTypeTrader::self()->query(QStringLiteral("FakeBasePart")); QVERIFY(offers.count() > 0); } diff --git a/autotests/ksycocatest.cpp b/autotests/ksycocatest.cpp index 9ad4aa8..73aafc8 100644 --- a/autotests/ksycocatest.cpp +++ b/autotests/ksycocatest.cpp @@ -1,353 +1,353 @@ /* This file is part of the KDE project Copyright (C) 2015 David Faure This library is free software; you can redistribute it and/or modify it under the terms of the GNU Library General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Library General Public License for more details. You should have received a copy of the GNU Library General Public License along with this library; see the file COPYING.LIB. If not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include // ## use QFile::setFileTime when it lands in Qt #include #ifdef Q_OS_UNIX #include #include #endif // taken from tst_qstandardpaths #if defined(Q_OS_UNIX) && !defined(Q_OS_MAC) && !defined(Q_OS_BLACKBERRY) && !defined(Q_OS_ANDROID) #define Q_XDG_PLATFORM #endif #if QT_VERSION >= QT_VERSION_CHECK(5, 8, 0) // On Unix, lastModified() finally returns milliseconds as well, since Qt 5.8.0 // Not sure about the situation on Windows though. static const int s_waitDelay = 10; #else static const int s_waitDelay = 1000; #endif extern KSERVICE_EXPORT int ksycoca_ms_between_checks; class KSycocaTest : public QObject { Q_OBJECT private Q_SLOTS: void initTestCase() { QStandardPaths::enableTestMode(true); QVERIFY(m_tempDir.isValid()); // we don't need the services dir -> ensure there isn't one, so we can check allResourceDirs below. QDir(servicesDir()).removeRecursively(); QDir(menusDir()).removeRecursively(); QDir().mkpath(menusDir() + "/fakeSubserviceDirectory"); #ifdef Q_XDG_PLATFORM qputenv("XDG_DATA_DIRS", QFile::encodeName(m_tempDir.path())); // so that vfolder_menu doesn't go look into /etc and /usr qputenv("XDG_CONFIG_DIRS", QFile::encodeName(m_tempDir.path())); #else // We need to make changes to a global dir without messing up the system QSKIP("This test requires XDG_DATA_DIRS"); #endif createGlobalServiceType(); } void cleanupTestCase() { QFile::remove(serviceTypesDir() + "/fakeLocalServiceType.desktop"); QFile::remove(KSycoca::absoluteFilePath()); } void ensureCacheValidShouldCreateDB(); void kBuildSycocaShouldEmitDatabaseChanged(); void dirInFutureShouldRebuildSycocaOnce(); void dirTimestampShouldBeCheckedRecursively(); void recursiveCheckShouldIgnoreLinksGoingUp(); void testAllResourceDirs(); void testDeletingSycoca(); void testGlobalSycoca(); void testNonReadableSycoca(); private: void createGlobalServiceType() { KDesktopFile file(serviceTypesDir() + "/fakeGlobalServiceType.desktop"); KConfigGroup group = file.desktopGroup(); group.writeEntry("Comment", "Fake Global ServiceType"); group.writeEntry("Type", "ServiceType"); group.writeEntry("X-KDE-ServiceType", "FakeGlobalServiceType"); file.sync(); qDebug() << "created" << serviceTypesDir() + "/fakeGlobalServiceType.desktop"; } QString servicesDir() const { return QStandardPaths::writableLocation(QStandardPaths::GenericDataLocation) + "/kservices5"; } QString serviceTypesDir() const { return QStandardPaths::writableLocation(QStandardPaths::GenericDataLocation) + "/kservicetypes5"; } QString menusDir() const { return QStandardPaths::writableLocation(QStandardPaths::GenericConfigLocation) + "/menus"; } QString appsDir() const { return QStandardPaths::writableLocation(QStandardPaths::ApplicationsLocation) + QLatin1Char('/'); } static void runKBuildSycoca(const QProcessEnvironment &environment, bool global = false); QTemporaryDir m_tempDir; }; QTEST_MAIN(KSycocaTest) void KSycocaTest::ensureCacheValidShouldCreateDB() // this is what kded does on startup { QFile::remove(KSycoca::absoluteFilePath()); KSycoca::self()->ensureCacheValid(); QVERIFY(QFile::exists(KSycoca::absoluteFilePath())); QVERIFY(KServiceType::serviceType(QStringLiteral("FakeGlobalServiceType"))); } void KSycocaTest::kBuildSycocaShouldEmitDatabaseChanged() { // It used to be a DBus signal, now it's file watching QTest::qWait(s_waitDelay); // Ensure kbuildsycoca has something to do QVERIFY(QFile::remove(serviceTypesDir() + "/fakeGlobalServiceType.desktop")); // Run kbuildsycoca QSignalSpy spy(KSycoca::self(), SIGNAL(databaseChanged(QStringList))); runKBuildSycoca(QProcessEnvironment::systemEnvironment()); qDebug() << "waiting for signal"; QVERIFY(spy.wait(20000)); qDebug() << "got signal"; // Put it back for other tests createGlobalServiceType(); } void KSycocaTest::dirInFutureShouldRebuildSycocaOnce() { const QDateTime oldTimestamp = QFileInfo(KSycoca::absoluteFilePath()).lastModified(); // ### use QFile::setFileTime when it lands in Qt... #ifdef Q_OS_UNIX const QString path = serviceTypesDir(); struct timeval tp; - gettimeofday(&tp, 0); + gettimeofday(&tp, nullptr); struct utimbuf utbuf; utbuf.actime = tp.tv_sec; utbuf.modtime = tp.tv_sec + 60; // 60 second in the future QCOMPARE(utime(QFile::encodeName(path).constData(), &utbuf), 0); qDebug("Time changed for %s", qPrintable(path)); qDebug() << QDateTime::currentDateTime() << QFileInfo(path).lastModified(); #else QSKIP("This test requires utime"); #endif ksycoca_ms_between_checks = 0; QTest::qWait(s_waitDelay); KSycoca::self()->ensureCacheValid(); const QDateTime newTimestamp = QFileInfo(KSycoca::absoluteFilePath()).lastModified(); QVERIFY(newTimestamp > oldTimestamp); QTest::qWait(s_waitDelay); KSycoca::self()->ensureCacheValid(); const QDateTime againTimestamp = QFileInfo(KSycoca::absoluteFilePath()).lastModified(); QCOMPARE(againTimestamp, newTimestamp); // same mtime, it didn't get rebuilt // Ensure we don't pollute the other tests, with our dir in the future. #ifdef Q_OS_UNIX utbuf.modtime = tp.tv_sec; QCOMPARE(utime(QFile::encodeName(path).constData(), &utbuf), 0); qDebug("Time changed back for %s", qPrintable(path)); qDebug() << QDateTime::currentDateTime() << QFileInfo(path).lastModified(); #endif } void KSycocaTest::dirTimestampShouldBeCheckedRecursively() { #ifndef Q_OS_UNIX QSKIP("This test requires utime"); #endif const QDateTime oldTimestamp = QFileInfo(KSycoca::absoluteFilePath()).lastModified(); const QString path = menusDir() + QLatin1String("/fakeSubserviceDirectory"); // ### use QFile::setFileTime when it lands in Qt... #ifdef Q_OS_UNIX struct timeval tp; - gettimeofday(&tp, 0); + gettimeofday(&tp, nullptr); struct utimbuf utbuf; utbuf.actime = tp.tv_sec; utbuf.modtime = tp.tv_sec + 60; // 60 second in the future QCOMPARE(utime(QFile::encodeName(path).constData(), &utbuf), 0); qDebug("Time changed for %s", qPrintable(path)); qDebug() << QDateTime::currentDateTime() << QFileInfo(path).lastModified(); #endif ksycoca_ms_between_checks = 0; QTest::qWait(s_waitDelay); qDebug() << "Waited 1s, calling ensureCacheValid (should rebuild)"; KSycoca::self()->ensureCacheValid(); const QDateTime newTimestamp = QFileInfo(KSycoca::absoluteFilePath()).lastModified(); if (newTimestamp <= oldTimestamp) { qWarning() << "oldTimestamp=" << oldTimestamp << "newTimestamp=" << newTimestamp; } QVERIFY(newTimestamp > oldTimestamp); QTest::qWait(s_waitDelay); qDebug() << "Waited 1s, calling ensureCacheValid (should not rebuild)"; KSycoca::self()->ensureCacheValid(); const QDateTime againTimestamp = QFileInfo(KSycoca::absoluteFilePath()).lastModified(); QCOMPARE(againTimestamp, newTimestamp); // same mtime, it didn't get rebuilt // Ensure we don't pollute the other tests QDir(path).removeRecursively(); } void KSycocaTest::recursiveCheckShouldIgnoreLinksGoingUp() { #ifndef Q_OS_UNIX QSKIP("This test requires symlinks and utime"); #endif ksycoca_ms_between_checks = 0; const QString link = menusDir() + QLatin1String("/linkGoingUp"); QVERIFY(QFile::link("..", link)); QTest::qWait(s_waitDelay); KSycoca::self()->ensureCacheValid(); QVERIFY2(QFile::exists(KSycoca::absoluteFilePath()), qPrintable(KSycoca::absoluteFilePath())); const QDateTime oldTimestamp = QFileInfo(KSycoca::absoluteFilePath()).lastModified(); QVERIFY(oldTimestamp.isValid()); const QString path = QFileInfo(menusDir()).absolutePath(); // the parent of the menus dir // ### use QFile::setFileTime when it lands in Qt... #ifdef Q_OS_UNIX struct timeval tp; - gettimeofday(&tp, 0); + gettimeofday(&tp, nullptr); struct utimbuf utbuf; utbuf.actime = tp.tv_sec; utbuf.modtime = tp.tv_sec + 60; // 60 second in the future QCOMPARE(utime(QFile::encodeName(path).constData(), &utbuf), 0); qDebug("Time changed for %s", qPrintable(path)); qDebug() << QDateTime::currentDateTime() << QFileInfo(path).lastModified(); #endif ksycoca_ms_between_checks = 0; QTest::qWait(s_waitDelay); qDebug() << "Waited 1s, calling ensureCacheValid (should not rebuild)"; KSycoca::self()->ensureCacheValid(); const QDateTime againTimestamp = QFileInfo(KSycoca::absoluteFilePath()).lastModified(); QCOMPARE(againTimestamp, oldTimestamp); // same mtime, it didn't get rebuilt // Ensure we don't pollute the other tests QFile(link).remove(); } void KSycocaTest::runKBuildSycoca(const QProcessEnvironment &environment, bool global) { QProcess proc; const QString kbuildsycoca = QStringLiteral(KBUILDSYCOCAEXE); QVERIFY(!kbuildsycoca.isEmpty()); QStringList args; args << QStringLiteral("--testmode"); if (global) { args << QStringLiteral("--global"); } proc.setProcessChannelMode(QProcess::ForwardedChannels); proc.start(kbuildsycoca, args); proc.setProcessEnvironment(environment); proc.waitForFinished(); QCOMPARE(proc.exitStatus(), QProcess::NormalExit); } void KSycocaTest::testAllResourceDirs() { // Dirs that exist and dirs that don't exist, should both be in allResourceDirs(). const QStringList dirs = KSycoca::self()->allResourceDirs(); QVERIFY2(dirs.contains(servicesDir()), qPrintable(dirs.join(','))); QVERIFY2(dirs.contains(serviceTypesDir()), qPrintable(dirs.join(','))); } void KSycocaTest::testDeletingSycoca() { // Mostly the same as ensureCacheValidShouldCreateDB, but KSycoca::self() already exists // So this is a check that deleting sycoca doesn't make apps crash (bug 343618). QFile::remove(KSycoca::absoluteFilePath()); ksycoca_ms_between_checks = 0; QVERIFY(KServiceType::serviceType(QStringLiteral("FakeGlobalServiceType"))); QVERIFY(QFile::exists(KSycoca::absoluteFilePath())); } void KSycocaTest::testGlobalSycoca() { // No local DB QFile::remove(KSycoca::absoluteFilePath()); // Build global DB // We could do it in-process, but let's check what a sysadmin would do: run kbuildsycoca5 --global QProcessEnvironment env = QProcessEnvironment::systemEnvironment(); env.insert(QStringLiteral("XDG_DATA_DIRS"), m_tempDir.path()); runKBuildSycoca(env, true /*global*/); QVERIFY(QFile::exists(KSycoca::absoluteFilePath(KSycoca::GlobalDatabase))); KSycoca::self()->ensureCacheValid(); QVERIFY(!QFile::exists(KSycoca::absoluteFilePath())); // Now create a local file, after a 1s delay, until QDateTime includes ms... QTest::qWait(s_waitDelay); KDesktopFile file(serviceTypesDir() + "/fakeLocalServiceType.desktop"); KConfigGroup group = file.desktopGroup(); group.writeEntry("Comment", "Fake Local ServiceType"); group.writeEntry("Type", "ServiceType"); group.writeEntry("X-KDE-ServiceType", "FakeLocalServiceType"); file.sync(); //qDebug() << "created" << serviceTypesDir() + "/fakeLocalServiceType.desktop at" << QDateTime::currentMSecsSinceEpoch(); // Using ksycoca should now build a local one ksycoca_ms_between_checks = 0; QVERIFY(KServiceType::serviceType(QStringLiteral("FakeLocalServiceType"))); QVERIFY(QFile::exists(KSycoca::absoluteFilePath())); } void KSycocaTest::testNonReadableSycoca() { // Lose readability (to simulate e.g. owned by root) QFile(KSycoca::absoluteFilePath()).setPermissions(QFile::WriteOwner); ksycoca_ms_between_checks = 0; KBuildSycoca builder; QVERIFY(builder.recreate()); QVERIFY(KServiceType::serviceType(QStringLiteral("FakeGlobalServiceType"))); // cleanup QFile::remove(KSycoca::absoluteFilePath()); } #include "ksycocatest.moc" diff --git a/autotests/pluginlocatortest.cpp b/autotests/pluginlocatortest.cpp index ab7f20f..dc36baf 100644 --- a/autotests/pluginlocatortest.cpp +++ b/autotests/pluginlocatortest.cpp @@ -1,87 +1,87 @@ /****************************************************************************** * Copyright 2013 Sebastian Kügler * * * * This library is free software; you can redistribute it and/or * * modify it under the terms of the GNU Library General Public * * License as published by the Free Software Foundation; either * * version 2 of the License, or (at your option) any later version. * * * * This library is distributed in the hope that it will be useful, * * but WITHOUT ANY WARRANTY; without even the implied warranty of * * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * * Library General Public License for more details. * * * * You should have received a copy of the GNU Library General Public License * * along with this library; see the file COPYING.LIB. If not, write to * * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, * * Boston, MA 02110-1301, USA. * *******************************************************************************/ #include "pluginlocatortest.h" #include "nsaplugin.h" #include #include #include #include #include QTEST_MAIN(PluginTest) void PluginTest::initTestCase() { QCoreApplication::setLibraryPaths(QStringList() << QDir::currentPath()); } void PluginTest::findPlugin_data() { QTest::addColumn("serviceType"); QTest::addColumn("constraint"); QTest::addColumn("expectedResult"); const QString _st = QStringLiteral("KService/NSA"); QString _c = ""; QTest::newRow("no constraints") << _st << _c << 1; _c = QStringLiteral("[X-KDE-PluginInfo-Name] == '%1'").arg(QStringLiteral("fakeplugin")); QTest::newRow("by pluginname") << _st << _c << 1; _c = QStringLiteral("[X-KDE-PluginInfo-Category] == '%1'").arg(QStringLiteral("Examples")); QTest::newRow("by category") << _st << _c << 1; _c = QStringLiteral("([X-KDE-PluginInfo-Category] == 'Examples') AND ([X-KDE-PluginInfo-Email] == 'sebas@kde.org')"); QTest::newRow("complex query") << _st << _c << 1; _c = QStringLiteral("([X-KDE-PluginInfo-Category] == 'Examples') AND ([X-KDE-PluginInfo-Email] == 'prrrrt')"); QTest::newRow("empty query") << _st << _c << 0; } void PluginTest::findPlugin() { QFETCH(QString, serviceType); QFETCH(QString, constraint); QFETCH(int, expectedResult); const KPluginInfo::List res = KPluginTrader::self()->query(QString(), serviceType, constraint); QCOMPARE(res.count(), expectedResult); } void PluginTest::findSomething() { const KPluginInfo::List res = KPluginTrader::self()->query(QString()); QVERIFY(res.count() > 0); } void PluginTest::loadPlugin() { const QString pluginName("fakeplugin"); const QString serviceType("KService/NSA"); const QString constraint = QStringLiteral("[X-KDE-PluginInfo-Name] == '%1'").arg(pluginName); QObject *plugin = KPluginTrader::createInstanceFromQuery(QString(), serviceType, constraint, this); - QVERIFY(plugin != 0); + QVERIFY(plugin != nullptr); QCOMPARE(plugin->objectName(), QStringLiteral("Test Plugin Spy")); } #include "moc_pluginlocatortest.cpp" diff --git a/src/kdeinit/ktoolinvocation.cpp b/src/kdeinit/ktoolinvocation.cpp index c9eb922..7e7f263 100644 --- a/src/kdeinit/ktoolinvocation.cpp +++ b/src/kdeinit/ktoolinvocation.cpp @@ -1,299 +1,299 @@ /* This file is part of the KDE libraries Copyright (C) 2005 Brad Hards Copyright (C) 2006 Thiago Macieira This library is free software; you can redistribute it and/or modify it under the terms of the GNU Library General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Library General Public License for more details. You should have received a copy of the GNU Library General Public License along with this library; see the file COPYING.LIB. If not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #include "ktoolinvocation.h" #include "klauncher_iface.h" #include #include #include #include #include #include #include // for EINVAL class KToolInvocationSingleton { public: KToolInvocation instance; }; Q_GLOBAL_STATIC(KToolInvocationSingleton, s_self) KToolInvocation *KToolInvocation::self() { return &s_self()->instance; } -KToolInvocation::KToolInvocation() : QObject(0), d(0) +KToolInvocation::KToolInvocation() : QObject(nullptr), d(nullptr) { } KToolInvocation::~KToolInvocation() { } static void printError(const QString &text, QString *error) { if (error) { *error = text; } else { qWarning() << text; } } bool KToolInvocation::isMainThreadActive(QString *error) { if (QCoreApplication::instance() && QCoreApplication::instance()->thread() != QThread::currentThread()) { printError(i18n("Function must be called from the main thread."), error); return false; } return true; } int KToolInvocation::startServiceInternal(const char *_function, const QString &_name, const QStringList &URLs, QString *error, QString *serviceName, int *pid, const QByteArray &startup_id, bool noWait, const QString &workdir) { QString function = QLatin1String(_function); KToolInvocation::ensureKdeinitRunning(); QDBusMessage msg = QDBusMessage::createMethodCall(QStringLiteral("org.kde.klauncher5"), QStringLiteral("/KLauncher"), QStringLiteral("org.kde.KLauncher"), function); msg << _name << URLs; if (function == QLatin1String("kdeinit_exec_with_workdir")) { msg << workdir; } // make sure there is id, so that user timestamp exists QStringList envs; QByteArray s = startup_id; emit kapplication_hook(envs, s); msg << envs; msg << QString::fromLatin1(s); if (!function.startsWith(QLatin1String("kdeinit_exec"))) { msg << noWait; } QDBusMessage reply = QDBusConnection::sessionBus().call(msg, QDBus::Block, INT_MAX); if (reply.type() != QDBusMessage::ReplyMessage) { QDBusReply replyObj(reply); if (replyObj.error().type() == QDBusError::NoReply) { printError(i18n("Error launching %1. Either KLauncher is not running anymore, or it failed to start the application.", _name), error); } else { const QString rpl = reply.arguments().count() > 0 ? reply.arguments().at(0).toString() : reply.errorMessage(); printError(i18n("KLauncher could not be reached via D-Bus. Error when calling %1:\n%2\n", function, rpl), error); } //qDebug() << reply; return EINVAL; } if (noWait) { return 0; } Q_ASSERT(reply.arguments().count() == 4); if (serviceName) { *serviceName = reply.arguments().at(1).toString(); } if (error) { *error = reply.arguments().at(2).toString(); } if (pid) { *pid = reply.arguments().at(3).toInt(); } return reply.arguments().at(0).toInt(); } #ifndef KSERVICE_NO_DEPRECATED int KToolInvocation::startServiceByName(const QString &_name, const QString &URL, QString *error, QString *serviceName, int *pid, const QByteArray &startup_id, bool noWait) { if (!isMainThreadActive(error)) { return EINVAL; } QStringList URLs; if (!URL.isEmpty()) { URLs.append(URL); } return self()->startServiceInternal("start_service_by_name", _name, URLs, error, serviceName, pid, startup_id, noWait); } #endif #ifndef KSERVICE_NO_DEPRECATED int KToolInvocation::startServiceByName(const QString &_name, const QStringList &URLs, QString *error, QString *serviceName, int *pid, const QByteArray &startup_id, bool noWait) { if (!isMainThreadActive(error)) { return EINVAL; } return self()->startServiceInternal("start_service_by_name", _name, URLs, error, serviceName, pid, startup_id, noWait); } #endif int KToolInvocation::startServiceByDesktopPath(const QString &_name, const QString &URL, QString *error, QString *serviceName, int *pid, const QByteArray &startup_id, bool noWait) { if (!isMainThreadActive(error)) { return EINVAL; } QStringList URLs; if (!URL.isEmpty()) { URLs.append(URL); } return self()->startServiceInternal("start_service_by_desktop_path", _name, URLs, error, serviceName, pid, startup_id, noWait); } int KToolInvocation::startServiceByDesktopPath(const QString &_name, const QStringList &URLs, QString *error, QString *serviceName, int *pid, const QByteArray &startup_id, bool noWait) { if (!isMainThreadActive(error)) { return EINVAL; } return self()->startServiceInternal("start_service_by_desktop_path", _name, URLs, error, serviceName, pid, startup_id, noWait); } int KToolInvocation::startServiceByDesktopName(const QString &_name, const QString &URL, QString *error, QString *serviceName, int *pid, const QByteArray &startup_id, bool noWait) { if (!isMainThreadActive(error)) { return EINVAL; } QStringList URLs; if (!URL.isEmpty()) { URLs.append(URL); } return self()->startServiceInternal("start_service_by_desktop_name", _name, URLs, error, serviceName, pid, startup_id, noWait); } int KToolInvocation::startServiceByDesktopName(const QString &_name, const QStringList &URLs, QString *error, QString *serviceName, int *pid, const QByteArray &startup_id, bool noWait) { if (!isMainThreadActive(error)) { return EINVAL; } return self()->startServiceInternal("start_service_by_desktop_name", _name, URLs, error, serviceName, pid, startup_id, noWait); } int KToolInvocation::kdeinitExec(const QString &name, const QStringList &args, QString *error, int *pid, const QByteArray &startup_id) { if (!isMainThreadActive(error)) { return EINVAL; } return self()->startServiceInternal("kdeinit_exec", - name, args, error, 0, pid, startup_id, false); + name, args, error, nullptr, pid, startup_id, false); } int KToolInvocation::kdeinitExecWait(const QString &name, const QStringList &args, QString *error, int *pid, const QByteArray &startup_id) { if (!isMainThreadActive(error)) { return EINVAL; } return self()->startServiceInternal("kdeinit_exec_wait", - name, args, error, 0, pid, startup_id, false); + name, args, error, nullptr, pid, startup_id, false); } void KToolInvocation::invokeMailer(const QString &address, const QString &subject, const QByteArray &startup_id) { if (!isMainThreadActive()) { return; } invokeMailer(address, QString(), QString(), subject, QString(), QString(), QStringList(), startup_id); } void KToolInvocation::invokeMailer(const QUrl &mailtoURL, const QByteArray &startup_id, bool allowAttachments) { if (!isMainThreadActive()) { return; } QString address = mailtoURL.path(); QString subject; QString cc; QString bcc; QString body; QList > queryItems = QUrlQuery(mailtoURL).queryItems(); const QChar comma = QChar::fromLatin1(','); QStringList attachURLs; for (int i = 0; i < queryItems.count(); ++i) { const QString q = queryItems.at(i).first.toLower(); const QString value = queryItems.at(i).second; if (q == QLatin1String("subject")) { subject = value; } else if (q == QLatin1String("cc")) { cc = cc.isEmpty() ? value : cc + comma + value; } else if (q == QLatin1String("bcc")) { bcc = bcc.isEmpty() ? value : bcc + comma + value; } else if (q == QLatin1String("body")) { body = value; } else if (allowAttachments && q == QLatin1String("attach")) { attachURLs.push_back(value); } else if (allowAttachments && q == QLatin1String("attachment")) { attachURLs.push_back(value); } else if (q == QLatin1String("to")) { address = address.isEmpty() ? value : address + comma + value; } } invokeMailer(address, cc, bcc, subject, body, QString(), attachURLs, startup_id); } void KToolInvocation::ensureKdeinitRunning() { KDEInitInterface::ensureKdeinitRunning(); } diff --git a/src/kdeinit/ktoolinvocation.h b/src/kdeinit/ktoolinvocation.h index 7378c23..593ee3f 100644 --- a/src/kdeinit/ktoolinvocation.h +++ b/src/kdeinit/ktoolinvocation.h @@ -1,392 +1,392 @@ /* This file is part of the KDE libraries Copyright (c) 1997-1999 Matthias Kalle Dalheimer Copyright (c) 1997-2000 Matthias Ettrich Copyright (c) 1998-2005 Stephan Kulow Copyright (c) 1999-2004 Waldo Bastian Copyright (c) 2001-2005 Lubos Lunak Copyright (C) 2008 Aaron Seigo This library is free software; you can redistribute it and/or modify it under the terms of the GNU Library General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Library General Public License for more details. You should have received a copy of the GNU Library General Public License along with this library; see the file COPYING.LIB. If not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #ifndef _KTOOLINVOCATION_H #define _KTOOLINVOCATION_H #include #include #include #include class QUrl; class KToolInvocationPrivate; /** * KToolInvocation: for starting other programs * * @section desktopfiles Desktop files for startServiceBy * * The way a service gets started depends on the 'X-DBUS-StartupType' * entry in the desktop file of the service: * * There are three possibilities: * @li X-DBUS-StartupType=None (default) * Always start a new service, * don't wait till the service registers with D-Bus. * @li X-DBUS-StartupType=Multi * Always start a new service, * wait until the service has registered with D-Bus. * @li X-DBUS-StartupType=Unique * Only start the service if it isn't already running, * wait until the service has registered with D-Bus. * The .desktop file can specify the name that the application will use when registering * using X-DBUS-ServiceName=org.domain.mykapp. Otherwise org.kde.binaryname is assumed. * * @section thread Multi-threading * * The static members (apart from self()), have to be called from the QApplication main thread. * Calls to members are only allowed if there is a Q(Core)Application object created * If you call the members with signal/slot connections across threads, you can't use the return values * If a function is called from the wrong thread and it has a return value -1 is returned * Investigate if this is really needed or if D-Bus is threadsafe anyway * * For more details see techbase. * */ class KSERVICE_EXPORT KToolInvocation : public QObject { Q_OBJECT private: KToolInvocation(); public: // @internal ~KToolInvocation(); static KToolInvocation *self(); public Q_SLOTS: /** * Convenience method; invokes the standard email application. * * @param address The destination address * @param subject Subject string. Can be QString(). * @param startup_id for app startup notification, "0" for none, * "" ( empty string ) is the default * * @deprecated since 5.0, use QDesktopServices::openUrl(mailtoURL), * using QUrl::setPath(address) and a query item of "subject" for the subject. */ KSERVICE_DEPRECATED static void invokeMailer(const QString &address, const QString &subject, const QByteArray &startup_id = QByteArray()); /** * Invokes the standard email application. * * @param mailtoURL A mailto URL. * @param startup_id for app startup notification, "0" for none, * "" ( empty string ) is the default * @param allowAttachments whether attachments specified in mailtoURL should be honoured. * The default is false; do not honor requests for attachments. * @deprecated since 5.0, use QDesktopServices::openUrl(mailtoURL) */ KSERVICE_DEPRECATED static void invokeMailer(const QUrl &mailtoURL, const QByteArray &startup_id = QByteArray(), bool allowAttachments = false); /** * Convenience method; invokes the standard email application. * * All parameters are optional. * * @param to The destination address. * @param cc The Cc field * @param bcc The Bcc field * @param subject Subject string * @param body A string containing the body of the mail (exclusive with messageFile) * @param messageFile A file (URL) containing the body of the mail (exclusive with body) - currently unsupported * @param attachURLs List of URLs to be attached to the mail. * @param startup_id for app startup notification, "0" for none, * "" ( empty string ) is the default */ static void invokeMailer(const QString &to, const QString &cc, const QString &bcc, const QString &subject, const QString &body, const QString &messageFile = QString(), const QStringList &attachURLs = QStringList(), const QByteArray &startup_id = QByteArray()); /** * Invokes the user's preferred browser. * Note that you should only do this when you know for sure that the browser can * handle the URL (i.e. its mimetype). In doubt, if the URL can point to an image * or anything else than HTML, prefer to use new KRun( url ). * * See also 0). * @deprecated Use startServiceByDesktopName or startServiceByDesktopPath */ #ifndef KSERVICE_NO_DEPRECATED KSERVICE_DEPRECATED static int startServiceByName(const QString &_name, const QString &URL, - QString *error = 0, QString *serviceName = 0, int *pid = 0, + QString *error = nullptr, QString *serviceName = nullptr, int *pid = nullptr, const QByteArray &startup_id = QByteArray(), bool noWait = false); #endif /** * Starts a service based on the (translated) name of the service. * E.g. "Web Browser" * * @param _name the name of the service * @param URLs if not empty these URLs will be passed to the service * @param error On failure, @p error contains a description of the error * that occurred. If the pointer is 0, the argument will be * ignored * @param serviceName On success, @p serviceName contains the DCOP name * under which this service is available. If empty, the service does * not provide DCOP services. If the pointer is 0 the argument * will be ignored * @param pid On success, the process id of the new service will be written * here. If the pointer is 0, the argument will be ignored. * @param startup_id for app startup notification, "0" for none, * "" ( empty string ) is the default * @param noWait if set, the function does not wait till the service is running. * @return an error code indicating success (== 0) or failure (> 0). * @deprecated Use startServiceByDesktopName or startServiceByDesktopPath */ #ifndef KSERVICE_NO_DEPRECATED KSERVICE_DEPRECATED static int startServiceByName(const QString &_name, const QStringList &URLs = QStringList(), - QString *error = 0, QString *serviceName = 0, int *pid = 0, + QString *error = nullptr, QString *serviceName = nullptr, int *pid = nullptr, const QByteArray &startup_id = QByteArray(), bool noWait = false); #endif /** * Starts a service based on the desktop path of the service. * E.g. "Applications/konqueror.desktop" or "/home/user/bla/myfile.desktop" * * @param _name the path of the desktop file * @param URL if not empty this URL is passed to the service * @param error On failure, @p error contains a description of the error * that occurred. If the pointer is 0, the argument will be * ignored * @param serviceName On success, @p serviceName contains the DCOP name * under which this service is available. If empty, the service does * not provide DCOP services. If the pointer is 0 the argument * will be ignored * @param pid On success, the process id of the new service will be written * here. If the pointer is 0, the argument will be ignored. * @param startup_id for app startup notification, "0" for none, * "" ( empty string ) is the default * @param noWait if set, the function does not wait till the service is running. * @return an error code indicating success (== 0) or failure (> 0). * * @deprecated since 5.0 use QDBusConnectionInterface::startService("org.kde.serviceName"), * to start a unique application in order to make dbus calls to it (after ensuring that * it installs a dbus org.kde.serviceName.service file). Otherwise just use QProcess or KRun. */ static int startServiceByDesktopPath(const QString &_name, const QString &URL, - QString *error = 0, QString *serviceName = 0, int *pid = 0, + QString *error = nullptr, QString *serviceName = nullptr, int *pid = nullptr, const QByteArray &startup_id = QByteArray(), bool noWait = false); /** * Starts a service based on the desktop path of the service. * E.g. "Applications/konqueror.desktop" or "/home/user/bla/myfile.desktop" * * @param _name the path of the desktop file * @param URLs if not empty these URLs will be passed to the service * @param error On failure, @p error contains a description of the error * that occurred. If the pointer is 0, the argument will be * ignored * @param serviceName On success, @p serviceName contains the DCOP name * under which this service is available. If empty, the service does * not provide DCOP services. If the pointer is 0 the argument * will be ignored * @param pid On success, the process id of the new service will be written * here. If the pointer is 0, the argument will be ignored. * @param startup_id for app startup notification, "0" for none, * "" ( empty string ) is the default * @param noWait if set, the function does not wait till the service is running. * @return an error code indicating success (== 0) or failure (> 0). * @deprecated since 5.0 use QDBusConnectionInterface::startService("org.kde.serviceName"), * to start a unique application in order to make dbus calls to it (after ensuring that * it installs a dbus org.kde.serviceName.service file). Otherwise just use QProcess or KRun. */ static int startServiceByDesktopPath(const QString &_name, const QStringList &URLs = QStringList(), - QString *error = 0, QString *serviceName = 0, int *pid = 0, + QString *error = nullptr, QString *serviceName = nullptr, int *pid = nullptr, const QByteArray &startup_id = QByteArray(), bool noWait = false); /** * Starts a service based on the desktop name of the service. * E.g. "konqueror" * * @param _name the desktop name of the service * @param URL if not empty this URL is passed to the service * @param error On failure, @p error contains a description of the error * that occurred. If the pointer is 0, the argument will be * ignored * @param serviceName On success, @p serviceName contains the D-Bus service name * under which this service is available. If empty, the service does * not provide D-Bus services. If the pointer is 0 the argument * will be ignored * @param pid On success, the process id of the new service will be written * here. If the pointer is 0, the argument will be ignored. * @param startup_id for app startup notification, "0" for none, * "" ( empty string ) is the default * @param noWait if set, the function does not wait till the service is running. * @return an error code indicating success (== 0) or failure (> 0). * @deprecated since 5.0 use QDBusConnectionInterface::startService("org.kde.serviceName"), * to start a unique application in order to make dbus calls to it (after ensuring that * it installs a dbus org.kde.serviceName.service file). Otherwise just use QProcess or KRun. */ static int startServiceByDesktopName(const QString &_name, const QString &URL, - QString *error = 0, QString *serviceName = 0, int *pid = 0, + QString *error = nullptr, QString *serviceName = nullptr, int *pid = nullptr, const QByteArray &startup_id = QByteArray(), bool noWait = false); /** * Starts a service based on the desktop name of the service. * E.g. "konqueror" * * @param _name the desktop name of the service * @param URLs if not empty these URLs will be passed to the service * @param error On failure, @p error contains a description of the error * that occurred. If the pointer is 0, the argument will be * ignored * @param serviceName On success, @p serviceName contains the D-Bus service name * under which this service is available. If empty, the service does * not provide D-Bus services. If the pointer is 0 the argument * will be ignored * @param pid On success, the process id of the new service will be written * here. If the pointer is 0, the argument will be ignored. * @param startup_id for app startup notification, "0" for none, * "" ( empty string ) is the default * @param noWait if set, the function does not wait till the service is running. * @return an error code indicating success (== 0) or failure (> 0). * @deprecated since 5.0 use QDBusConnectionInterface::startService("org.kde.serviceName"), * to start a unique application in order to make dbus calls to it (after ensuring that * it installs a dbus org.kde.serviceName.service file). Otherwise just use QProcess or KRun. */ static int startServiceByDesktopName(const QString &_name, const QStringList &URLs = QStringList(), - QString *error = 0, QString *serviceName = 0, int *pid = 0, + QString *error = nullptr, QString *serviceName = nullptr, int *pid = nullptr, const QByteArray &startup_id = QByteArray(), bool noWait = false); /** * Starts a program via kdeinit. * * program name and arguments are converted to according to the * local encoding and passed as is to kdeinit. * * @param name Name of the program to start * @param args Arguments to pass to the program * @param error On failure, @p error contains a description of the error * that occurred. If the pointer is 0, the argument will be * ignored * @param pid On success, the process id of the new service will be written * here. If the pointer is 0, the argument will be ignored. * @param startup_id for app startup notification, "0" for none, * "" ( empty string ) is the default * @return an error code indicating success (== 0) or failure (> 0). */ static int kdeinitExec(const QString &name, const QStringList &args = QStringList(), - QString *error = 0, int *pid = 0, const QByteArray &startup_id = QByteArray()); + QString *error = nullptr, int *pid = nullptr, const QByteArray &startup_id = QByteArray()); /** * Starts a program via kdeinit and wait for it to finish. * * Like kdeinitExec(), but it waits till the program is finished. * As such it behaves similar to the system(...) function. * * @param name Name of the program to start * @param args Arguments to pass to the program * @param error On failure, @p error contains a description of the error * that occurred. If the pointer is 0, the argument will be * ignored * @param pid On success, the process id of the new service will be written * here. If the pointer is 0, the argument will be ignored. * @param startup_id for app startup notification, "0" for none, * "" ( empty string ) is the default * @return an error code indicating success (== 0) or failure (> 0). */ static int kdeinitExecWait(const QString &name, const QStringList &args = QStringList(), - QString *error = 0, int *pid = 0, const QByteArray &startup_id = QByteArray()); + QString *error = nullptr, int *pid = nullptr, const QByteArray &startup_id = QByteArray()); /** * Ensures that kdeinit5 and klauncher are running. */ static void ensureKdeinitRunning(); Q_SIGNALS: /** * Hook for KApplication in kdeui * @internal */ void kapplication_hook(QStringList &env, QByteArray &startup_id); private: int startServiceInternal(const char *_function, const QString &_name, const QStringList &URLs, QString *error, QString *serviceName, int *pid, const QByteArray &startup_id, bool noWait, const QString &workdir = QString()); - static bool isMainThreadActive(QString *error = 0); + static bool isMainThreadActive(QString *error = nullptr); KToolInvocationPrivate *const d; friend class KToolInvocationSingleton; }; #endif diff --git a/src/kdeinit/ktoolinvocation_x11.cpp b/src/kdeinit/ktoolinvocation_x11.cpp index 7413ea6..db21a3d 100644 --- a/src/kdeinit/ktoolinvocation_x11.cpp +++ b/src/kdeinit/ktoolinvocation_x11.cpp @@ -1,388 +1,388 @@ /* This file is part of the KDE libraries Copyright (c) 1997,1998 Matthias Kalle Dalheimer Copyright (c) 1999 Espen Sand Copyright (c) 2000-2004 Frerich Raabe Copyright (c) 2003,2004 Oswald Buddenhagen Copyright (c) 2006 Thiago Macieira Copyright (C) 2008 Aaron Seigo This library is free software; you can redistribute it and/or modify it under the terms of the GNU Library General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Library General Public License for more details. You should have received a copy of the GNU Library General Public License along with this library; see the file COPYING.LIB. If not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #include "ktoolinvocation.h" #include #include #include #include "kconfig.h" #include "kshell.h" #include "kmacroexpander.h" #include "klocalizedstring.h" #include "kmessage.h" #include "kservice.h" #include #include #include #include #include #include #include #include #include static QStringList splitEmailAddressList(const QString &aStr) { // This is a copy of KPIM::splitEmailAddrList(). // Features: // - always ignores quoted characters // - ignores everything (including parentheses and commas) // inside quoted strings // - supports nested comments // - ignores everything (including double quotes and commas) // inside comments QStringList list; if (aStr.isEmpty()) { return list; } QString addr; uint addrstart = 0; int commentlevel = 0; bool insidequote = false; for (int index = 0; index < aStr.length(); index++) { // the following conversion to latin1 is o.k. because // we can safely ignore all non-latin1 characters switch (aStr[index].toLatin1()) { case '"' : // start or end of quoted string if (commentlevel == 0) { insidequote = !insidequote; } break; case '(' : // start of comment if (!insidequote) { commentlevel++; } break; case ')' : // end of comment if (!insidequote) { if (commentlevel > 0) { commentlevel--; } else { //qDebug() << "Error in address splitting: Unmatched ')'" // << endl; return list; } } break; case '\\' : // quoted character index++; // ignore the quoted character break; case ',' : if (!insidequote && (commentlevel == 0)) { addr = aStr.mid(addrstart, index - addrstart); if (!addr.isEmpty()) { list += addr.simplified(); } addrstart = index + 1; } break; } } // append the last address to the list if (!insidequote && (commentlevel == 0)) { addr = aStr.mid(addrstart, aStr.length() - addrstart); if (!addr.isEmpty()) { list += addr.simplified(); } } //else // qDebug() << "Error in address splitting: " // << "Unexpected end of address list" // << endl; return list; } void KToolInvocation::invokeMailer(const QString &_to, const QString &_cc, const QString &_bcc, const QString &subject, const QString &body, const QString & /*messageFile TODO*/, const QStringList &attachURLs, const QByteArray &startup_id) { if (!isMainThreadActive()) { return; } KConfig config(QStringLiteral("emaildefaults")); KConfigGroup defaultsGrp(&config, "Defaults"); QString group = defaultsGrp.readEntry("Profile", "Default"); KConfigGroup profileGrp(&config, QStringLiteral("PROFILE_%1").arg(group)); QString command = profileGrp.readPathEntry("EmailClient", QString()); QString to, cc, bcc; if (command.isEmpty() || command == QLatin1String("kmail") || command.endsWith(QLatin1String("/kmail"))) { command = QStringLiteral("kmail --composer -s %s -c %c -b %b --body %B --attach %A -- %t"); if (!_to.isEmpty()) { QUrl url; url.setScheme(QStringLiteral("mailto")); url.setPath(_to); to = QString::fromLatin1(url.toEncoded()); } if (!_cc.isEmpty()) { QUrl url; url.setScheme(QStringLiteral("mailto")); url.setPath(_cc); cc = QString::fromLatin1(url.toEncoded()); } if (!_bcc.isEmpty()) { QUrl url; url.setScheme(QStringLiteral("mailto")); url.setPath(_bcc); bcc = QString::fromLatin1(url.toEncoded()); } } else { to = _to; cc = _cc; bcc = _bcc; if (!command.contains(QLatin1Char('%'))) { command += QLatin1String(" %u"); } } if (profileGrp.readEntry("TerminalClient", false)) { KConfigGroup confGroup(KSharedConfig::openConfig(), "General"); QString preferredTerminal = confGroup.readPathEntry("TerminalApplication", QStringLiteral("konsole")); command = preferredTerminal + QString::fromLatin1(" -e ") + command; } QStringList cmdTokens = KShell::splitArgs(command); QString cmd = cmdTokens.takeFirst(); QUrl url; QUrlQuery query; if (!to.isEmpty()) { QStringList tos = splitEmailAddressList(to); url.setPath(tos.first()); tos.erase(tos.begin()); for (QStringList::ConstIterator it = tos.constBegin(); it != tos.constEnd(); ++it) { query.addQueryItem(QStringLiteral("to"), *it); } } const QStringList ccs = splitEmailAddressList(cc); for (QStringList::ConstIterator it = ccs.constBegin(); it != ccs.constEnd(); ++it) { query.addQueryItem(QStringLiteral("cc"), *it); } const QStringList bccs = splitEmailAddressList(bcc); for (QStringList::ConstIterator it = bccs.constBegin(); it != bccs.constEnd(); ++it) { query.addQueryItem(QStringLiteral("bcc"), *it); } for (QStringList::ConstIterator it = attachURLs.constBegin(); it != attachURLs.constEnd(); ++it) { query.addQueryItem(QStringLiteral("attach"), *it); } if (!subject.isEmpty()) { query.addQueryItem(QStringLiteral("subject"), subject); } if (!body.isEmpty()) { query.addQueryItem(QStringLiteral("body"), body); } url.setQuery(query); if (!(to.isEmpty() && (!url.hasQuery()))) { url.setScheme(QStringLiteral("mailto")); } QHash keyMap; keyMap.insert(QLatin1Char('t'), to); keyMap.insert(QLatin1Char('s'), subject); keyMap.insert(QLatin1Char('c'), cc); keyMap.insert(QLatin1Char('b'), bcc); keyMap.insert(QLatin1Char('B'), body); keyMap.insert(QLatin1Char('u'), url.toString()); QString attachlist = attachURLs.join(QLatin1Char(',')); attachlist.prepend(QLatin1Char('\'')); attachlist.append(QLatin1Char('\'')); keyMap.insert(QLatin1Char('A'), attachlist); for (int i = 0; i < cmdTokens.count(); ++i) { if (cmdTokens.at(i) == QLatin1String("%A")) { if (attachURLs.isEmpty()) { cmdTokens.removeAt(i); } else { const QString previousStr = cmdTokens.at(i-1); cmdTokens.removeAt(i); const int currentPos = i; Q_FOREACH(const QString &url, attachURLs) { cmdTokens.insert(currentPos, previousStr); cmdTokens.insert(currentPos, url); i += 2; } } } else { const QString str = KMacroExpander::expandMacros(cmdTokens.at(i), keyMap); cmdTokens[i] = str; } } QString error; // TODO this should check if cmd has a .desktop file, and use data from it, together // with sending more ASN data - if (kdeinitExec(cmd, cmdTokens, &error, NULL, startup_id)) { + if (kdeinitExec(cmd, cmdTokens, &error, nullptr, startup_id)) { KMessage::message(KMessage::Error, i18n("Could not launch the mail client:\n\n%1", error), i18n("Could not launch Mail Client")); } } void KToolInvocation::invokeBrowser(const QString &url, const QByteArray &startup_id) { if (!isMainThreadActive()) { return; } QStringList args; args << url; QString error; // This method should launch a webbrowser, preferably without doing a mimetype // check first, like KRun (i.e. kde-open) would do. // In a KDE session, honour BrowserApplication if set, otherwise use preferred app for text/html if any, // otherwise xdg-open, otherwise kde-open (which does a mimetype check first though). // Outside KDE, call xdg-open if present, otherwise fallback to the above logic. QString exe; // the binary we are going to launch. const QString xdg_open = QStandardPaths::findExecutable(QStringLiteral("xdg-open")); if (qEnvironmentVariableIsEmpty("KDE_FULL_SESSION")) { exe = xdg_open; } if (exe.isEmpty()) { // We're in a KDE session (or there's no xdg-open installed) KConfigGroup config(KSharedConfig::openConfig(), "General"); const QString browserApp = config.readPathEntry("BrowserApplication", QString()); if (!browserApp.isEmpty()) { exe = browserApp; if (exe.startsWith(QLatin1Char('!'))) { exe = exe.mid(1); // Literal command QStringList cmdTokens = KShell::splitArgs(exe); exe = cmdTokens.takeFirst(); args = cmdTokens + args; } else { // desktop file ID KService::Ptr service = KService::serviceByStorageId(exe); if (service) { //qDebug() << "Starting service" << service->entryPath(); if (startServiceByDesktopPath(service->entryPath(), args, - &error, 0, 0, startup_id)) { + &error, nullptr, nullptr, startup_id)) { KMessage::message(KMessage::Error, // TODO: i18n("Could not launch %1:\n\n%2", exe, error), i18n("Could not launch the browser:\n\n%1", error), i18n("Could not launch Browser")); } return; } } } else { const KService::Ptr htmlApp = KMimeTypeTrader::self()->preferredService(QStringLiteral("text/html")); if (htmlApp) { // WORKAROUND: For bugs 264562 and 265474: // In order to correctly handle non-HTML urls we change the service // desktop file name to "kfmclient.desktop" whenever the above query // returns "kfmclient_html.desktop".Otherwise, the hard coded mime-type // "text/html" mime-type parameter in the kfmclient_html will cause all // URLs to be treated as if they are HTML page. QString entryPath = htmlApp->entryPath(); if (entryPath.endsWith(QLatin1String("kfmclient_html.desktop"))) { entryPath.remove(entryPath.length() - 13, 5); } QString error; int pid = 0; - int err = startServiceByDesktopPath(entryPath, url, &error, 0, &pid, startup_id); + int err = startServiceByDesktopPath(entryPath, url, &error, nullptr, &pid, startup_id); if (err != 0) { KMessage::message(KMessage::Error, // TODO: i18n("Could not launch %1:\n\n%2", htmlApp->exec(), error), i18n("Could not launch the browser:\n\n%1", error), i18n("Could not launch Browser")); } else { // success return; } } else { exe = xdg_open; } } } if (exe.isEmpty()) { exe = QStringLiteral("kde-open"); // it's from kdebase-runtime, it has to be there. } //qDebug() << "Using" << exe << "to open" << url; - if (kdeinitExec(exe, args, &error, NULL, startup_id)) { + if (kdeinitExec(exe, args, &error, nullptr, startup_id)) { KMessage::message(KMessage::Error, // TODO: i18n("Could not launch %1:\n\n%2", exe, error), i18n("Could not launch the browser:\n\n%1", error), i18n("Could not launch Browser")); } } void KToolInvocation::invokeTerminal(const QString &command, const QString &workdir, const QByteArray &startup_id) { if (!isMainThreadActive()) { return; } KConfigGroup confGroup(KSharedConfig::openConfig(), "General"); QString exec = confGroup.readPathEntry("TerminalApplication", QStringLiteral("konsole")); if (!command.isEmpty()) { if (exec == QLatin1String("konsole")) { exec += QString::fromLatin1(" --noclose"); } else if (exec == QLatin1String("xterm")) { exec += QString::fromLatin1(" -hold"); } exec += QString::fromLatin1(" -e ") + command; } QStringList cmdTokens = KShell::splitArgs(exec); QString cmd = cmdTokens.takeFirst(); if (exec == QLatin1String("konsole") && !workdir.isEmpty()) { cmdTokens << QStringLiteral("--workdir"); cmdTokens << workdir; // For other terminals like xterm, we'll simply change the working // directory before launching them, see below. } QString error; if (self()->startServiceInternal("kdeinit_exec_with_workdir", - cmd, cmdTokens, &error, 0, NULL, startup_id, false, workdir)) { + cmd, cmdTokens, &error, nullptr, nullptr, startup_id, false, workdir)) { KMessage::message(KMessage::Error, i18n("Could not launch the terminal client:\n\n%1", error), i18n("Could not launch Terminal Client")); } } diff --git a/src/plugin/kdbusservicestarter.cpp b/src/plugin/kdbusservicestarter.cpp index 4655fd8..5a361c4 100644 --- a/src/plugin/kdbusservicestarter.cpp +++ b/src/plugin/kdbusservicestarter.cpp @@ -1,113 +1,113 @@ /* This file is part of the KDE libraries Copyright (C) 2003 David Faure This library is free software; you can redistribute it and/or modify it under the terms of the GNU Library General Public License version 2 as published by the Free Software Foundation. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Library General Public License for more details. You should have received a copy of the GNU Library General Public License along with this library; see the file COPYING.LIB. If not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #include "kdbusservicestarter.h" #include "kservicetypetrader.h" #include "kservice.h" #include #include #include #include #include class KDBusServiceStarterPrivate { public: - KDBusServiceStarterPrivate() : q(0) {} + KDBusServiceStarterPrivate() : q(nullptr) {} ~KDBusServiceStarterPrivate() { delete q; } KDBusServiceStarter *q; }; Q_GLOBAL_STATIC(KDBusServiceStarterPrivate, privateObject) KDBusServiceStarter *KDBusServiceStarter::self() { if (!privateObject()->q) { new KDBusServiceStarter; Q_ASSERT(privateObject()->q); } return privateObject()->q; } KDBusServiceStarter::KDBusServiceStarter() { // Set the singleton instance - useful when a derived KDBusServiceStarter // was created (before self() was called) Q_ASSERT(!privateObject()->q); privateObject()->q = this; } KDBusServiceStarter::~KDBusServiceStarter() { } int KDBusServiceStarter::findServiceFor(const QString &serviceType, const QString &_constraint, QString *error, QString *pDBusService, int flags) { // Ask the trader which service is preferred for this servicetype // We want one that provides a DBus interface QString constraint = _constraint; if (!constraint.isEmpty()) { constraint += QLatin1String(" and "); } constraint += QLatin1String("exist [X-DBUS-ServiceName]"); const KService::List offers = KServiceTypeTrader::self()->query(serviceType, constraint); if (offers.isEmpty()) { if (error) { *error = i18n("No service implementing %1", serviceType); } qWarning() << "KDBusServiceStarter: No service implementing " << serviceType; return -1; } KService::Ptr ptr = offers.first(); QString dbusService = ptr->property(QStringLiteral("X-DBUS-ServiceName")).toString(); if (!QDBusConnection::sessionBus().interface()->isServiceRegistered(dbusService)) { QString err; if (startServiceFor(serviceType, constraint, &err, &dbusService, flags) != 0) { if (error) { *error = err; } qWarning() << "Couldn't start service" << dbusService << "for" << serviceType << ":" << err; return -2; } } //qDebug() << "DBus service is available now, as" << dbusService; if (pDBusService) { *pDBusService = dbusService; } return 0; } int KDBusServiceStarter::startServiceFor(const QString &serviceType, const QString &constraint, QString *error, QString *dbusService, int /*flags*/) { const KService::List offers = KServiceTypeTrader::self()->query(serviceType, constraint); if (offers.isEmpty()) { return -1; } KService::Ptr ptr = offers.first(); //qDebug() << "starting" << ptr->entryPath(); return KToolInvocation::startServiceByDesktopPath(ptr->entryPath(), QStringList(), error, dbusService); } diff --git a/src/plugin/kdbusservicestarter.h b/src/plugin/kdbusservicestarter.h index fcd70f7..f65aa50 100644 --- a/src/plugin/kdbusservicestarter.h +++ b/src/plugin/kdbusservicestarter.h @@ -1,100 +1,100 @@ /* This file is part of the KDE libraries Copyright (C) 2003 David Faure This library is free software; you can redistribute it and/or modify it under the terms of the GNU Library General Public License version 2 as published by the Free Software Foundation. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Library General Public License for more details. You should have received a copy of the GNU Library General Public License along with this library; see the file COPYING.LIB. If not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #ifndef KDBUSSERVICESTARTER_H #define KDBUSSERVICESTARTER_H #include #include class KDBusServiceStarterPrivate; /** * A generic DBUS service starter, using KServiceTypeTrader. * The default implementation starts new processes, but this interface can * also be reimplemented by specific applications to provide dlopened in-process DBus objects. * This interface is similar to the startServiceByName() function found in QDBusBusService, but * with the added benefit of using KTrader (and, therefore, additional constraints and the * ability to search the standard KDE dirs). * @author David Faure */ class KSERVICE_EXPORT KDBusServiceStarter //krazy:exclude=dpointer (uses K_GLOBAL_STATIC) { public: static KDBusServiceStarter *self(); /** * Check if a given DBus service is available - from the serviceType it's supposed to implement. * * The trader is queried to find the preferred application for this serviceType, * with the constraint that its X-DBus-ServiceName property must be defined. * Then the DBus server is checked. If the service is not available, * this method will call startServiceFor to start it. * * @param serviceType the type of service we're looking for * @param constraint see KServiceTypeTrader * @param error On failure, @p error contains a description of the error * that occurred. If the pointer is 0, the argument will be * ignored * @param dbusService On success, @p dbusService contains the DBus service name * under which this service is available. If the pointer is 0 the argument * will be ignored * @param flags for future extensions (currently unused) * * @return an error code indicating success (== 0) or failure (> 0). */ int findServiceFor(const QString &serviceType, const QString &constraint = QString(), - QString *error = 0, QString *dbusService = 0, + QString *error = nullptr, QString *dbusService = nullptr, int flags = 0); /** * Find an implementation of the given @p serviceType, * and start it, to use its DBus interface. * The default implementation uses KServiceTypeTrader to find the preferred Application, * and then starts it using KToolInvocation::startService... * * However applications (like kontact) can reimplement this method, to provide * an in-process way of loading the implementation for this service type. * * @param serviceType the type of service we're looking for * @param constraint see KServiceTypeTrader * @param error On failure, @p error contains a description of the error * that occurred. If the pointer is 0, the argument will be * ignored * @param dbusService On success, @p dbusService contains the DBus service name * under which this service is available. If the pointer is 0 the argument * will be ignored * @param flags for future extensions (currently unused) * * @return an error code indicating success (== 0) or failure (> 0). */ virtual int startServiceFor(const QString &serviceType, const QString &constraint = QString(), - QString *error = 0, QString *dbusService = 0, + QString *error = nullptr, QString *dbusService = nullptr, int flags = 0); protected: friend class KDBusServiceStarterPrivate; KDBusServiceStarter(); virtual ~KDBusServiceStarter(); }; #endif diff --git a/src/plugin/kplugintrader.cpp b/src/plugin/kplugintrader.cpp index 8c97c22..58c3401 100644 --- a/src/plugin/kplugintrader.cpp +++ b/src/plugin/kplugintrader.cpp @@ -1,102 +1,102 @@ /* This file is part of the KDE libraries Copyright (C) 2000 Torben Weis Copyright (C) 2006 David Faure Copyright 2013 Sebastian Kügler This library is free software; you can redistribute it and/or modify it under the terms of the GNU Library General Public License version 2 as published by the Free Software Foundation. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Library General Public License for more details. You should have received a copy of the GNU Library General Public License along with this library; see the file COPYING.LIB. If not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #include "kplugintrader.h" #include "ktraderparsetree_p.h" #include #include #include #include #include #include #include using namespace KTraderParse; class KPluginTraderSingleton { public: KPluginTrader instance; }; Q_GLOBAL_STATIC(KPluginTraderSingleton, s_globalPluginTrader) KPluginTrader *KPluginTrader::self() { return &s_globalPluginTrader()->instance; } KPluginTrader::KPluginTrader() - : d(0) + : d(nullptr) { } KPluginTrader::~KPluginTrader() { } void KPluginTrader::applyConstraints(KPluginInfo::List &lst, const QString &constraint) { if (lst.isEmpty() || constraint.isEmpty()) { return; } const ParseTreeBase::Ptr constr = parseConstraints(constraint); // for ownership const ParseTreeBase *pConstraintTree = constr.data(); // for speed if (!constr) { // parse error lst.clear(); } else { // Find all plugin information matching the constraint and remove the rest KPluginInfo::List::iterator it = lst.begin(); while (it != lst.end()) { if (matchConstraintPlugin(pConstraintTree, *it, lst) != 1) { it = lst.erase(it); } else { ++it; } } } } KPluginInfo::List KPluginTrader::query(const QString &subDirectory, const QString &servicetype, const QString &constraint) { auto filter = [&](const KPluginMetaData &md) -> bool { const auto &types = md.serviceTypes(); if (!types.isEmpty() && types.contains(servicetype)) { return true; } // handle compatibility JSON: const auto &data = md.rawData(); const auto &jsonTypes = data.value(QStringLiteral("X-KDE-ServiceTypes")).toVariant().toStringList(); if (!jsonTypes.isEmpty() && jsonTypes.contains(servicetype)) { return true; } return data.value(QStringLiteral("ServiceTypes")).toVariant().toStringList().contains(servicetype); }; QVector plugins = servicetype.isEmpty() ? KPluginLoader::findPlugins(subDirectory) : KPluginLoader::findPlugins(subDirectory, filter); KPluginInfo::List lst = KPluginInfo::fromMetaData(plugins); applyConstraints(lst, constraint); return lst; } diff --git a/src/plugin/kplugintrader.h b/src/plugin/kplugintrader.h index f41d398..daddb2e 100644 --- a/src/plugin/kplugintrader.h +++ b/src/plugin/kplugintrader.h @@ -1,268 +1,268 @@ /* This file is part of the KDE libraries Copyright (C) 2000 Torben Weis Copyright (C) 2006 David Faure Copyright 2013 Sebastian Kügler This library is free software; you can redistribute it and/or modify it under the terms of the GNU Library General Public License version 2 as published by the Free Software Foundation. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Library General Public License for more details. You should have received a copy of the GNU Library General Public License along with this library; see the file COPYING.LIB. If not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #ifndef __kplugintrader_h__ #define __kplugintrader_h__ #include "kplugininfo.h" class KPluginTraderPrivate; /** * \class KPluginTrader kplugintrader.h * * A trader interface which provides a way to query specific subdirectories in the Qt * plugin paths for plugins. KPluginTrader provides an easy way to load a plugin * instance from a KPluginFactory, or just querying for existing plugins. * * KPluginTrader provides a way for an application to query directories in the * Qt plugin paths, accessed through QCoreApplication::libraryPaths(). * Plugins may match a specific set of requirements. This allows to find * specific plugins at run-time without having to hard-code their names and/or * paths. KPluginTrader does not search recursively, you are rather encouraged * to install plugins into specific subdirectories to further speed searching. * * KPluginTrader exclusively searches within the plugin binaries' metadata * (via QPluginLoader::metaData()). It does not search these directories recursively. * * KPluginTrader does not use KServiceTypeTrader or KSyCoCa. As such, it will * only find binary plugins. If you are looking for a generic way to query for * services, use KServiceTypeTrader. For anything relating to mimetypes (type * of files), use KMimeTypeTrader. * * \par Example * * If you want to find all plugins for your application, * you would define a KMyApp/Plugin servicetype, and then you can query * the trader for it: * \code * KPluginInfo::List offers = * KPluginTrader::self()->query("KMyApp/Plugin", "kf5"); * \endcode * * You can add a constraint in the "trader query language". For instance: * \code * KPluginTrader::self()->query("KMyApp/Plugin", "kf5", * "[X-KMyApp-InterfaceVersion] > 15"); * \endcode * * Please note that when including property names containing arithmetic operators like - or +, then you have * to put brackets around the property name, in order to correctly separate arithmetic operations from * the name. So for example a constraint expression like * \code * X-KMyApp-InterfaceVersion > 4 // wrong! * \endcode * needs to be written as * \code * [X-KMyApp-InterfaceVersion] > 4 * \endcode * otherwise it could also be interpreted as * Subtract the numeric value of the property "KMyApp" and "InterfaceVersion" from the * property "X" and make sure it is greater than 4.\n * Instead of the other meaning, make sure that the numeric value of "X-KMyApp-InterfaceVersion" is * greater than 4. * * @see KMimeTypeTrader, KServiceTypeTrader, KPluginInfo * @see QCoreApplication::libraryPaths * @see QT_PLUGIN_PATH (env variable) * @see KPluginFactory * @see kservice_desktop_to_json (Cmake macro) * @see K_PLUGIN_FACTORY_WITH_JSON (macro defined in KPluginFactory) * * @since 5.0 */ class KSERVICE_EXPORT KPluginTrader { public: /** * Standard destructor */ ~KPluginTrader(); /** * The main function in the KPluginTrader class. * * It will return a list of plugins that match your specifications. Required parameter is the * service type and subdirectory. This method will append the subDirectory to every path found * in QCoreApplication::libraryPaths(), append the subDirectory parameter, and search through * the plugin's metadata * * KPluginTrader exclusively searches within the plugin binaries' metadata * (via QPluginLoader::metaData()). It does not search these directories recursively. * * The constraint parameter is used to limit the possible choices returned based on the * constraints you give it. * * The @p constraint language is rather full. The most common * keywords are AND, OR, NOT, IN, and EXIST, all used in an * almost spoken-word form. An example is: * \code * (Type == 'Service') and (('KParts/ReadOnlyPart' in ServiceTypes) or (exist Exec)) * \endcode * * If you want to load a list of plugins from a specific subdirectory, you can do the following: * * \code * * KPluginInfo::List plugins = KPluginTrader::self()->query("plasma/engines"); * * foreach (const KPluginInfo &info, plugins) { * KPluginLoader loader(info.libraryPath()); * const QVariantList argsWithMetaData = QVariantList() << loader.metaData().toVariantMap(); * // In many cases, plugins are actually based on KPluginFactory, this is how that works: * KPluginFactory* factory = loader.factory(); * if (factory) { * Engine* component = factory->create(parent, argsWithMetaData); * if (component) { * // Do whatever you want to do with the resulting object * } * } * // Otherwise, just use the normal QPluginLoader methods * Engine *myengine = qobject_cast(loader.instance()); * if (myengine) { * // etc. ... * } * } * \endcode * * If you have a specific query for just one plugin, use the createInstanceFromQuery method. * * The keys used in the query (Type, ServiceType, Exec) are all fields found in the .json files * which are compiled into the plugin binaries. * * @param subDirectory The subdirectory under the Qt plugin path * @param servicetype A service type like 'KMyApp/Plugin' or 'KFilePlugin' * @param constraint A constraint to limit the choices returned, QString() to * get all services of the given @p servicetype * * @return A list of services that satisfy the query * @see http://techbase.kde.org/Development/Tutorials/Services/Traders#The_KTrader_Query_Language */ KPluginInfo::List query(const QString &subDirectory, const QString &serviceType = QString(), const QString &constraint = QString()); /** * This is a static pointer to the KPluginTrader singleton. * * You will need to use this to access the KPluginTrader functionality since the * constructors are protected. * * @return Static KPluginTrader instance */ static KPluginTrader *self(); /** * Get a plugin from a trader query * * Example: * \code * KMyAppPlugin* plugin = KPluginTrader::createInstanceFromQuery(subDirectory, serviceType, QString(), parentObject ); * if ( plugin ) { * .... * } * \endcode * * @param subDirectory The subdirectory under the Qt plugin pathes to search in * @param serviceType The type of service for which to find a plugin * @param constraint An optional constraint to pass to the trader (see KTrader) * @param parent The parent object for the part itself * @param args A list of arguments passed to the service component * @param error The string passed here will contain an error description. * @return A pointer to the newly created object or a null pointer if the * factory was unable to create an object of the given type. */ template static T *createInstanceFromQuery(const QString &subDirectory, const QString &serviceType = QString(), const QString &constraint = QString(), - QObject *parent = 0, + QObject *parent = nullptr, const QVariantList &args = QVariantList(), - QString *error = 0) + QString *error = nullptr) { - return createInstanceFromQuery(subDirectory, serviceType, constraint, parent, 0, args, error); + return createInstanceFromQuery(subDirectory, serviceType, constraint, parent, nullptr, args, error); } /** * Get a plugin from a trader query * * This method works like * createInstanceFromQuery(const QString&, const QString& ,const QString&, QObject*, * const QVariantList&, QString*), * but you can specify an additional parent widget. This is important for a KPart, for example. * * @param subDirectory The subdirectory under the Qt plugin pathes to search in * @param serviceType the type of service for which to find a plugin * @param constraint an optional constraint to pass to the trader (see KTrader) * @param parent the parent object for the part itself * @param parentWidget the parent widget for the plugin * @param args A list of arguments passed to the service component * @param error The string passed here will contain an error description. * @return A pointer to the newly created object or a null pointer if the * factory was unable to create an object of the given type. */ template static T *createInstanceFromQuery(const QString &subDirectory, const QString &serviceType, const QString &constraint, QObject *parent, QWidget *parentWidget, const QVariantList &args = QVariantList(), - QString *error = 0) + QString *error = nullptr) { Q_UNUSED(parentWidget) Q_UNUSED(args) if (error) { error->clear(); } const KPluginInfo::List offers = self()->query(subDirectory, serviceType, constraint); Q_FOREACH (const KPluginInfo &info, offers) { KPluginLoader loader(info.libraryPath()); const QVariantList argsWithMetaData = QVariantList() << loader.metaData().toVariantMap(); KPluginFactory *factory = loader.factory(); if (factory) { T *component = factory->create(parent, argsWithMetaData); if (component) { return component; } } } if (error && error->isEmpty()) { *error = QCoreApplication::translate("", "No service matching the requirements was found"); } - return 0; + return nullptr; } static void applyConstraints(KPluginInfo::List &lst, const QString &constraint); private: /** * @internal */ KPluginTrader(); // disallow copy ctor and assignment operator KPluginTrader(const KPluginTrader &other); KPluginTrader &operator=(const KPluginTrader &rhs); KPluginTraderPrivate *const d; friend class KPluginTraderSingleton; }; #endif diff --git a/src/services/kautostart.cpp b/src/services/kautostart.cpp index 17eeca8..97be2a0 100644 --- a/src/services/kautostart.cpp +++ b/src/services/kautostart.cpp @@ -1,356 +1,356 @@ /* This file is part of the KDE libraries Copyright (C) 2006 Aaron Seigo This library is free software; you can redistribute it and/or modify it under the terms of the GNU Library General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Library General Public License for more details. You should have received a copy of the GNU Library General Public License along with this library; see the file COPYING.LIB. If not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #include "kautostart.h" #include "kdesktopfile.h" #include "kconfiggroup.h" #include #include #include class KAutostartPrivate { public: KAutostartPrivate() - : df(0), + : df(nullptr), copyIfNeededChecked(false) { } ~KAutostartPrivate() { delete df; } void copyIfNeeded(); QString name; KDesktopFile *df; bool copyIfNeededChecked; }; void KAutostartPrivate::copyIfNeeded() { if (copyIfNeededChecked) { return; } const QString local = QStandardPaths::writableLocation(QStandardPaths::GenericConfigLocation) + QLatin1String("/autostart/") + name; if (!QFile::exists(local)) { const QString global = QStandardPaths::locate(QStandardPaths::GenericConfigLocation, QLatin1String("autostart/") + name); if (!global.isEmpty()) { KDesktopFile *newDf = df->copyTo(local); delete df; delete newDf; //Force sync-to-disk df = new KDesktopFile(QStandardPaths::GenericConfigLocation, QStringLiteral("autostart/") + name); //Recreate from disk } } copyIfNeededChecked = true; } KAutostart::KAutostart(const QString &entryName, QObject *parent) : QObject(parent), d(new KAutostartPrivate) { if (entryName.isEmpty()) { d->name = QCoreApplication::applicationName(); } else { d->name = entryName; } if (!d->name.endsWith(QLatin1String(".desktop"))) { d->name.append(QStringLiteral(".desktop")); } const QString path = QStandardPaths::locate(QStandardPaths::GenericConfigLocation, QStringLiteral("autostart/") + d->name); if (path.isEmpty()) { // just a new KDesktopFile, since we have nothing to use d->df = new KDesktopFile(QStandardPaths::GenericConfigLocation, QStringLiteral("autostart/") + d->name); d->copyIfNeededChecked = true; } else { d->df = new KDesktopFile(path); } } KAutostart::~KAutostart() { delete d; } void KAutostart::setAutostarts(bool autostart) { bool currentAutostartState = !d->df->desktopGroup().readEntry("Hidden", false); if (currentAutostartState == autostart) { return; } d->copyIfNeeded(); d->df->desktopGroup().writeEntry("Hidden", !autostart); } bool KAutostart::autostarts(const QString &environment, Conditions check) const { // check if this is actually a .desktop file bool starts = d->df->desktopGroup().exists(); // check the hidden field starts = starts && !d->df->desktopGroup().readEntry("Hidden", false); if (!environment.isEmpty()) { starts = starts && checkAllowedEnvironment(environment); } if (check & CheckCommand) { starts = starts && d->df->tryExec(); } if (check & CheckCondition) { starts = starts && checkStartCondition(); } return starts; } bool KAutostart::checkStartCondition() const { QString condition = d->df->desktopGroup().readEntry("X-KDE-autostart-condition"); if (condition.isEmpty()) { return true; } const QStringList list = condition.split(QLatin1Char(':')); if (list.count() < 4) { return true; } if (list[0].isEmpty() || list[2].isEmpty()) { return true; } KConfig config(list[0], KConfig::NoGlobals); KConfigGroup cg(&config, list[1]); const bool defaultValue = (list[3].toLower() == QLatin1String("true")); return cg.readEntry(list[2], defaultValue); } bool KAutostart::checkAllowedEnvironment(const QString &environment) const { const QStringList allowed = allowedEnvironments(); if (!allowed.isEmpty()) { return allowed.contains(environment); } const QStringList excluded = excludedEnvironments(); if (!excluded.isEmpty()) { return !excluded.contains(environment); } return true; } QString KAutostart::command() const { return d->df->desktopGroup().readEntry("Exec", QString()); } void KAutostart::setCommand(const QString &command) { if (d->df->desktopGroup().readEntry("Exec", QString()) == command) { return; } d->copyIfNeeded(); d->df->desktopGroup().writeEntry("Exec", command); } QString KAutostart::visibleName() const { return d->df->readName(); } void KAutostart::setVisibleName(const QString &name) { if (d->df->desktopGroup().readEntry("Name", QString()) == name) { return; } d->copyIfNeeded(); d->df->desktopGroup().writeEntry("Name", name); } bool KAutostart::isServiceRegistered(const QString &entryName) { const QString localDir = QStandardPaths::writableLocation(QStandardPaths::GenericConfigLocation) + QLatin1String("/autostart/"); return QFile::exists(localDir + entryName + QStringLiteral(".desktop")); } QString KAutostart::commandToCheck() const { return d->df->desktopGroup().readPathEntry("TryExec", QString()); } void KAutostart::setCommandToCheck(const QString &exec) { if (d->df->desktopGroup().readEntry("TryExec", QString()) == exec) { return; } d->copyIfNeeded(); d->df->desktopGroup().writePathEntry("TryExec", exec); } // do not specialize the readEntry template - // http://connect.microsoft.com/VisualStudio/feedback/ViewFeedback.aspx?FeedbackID=100911 static KAutostart::StartPhase readEntry(const KConfigGroup &group, const char *key, const KAutostart::StartPhase &aDefault) { const QByteArray data = group.readEntry(key, QByteArray()); if (data.isNull()) { return aDefault; } if (data == "0" || data == "BaseDesktop") { return KAutostart::BaseDesktop; } else if (data == "1" || data == "DesktopServices") { return KAutostart::DesktopServices; } else if (data == "2" || data == "Applications") { return KAutostart::Applications; } return aDefault; } KAutostart::StartPhase KAutostart::startPhase() const { return readEntry(d->df->desktopGroup(), "X-KDE-autostart-phase", Applications); } void KAutostart::setStartPhase(KAutostart::StartPhase phase) { QString data = QStringLiteral("Applications"); switch (phase) { case BaseDesktop: data = QStringLiteral("BaseDesktop"); break; case DesktopServices: data = QStringLiteral("DesktopServices"); break; case Applications: // This is the default break; } if (d->df->desktopGroup().readEntry("X-KDE-autostart-phase", QString()) == data) { return; } d->copyIfNeeded(); d->df->desktopGroup().writeEntry("X-KDE-autostart-phase", data); } QStringList KAutostart::allowedEnvironments() const { return d->df->desktopGroup().readXdgListEntry("OnlyShowIn"); } void KAutostart::setAllowedEnvironments(const QStringList &environments) { if (d->df->desktopGroup().readEntry("OnlyShowIn", QStringList()) == environments) { return; } d->copyIfNeeded(); d->df->desktopGroup().writeXdgListEntry("OnlyShowIn", environments); } void KAutostart::addToAllowedEnvironments(const QString &environment) { QStringList envs = allowedEnvironments(); if (envs.contains(environment)) { return; } envs.append(environment); setAllowedEnvironments(envs); } void KAutostart::removeFromAllowedEnvironments(const QString &environment) { QStringList envs = allowedEnvironments(); int index = envs.indexOf(environment); if (index < 0) { return; } envs.removeAt(index); setAllowedEnvironments(envs); } QStringList KAutostart::excludedEnvironments() const { return d->df->desktopGroup().readXdgListEntry("NotShowIn"); } void KAutostart::setExcludedEnvironments(const QStringList &environments) { if (d->df->desktopGroup().readEntry("NotShowIn", QStringList()) == environments) { return; } d->copyIfNeeded(); d->df->desktopGroup().writeXdgListEntry("NotShowIn", environments); } void KAutostart::addToExcludedEnvironments(const QString &environment) { QStringList envs = excludedEnvironments(); if (envs.contains(environment)) { return; } envs.append(environment); setExcludedEnvironments(envs); } void KAutostart::removeFromExcludedEnvironments(const QString &environment) { QStringList envs = excludedEnvironments(); int index = envs.indexOf(environment); if (index < 0) { return; } envs.removeAt(index); setExcludedEnvironments(envs); } QString KAutostart::startAfter() const { return d->df->desktopGroup().readEntry("X-KDE-autostart-after"); } diff --git a/src/services/kautostart.h b/src/services/kautostart.h index 3d48554..d92a3d3 100644 --- a/src/services/kautostart.h +++ b/src/services/kautostart.h @@ -1,286 +1,286 @@ /* This file is part of the KDE libraries Copyright (C) 2006 Aaron Seigo This library is free software; you can redistribute it and/or modify it under the terms of the GNU Library General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Library General Public License for more details. You should have received a copy of the GNU Library General Public License along with this library; see the file COPYING.LIB. If not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #ifndef KDELIBS_KAUTOSTART_H #define KDELIBS_KAUTOSTART_H #include #include class KAutostartPrivate; class QStringList; /** * KAutostart provides a programmatic means to control the state of * autostart services on a per-user basis. This is useful for applications * that wish to offer a configurable means to allow the application to be * autostarted. * * By using this class you future-proof your applications against potential * future or platform-specific changes to the autostart mechanism(s). * * Typical usage might look like: * * @code * KAutostart autostart; // without an entryName arg, gets name from KAboutData * autostart.setAutostarts(true); // will now start up when the user logs in * * // set the value in our configuration settings to reflect whether or not * // we will actually start up on log in * config.setAutoStart(autostart.autoStarts()); * @endcode */ class KSERVICE_EXPORT KAutostart : public QObject { Q_OBJECT public: /** * Creates a new KAutostart object that represents the autostart * service "entryName". If the service already exists in the system * then the values associated with that service, such as the executable * command, will be loaded as well. * * Note that unless this service is explicitly set to autostart, * simply creating a KAutostart object will not result in the * service being autostarted on next log in. * * If no such service is already registered and the command to be * executed on startup is not the same as entryName, then you will want * to set the associated command with setExec(const QString&) * @see setExec * @param entryName the name used to identify the service. If none is * provided then it uses the name registered with KAboutData. * @param parent QObject */ explicit KAutostart(const QString &entryName = QString(), - QObject *parent = 0); + QObject *parent = nullptr); ~KAutostart(); /** * Flags for each of the conditions that may affect whether or not * a service actually autostarted on login */ enum Condition { NoConditions = 0x0, /** * an executable that is checked for existence by name */ CheckCommand = 0x1, /** * autostart condition will be checked too (KDE-specific) * @since 4.3 */ CheckCondition = 0x2, /** * all necessary conditions will be checked * @since 4.3 */ CheckAll = 0xff }; Q_DECLARE_FLAGS(Conditions, Condition) /** * Enumerates the various autostart phases that occur during start-up. */ enum StartPhase { /** * the essential desktop services such as panels and window managers */ BaseDesktop = 0, /** * services that should be available before most interactive * applications start but that aren't part of the base desktop. * This would include things such as clipboard managers and * mouse gesture tools. */ DesktopServices = 1, /** * everything else that doesn't belong in the above two categories, * including most system tray applications, system monitors and * interactive applications */ Applications = 2 }; /** * Sets the given exec to start automatically at login * @param autostart will register with the autostart facility when true * and deregister when false * @see autostarts() */ void setAutostarts(bool autostart); /** * Returns whether or not the service represented by entryName in the * autostart system is set to autostart at login or not * @param environment if provided the check will be performed as if * being loaded in that environment * @param check autostart conditions to check for (see commandToCheck()) * @see setAutostarts() */ bool autostarts(const QString &environment = QString(), Conditions check = NoConditions) const; /** * Returns the associated command for this autostart service * @see setCommand() */ QString command() const; /** * Set the associated command for this autostart service * @see command() */ void setCommand(const QString &command); /** * Returns the user-visible name this autostart service is registered as * @see setVisibleName(), setEntryName() */ QString visibleName() const; /** * Sets the user-visible name for this autostart service. * @see visibleName() */ void setVisibleName(const QString &entryName); /** * Checks whether or not a service by the given name @p entryName is registered * with the autostart system. Does not check whether or not it is * set to actually autostart or not. * @param entryName the name of the service to check for */ static bool isServiceRegistered(const QString &entryName); /** * Returns the executable to check for when attempting to autostart * this service. If the executable is not found in the user's * environment, it will not autostart. * @see setCommandToCheck() */ QString commandToCheck() const; /** * Sets the executable to check for the existence of when * autostarting this service * @see commandToCheck() */ void setCommandToCheck(const QString &exec); /** * Returns the autostart phase this service is started in. * * Note that this is KDE specific and may not work in other * environments. * * @see StartPhase, setStartPhase() */ StartPhase startPhase() const; /** * Sets the service (by name) this service should be started after. * * Note that this is KDE specific and may not work in other * environments. * * @see StartPhase, startPhase() */ void setStartPhase(StartPhase phase); /** * Returns the list of environments (e.g. "KDE") this service is allowed * to start in. Use checkAllowedEnvironment() or autostarts() for actual * checks. * * This does not take other autostart conditions * into account. If any environment is added to the allowed environments * list, then only those environments will be allowed to * autoload the service. It is not allowed to specify both allowed and excluded * environments at the same time. * @see setAllowedEnvironments() */ QStringList allowedEnvironments() const; /** * Sets the environments this service is allowed to start in * @see allowedEnvironments(), addToAllowedEnvironments() */ void setAllowedEnvironments(const QStringList &environments); /** * Adds an environment to the list of environments this service may * start in. * @see setAllowedEnvironments(), removeFromAllowedEnvironments() */ void addToAllowedEnvironments(const QString &environment); /** * Removes an environment to the list of environments this service may * start in. * @see addToAllowedEnvironments() */ void removeFromAllowedEnvironments(const QString &environment); /** * Returns the list of environments this service is explicitly not * allowed to start in. Use checkAllowedEnvironment() or autostarts() for actual * checks. * * This does not take other autostart conditions * such as into account. It is not allowed to specify both allowed and excluded * environments at the same time. * @see setExcludedEnvironments() */ QStringList excludedEnvironments() const; /** * Sets the environments this service is not allowed to start in * @see excludedEnvironments(), addToExcludedEnvironments() */ void setExcludedEnvironments(const QStringList &environments); /** * Adds an environment to the list of environments this service may * not be autostarted in * @see removeFromExcludedEnvironments() */ void addToExcludedEnvironments(const QString &environment); /** * Removes an environment to the list of environments this service may * not be autostarted in * @see addToExcludedEnvironments() */ void removeFromExcludedEnvironments(const QString &environment); /** * Returns the name of another service that should be autostarted * before this one (if that service would be autostarted). * @internal * @since 4.3 */ QString startAfter() const; /** * Checks whether autostart is allowed in the given environment, * depending on allowedEnvironments() and excludedEnvironments(). * @since 4.3 */ bool checkAllowedEnvironment(const QString &environment) const; private: bool checkStartCondition() const; KAutostartPrivate *const d; }; Q_DECLARE_OPERATORS_FOR_FLAGS(KAutostart::Conditions) #endif diff --git a/src/services/kmimetypefactory.cpp b/src/services/kmimetypefactory.cpp index aeec4df..40a8590 100644 --- a/src/services/kmimetypefactory.cpp +++ b/src/services/kmimetypefactory.cpp @@ -1,171 +1,171 @@ /* This file is part of the KDE libraries * Copyright (C) 1999 Waldo Bastian * Copyright (C) 2006-2009 David Faure * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Library General Public * License version 2 as published by the Free Software Foundation; * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Library General Public License for more details. * * You should have received a copy of the GNU Library General Public License * along with this library; see the file COPYING.LIB. If not, write to * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, * Boston, MA 02110-1301, USA. */ #include "kmimetypefactory_p.h" #include #include #include extern int servicesDebugArea(); KMimeTypeFactory::KMimeTypeFactory(KSycoca *db) : KSycocaFactory(KST_KMimeTypeFactory, db) { } KMimeTypeFactory::~KMimeTypeFactory() { } int KMimeTypeFactory::entryOffset(const QString &mimeTypeName) { if (!sycocaDict()) { return -1; // Error! } assert(!sycoca()->isBuilding()); const int offset = sycocaDict()->find_string(mimeTypeName.toLower()); return offset; } int KMimeTypeFactory::serviceOffersOffset(const QString &mimeTypeName) { const int offset = entryOffset(mimeTypeName.toLower()); if (!offset) { return -1; // Not found } MimeTypeEntry::Ptr newMimeType(createEntry(offset)); if (!newMimeType) { return -1; } // Check whether the dictionary was right. if (newMimeType->name() != mimeTypeName.toLower()) { // No it wasn't... return -1; } return newMimeType->serviceOffersOffset(); } KMimeTypeFactory::MimeTypeEntry *KMimeTypeFactory::createEntry(int offset) const { KSycocaType type; QDataStream *str = sycoca()->findEntry(offset, type); if (!str) { - return 0; + return nullptr; } if (type != KST_KMimeTypeEntry) { qWarning() << "KMimeTypeFactory: unexpected object entry in KSycoca database (type=" << int(type) << ")"; - return 0; + return nullptr; } MimeTypeEntry *newEntry = new MimeTypeEntry(*str, offset); if (newEntry && !newEntry->isValid()) { qWarning() << "KMimeTypeFactory: corrupt object in KSycoca database!\n"; delete newEntry; - newEntry = 0; + newEntry = nullptr; } return newEntry; } QStringList KMimeTypeFactory::allMimeTypes() { // TODO: reimplement in terms of "listing xdgdata-mime", to avoid ksycoca dependency, // then move to KMimeTypeRepository QStringList result; const KSycocaEntry::List list = allEntries(); for (KSycocaEntry::List::ConstIterator it = list.begin(); it != list.end(); ++it) { Q_ASSERT((*it)->isType(KST_KMimeTypeEntry)); MimeTypeEntry::Ptr mimeType(static_cast((*it).data())); result.append(mimeType->name()); } return result; } KMimeTypeFactory::MimeTypeEntry::Ptr KMimeTypeFactory::findMimeTypeEntryByName(const QString &name) { Q_ASSERT(sycoca()->isBuilding()); // We're building a database - the mimetype entry must be in memory KSycocaEntry::Ptr servType = m_entryDict->value(name.toLower()); return MimeTypeEntry::Ptr(static_cast(servType.data())); } QStringList KMimeTypeFactory::resourceDirs() { return KSycocaFactory::allDirectories(QStringLiteral("mime")); } //// class KMimeTypeFactory::MimeTypeEntryPrivate : public KSycocaEntryPrivate { public: K_SYCOCATYPE(KST_KMimeTypeEntry, KSycocaEntryPrivate) MimeTypeEntryPrivate(const QString &file, const QString &name) : KSycocaEntryPrivate(file), m_name(name), m_serviceOffersOffset(-1) { } MimeTypeEntryPrivate(QDataStream &s, int offset) : KSycocaEntryPrivate(s, offset), m_serviceOffersOffset(-1) { s >> m_name >> m_serviceOffersOffset; } QString name() const Q_DECL_OVERRIDE { return m_name; } void save(QDataStream &s) Q_DECL_OVERRIDE; QString m_name; int m_serviceOffersOffset; }; void KMimeTypeFactory::MimeTypeEntryPrivate::save(QDataStream &s) { KSycocaEntryPrivate::save(s); s << m_name << m_serviceOffersOffset; } //// KMimeTypeFactory::MimeTypeEntry::MimeTypeEntry(const QString &file, const QString &name) : KSycocaEntry(*new MimeTypeEntryPrivate(file, name.toLower())) { } KMimeTypeFactory::MimeTypeEntry::MimeTypeEntry(QDataStream &s, int offset) : KSycocaEntry(*new MimeTypeEntryPrivate(s, offset)) { } KMimeTypeFactory::MimeTypeEntry::~MimeTypeEntry() {} int KMimeTypeFactory::MimeTypeEntry::serviceOffersOffset() const { Q_D(const MimeTypeEntry); return d->m_serviceOffersOffset; } void KMimeTypeFactory::MimeTypeEntry::setServiceOffersOffset(int off) { Q_D(MimeTypeEntry); d->m_serviceOffersOffset = off; } diff --git a/src/services/kmimetypefactory_p.h b/src/services/kmimetypefactory_p.h index fe8ae6a..2bd46a0 100644 --- a/src/services/kmimetypefactory_p.h +++ b/src/services/kmimetypefactory_p.h @@ -1,112 +1,112 @@ /* This file is part of the KDE libraries * Copyright (C) 1999 Waldo Bastian * Copyright (C) 2006-2007 David Faure * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Library General Public * License version 2 as published by the Free Software Foundation; * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Library General Public License for more details. * * You should have received a copy of the GNU Library General Public License * along with this library; see the file COPYING.LIB. If not, write to * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, * Boston, MA 02110-1301, USA. */ #ifndef KMIMETYPEFACTORY_H #define KMIMETYPEFACTORY_H #include #include #include "ksycocafactory_p.h" #include "ksycocaentry_p.h" class KSycoca; /** * @internal - this header is not installed * * A sycoca factory for mimetype entries * This is only used to point to the "service offers" in ksycoca for each mimetype. * @see KMimeType */ class KMimeTypeFactory : public KSycocaFactory { K_SYCOCAFACTORY(KST_KMimeTypeFactory) public: /** * Create factory */ KMimeTypeFactory(KSycoca *db); virtual ~KMimeTypeFactory(); /** * Not meant to be called at this level */ KSycocaEntry *createEntry(const QString &) const Q_DECL_OVERRIDE { assert(0); - return 0; + return nullptr; } /** * Returns the possible offset for a given mimetype entry. */ int entryOffset(const QString &mimeTypeName); /** * Returns the offset into the service offers for a given mimetype. */ int serviceOffersOffset(const QString &mimeTypeName); /** * Returns the directories to watch for this factory. */ static QStringList resourceDirs(); public: /** * @return all mimetypes * Slow and memory consuming, avoid using */ QStringList allMimeTypes(); /** * @return the unique mimetype factory, creating it if necessary */ static KMimeTypeFactory *self(); public: // public for KBuildServiceFactory // A small entry for each mimetype with name and offset into the services-offer-list. class MimeTypeEntryPrivate; class KSERVICE_EXPORT MimeTypeEntry : public KSycocaEntry { Q_DECLARE_PRIVATE(MimeTypeEntry) public: typedef QExplicitlySharedDataPointer Ptr; MimeTypeEntry(const QString &file, const QString &name); MimeTypeEntry(QDataStream &s, int offset); ~MimeTypeEntry(); int serviceOffersOffset() const; void setServiceOffersOffset(int off); }; MimeTypeEntry::Ptr findMimeTypeEntryByName(const QString &name); protected: MimeTypeEntry *createEntry(int offset) const Q_DECL_OVERRIDE; private: // d pointer: useless since this header is not installed //class KMimeTypeFactoryPrivate* d; }; #endif diff --git a/src/services/kmimetypetrader.h b/src/services/kmimetypetrader.h index 15f766e..551cddf 100644 --- a/src/services/kmimetypetrader.h +++ b/src/services/kmimetypetrader.h @@ -1,203 +1,203 @@ /* This file is part of the KDE libraries Copyright (C) 2000 Torben Weis Copyright (C) 2006 David Faure This library is free software; you can redistribute it and/or modify it under the terms of the GNU Library General Public License version 2 as published by the Free Software Foundation. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Library General Public License for more details. You should have received a copy of the GNU Library General Public License along with this library; see the file COPYING.LIB. If not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #ifndef KMIMETYPETRADER_H #define KMIMETYPETRADER_H #include class KMimeTypeTraderPrivate; class KServiceOffer; typedef QList KServiceOfferList; /** * KDE's trader for services associated to a given mimetype. * * Example: say that you want to the list of all KParts components that can handle HTML. * Our code would look like: * \code * KService::List lst = KMimeTypeTrader::self()->query("text/html", * "KParts/ReadOnlyPart"); * \endcode * * If you want to get the preferred KParts component for text/html you could use * preferredService("text/html", "KParts/ReadOnlyPart"), although if this is about * loading that component you would use createPartInstanceFromQuery directly. * * @see KServiceTypeTrader, KService */ class KSERVICE_EXPORT KMimeTypeTrader { public: /** * Standard destructor */ ~KMimeTypeTrader(); /** * This method returns a list of services which are associated with a given mimetype. * * Example usage: * To get list of applications that can handle a given mimetype, * set @p genericServiceType to "Application" (which is the default). * To get list of embeddable components that can handle a given mimetype, * set @p genericServiceType to "KParts/ReadOnlyPart". * * The constraint parameter is used to limit the possible choices * returned based on the constraints you give it. * * The @p constraint language is rather full. The most common * keywords are AND, OR, NOT, IN, and EXIST, all used in an * almost spoken-word form. An example is: * \code * (Type == 'Service') and (('Browser/View' in ServiceTypes) and (exist Library)) * \endcode * * The keys used in the query (Type, ServiceTypes, Library) are all * fields found in the .desktop files. * * @param mimeType A mime type like 'text/plain' or 'text/html'. * @param genericServiceType a basic service type, like 'KParts/ReadOnlyPart' or 'Application' * @param constraint A constraint to limit the choices returned, QString() to * get all services that can handle the given @p mimetype * * @return A list of services that satisfy the query, sorted by preference * (preferred service first) * @see http://techbase.kde.org/Development/Tutorials/Services/Traders#The_KTrader_Query_Language */ KService::List query(const QString &mimeType, const QString &genericServiceType = QStringLiteral("Application"), const QString &constraint = QString()) const; /** * Returns the preferred service for @p mimeType and @p genericServiceType * * This is almost like query().first(), except that it also checks * if the service is allowed as a preferred service (see KService::allowAsDefault). * * @param mimeType the mime type (see query()) * @param genericServiceType the service type (see query()) * @return the preferred service, or 0 if no service is available */ KService::Ptr preferredService(const QString &mimeType, const QString &genericServiceType = QStringLiteral("Application")); /** * This method creates and returns a part object from the trader query for a given \p mimeType. * * Example: * \code * KParts::ReadOnlyPart* part = KMimeTypeTrader::createInstanceFromQuery("text/plain", parentWidget, parentObject); * if (part) { * part->openUrl(url); * part->widget()->show(); // also insert the widget into a layout * } * \endcode * * @param mimeType the mimetype which this part is associated with * @param parentWidget the parent widget, will be set as the parent of the part's widget * @param parent the parent object for the part itself * @param constraint an optional constraint to pass to the trader * @param args A list of arguments passed to the service component * @param error The string passed here will contain an error description. * @return A pointer to the newly created object or a null pointer if the * factory was unable to create an object of the given type. */ template - static T *createPartInstanceFromQuery(const QString &mimeType, QWidget *parentWidget = 0, QObject *parent = 0, + static T *createPartInstanceFromQuery(const QString &mimeType, QWidget *parentWidget = nullptr, QObject *parent = nullptr, const QString &constraint = QString(), const QVariantList &args = QVariantList(), - QString *error = 0) + QString *error = nullptr) { const KService::List offers = self()->query(mimeType, QStringLiteral("KParts/ReadOnlyPart"), constraint); Q_FOREACH (const KService::Ptr &ptr, offers) { T *component = ptr->template createInstance(parentWidget, parent, args, error); if (component) { if (error) { error->clear(); } return component; } } if (error) { *error = QCoreApplication::translate("", "No service matching the requirements was found"); } return 0; } /** * This can be used to create a service instance from a mime type query * * @param mimeType A mime type like 'text/plain' or 'text/html'. * @param serviceType a basic service type * @param parent the parent object for the plugin itself * @param constraint A constraint to limit the choices returned, QString() to * get all services that can handle the given @p mimetype * @param args A list of arguments passed to the service component * @param error The string passed here will contain an error description. * @return A pointer to the newly created object or a null pointer if the * factory was unable to create an object of the given type. */ template - static T *createInstanceFromQuery(const QString &mimeType, const QString &serviceType, QObject *parent = 0, + static T *createInstanceFromQuery(const QString &mimeType, const QString &serviceType, QObject *parent = nullptr, const QString &constraint = QString(), const QVariantList &args = QVariantList(), - QString *error = 0) + QString *error = nullptr) { const KService::List offers = self()->query(mimeType, serviceType, constraint); Q_FOREACH (const KService::Ptr &ptr, offers) { T *component = ptr->template createInstance(parent, args, error); if (component) { if (error) { error->clear(); } return component; } } if (error) { *error = QCoreApplication::translate("", "No service matching the requirements was found"); } return 0; } /** * This is a static pointer to the KMimeTypeTrader singleton. * * You will need to use this to access the KMimeTypeTrader functionality since the * constructors are protected. * * @return Static KMimeTypeTrader instance */ static KMimeTypeTrader *self(); private: /** * @internal */ KMimeTypeTrader(); private: KMimeTypeTraderPrivate *const d; // class-static so that it can access KSycocaEntry::offset() static void filterMimeTypeOffers(KServiceOfferList &list, const QString &genericServiceType); static void filterMimeTypeOffers(KService::List &list, const QString &genericServiceType); friend class KMimeTypeTraderSingleton; }; #endif /* KMIMETYPETRADER_H */ diff --git a/src/services/kplugininfo.cpp b/src/services/kplugininfo.cpp index 72b21f9..5c9dc2c 100644 --- a/src/services/kplugininfo.cpp +++ b/src/services/kplugininfo.cpp @@ -1,769 +1,769 @@ /* This file is part of the KDE project Copyright (C) 2003,2007 Matthias Kretz Copyright 2013 Sebastian Kügler This library is free software; you can redistribute it and/or modify it under the terms of the GNU Library General Public License version 2 as published by the Free Software Foundation. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Library General Public License for more details. You should have received a copy of the GNU Library General Public License along with this library; see the file COPYING.LIB. If not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #include "kplugininfo.h" #include #include #include #include #include #include #include "ksycoca.h" #include "ksycoca_p.h" #include #include #include #include #include #include #include //#ifndef NDEBUG #define KPLUGININFO_ISVALID_ASSERTION \ do { \ if (!d) { \ qFatal("Accessed invalid KPluginInfo object"); \ } \ } while (false) //#else //#define KPLUGININFO_ISVALID_ASSERTION //#endif #define GlobalQStringLiteral(name, str) inline QString name() { return QStringLiteral(str); } namespace { GlobalQStringLiteral(s_hiddenKey, "Hidden") GlobalQStringLiteral(s_nameKey, "Name") GlobalQStringLiteral(s_commentKey, "Comment") GlobalQStringLiteral(s_iconKey, "Icon") GlobalQStringLiteral(s_libraryKey, "X-KDE-Library") GlobalQStringLiteral(s_authorKey, "X-KDE-PluginInfo-Author") GlobalQStringLiteral(s_emailKey, "X-KDE-PluginInfo-Email") GlobalQStringLiteral(s_pluginNameKey, "X-KDE-PluginInfo-Name") GlobalQStringLiteral(s_versionKey, "X-KDE-PluginInfo-Version") GlobalQStringLiteral(s_websiteKey, "X-KDE-PluginInfo-Website") GlobalQStringLiteral(s_categoryKey, "X-KDE-PluginInfo-Category") GlobalQStringLiteral(s_licenseKey, "X-KDE-PluginInfo-License") GlobalQStringLiteral(s_dependenciesKey, "X-KDE-PluginInfo-Depends") GlobalQStringLiteral(s_serviceTypesKey, "ServiceTypes") GlobalQStringLiteral(s_xKDEServiceTypes, "X-KDE-ServiceTypes") GlobalQStringLiteral(s_mimeTypeKey, "MimeType") GlobalQStringLiteral(s_formFactorsKey, "X-KDE-FormFactors") GlobalQStringLiteral(s_enabledbyDefaultKey, "X-KDE-PluginInfo-EnabledByDefault") GlobalQStringLiteral(s_enabledKey, "Enabled") // these keys are used in the json metadata GlobalQStringLiteral(s_jsonDescriptionKey, "Description") GlobalQStringLiteral(s_jsonAuthorsKey, "Authors") GlobalQStringLiteral(s_jsonEmailKey, "Email") GlobalQStringLiteral(s_jsonCategoryKey, "Category") GlobalQStringLiteral(s_jsonDependenciesKey, "Dependencies") GlobalQStringLiteral(s_jsonEnabledByDefaultKey, "EnabledByDefault") GlobalQStringLiteral(s_jsonFormFactorsKey, "FormFactors") GlobalQStringLiteral(s_jsonLicenseKey, "License") GlobalQStringLiteral(s_jsonIdKey, "Id") GlobalQStringLiteral(s_jsonVersionKey, "Version") GlobalQStringLiteral(s_jsonWebsiteKey, "Website") GlobalQStringLiteral(s_jsonMimeTypesKey, "MimeTypes") GlobalQStringLiteral(s_jsonKPluginKey, "KPlugin") } class KPluginInfoPrivate : public QSharedData { public: KPluginInfoPrivate() : hidden(false) , pluginenabled(false) , kcmservicesCached(false) {} static QStringList deserializeList(const QString &data); bool hidden : 1; bool pluginenabled : 1; mutable bool kcmservicesCached : 1; KPluginMetaData metaData; KConfigGroup config; KService::Ptr service; mutable QList kcmservices; /** assigns the @p md to @c metaData, but also ensures that compatibility values are handled */ void setMetaData(const KPluginMetaData &md, bool warnOnOldStyle); }; //This comes from KConfigGroupPrivate::deserializeList() QStringList KPluginInfoPrivate::deserializeList(const QString &data) { if (data.isEmpty()) { return QStringList(); } if (data == QLatin1String("\\0")) { return QStringList(QString()); } QStringList value; QString val; val.reserve(data.size()); bool quoted = false; for (int p = 0; p < data.length(); p++) { if (quoted) { val += data[p]; quoted = false; } else if (data[p].unicode() == '\\') { quoted = true; } else if (data[p].unicode() == ',' || data[p].unicode() == ';') { val.squeeze(); // release any unused memory value.append(val); val.clear(); val.reserve(data.size() - p); } else { val += data[p]; } } value.append(val); return value; } // maps the KService, QVariant and KDesktopFile keys to the new KPluginMetaData keys template static QJsonObject mapToJsonKPluginKey(const QString &name, const QString &description, const QStringList &dependencies, const QStringList &serviceTypes, const QStringList &formFactors, const T &data, Func accessor) { /* Metadata structure is as follows: "KPlugin": { "Name": "Date and Time", "Description": "Date and time by timezone", "Icon": "preferences-system-time", "Authors": { "Name": "Aaron Seigo", "Email": "aseigo@kde.org" }, "Category": "Date and Time", "Dependencies": [], "EnabledByDefault": "true", "License": "LGPL", "Id": "time", "Version": "1.0", "Website": "http://plasma.kde.org/", "ServiceTypes": ["Plasma/DataEngine"] "FormFactors": ["tablet", "handset"] } */ QJsonObject kplugin; kplugin[s_nameKey()] = name; kplugin[s_jsonDescriptionKey()] = description; kplugin[s_iconKey()] = accessor(data, s_iconKey()); QJsonObject authors; authors[s_nameKey()] = accessor(data, s_authorKey()); authors[s_jsonEmailKey()] = accessor(data, s_emailKey()); kplugin[s_jsonAuthorsKey()] = authors; kplugin[s_jsonCategoryKey()] = accessor(data, s_categoryKey()); QJsonValue enabledByDefault = accessor(data, s_enabledbyDefaultKey()); // make sure that enabledByDefault is bool and not string if (!enabledByDefault.isBool()) { enabledByDefault = enabledByDefault.toString().compare(QLatin1String("true"), Qt::CaseInsensitive) == 0; } kplugin[s_jsonEnabledByDefaultKey()] = enabledByDefault; kplugin[s_jsonLicenseKey()] = accessor(data, s_licenseKey()); kplugin[s_jsonIdKey()] = accessor(data, s_pluginNameKey()); kplugin[s_jsonVersionKey()] = accessor(data, s_versionKey()); kplugin[s_jsonWebsiteKey()] = accessor(data, s_websiteKey()); kplugin[s_jsonFormFactorsKey()] = QJsonArray::fromStringList(formFactors); kplugin[s_serviceTypesKey()] = QJsonArray::fromStringList(serviceTypes); kplugin[s_jsonDependenciesKey()] = QJsonArray::fromStringList(dependencies); QJsonValue mimeTypes = accessor(data, s_mimeTypeKey()); if (mimeTypes.isString()) { QStringList mimeList = KPluginInfoPrivate::deserializeList(mimeTypes.toString()); if (!mimeList.isEmpty()) { mimeTypes = QJsonArray::fromStringList(mimeList); } else { mimeTypes = QJsonValue(); } } kplugin[s_jsonMimeTypesKey()] = mimeTypes; return kplugin; } // TODO: KF6 remove static KPluginMetaData fromCompatibilityJson(const QJsonObject &json, const QString &lib, const QString &metaDataFile, bool warnOnOldStyle) { // This is not added to KPluginMetaData(QJsonObject, QString) to ensure that all the compatility code // remains in kservice and does not increase the size of kcoreaddons QStringList serviceTypes = KPluginMetaData::readStringList(json, s_xKDEServiceTypes()); if (serviceTypes.isEmpty()) { serviceTypes = KPluginMetaData::readStringList(json, s_serviceTypesKey()); } QJsonObject obj = json; QString name = KPluginMetaData::readTranslatedString(json, s_nameKey()); if (warnOnOldStyle) { qWarning("Constructing a KPluginInfo object from old style JSON. Please use" " kcoreaddons_desktop_to_json() for \"%s\" instead of kservice_desktop_to_json()" " in your CMake code.", qPrintable(lib)); } QString description = KPluginMetaData::readTranslatedString(json, s_commentKey()); QStringList formfactors = KPluginMetaData::readStringList(json, s_jsonFormFactorsKey()); QJsonObject kplugin = mapToJsonKPluginKey(name, description, KPluginMetaData::readStringList(json, s_dependenciesKey()), serviceTypes, formfactors, json, [](const QJsonObject &o, const QString &key) { return o.value(key); }); obj.insert(s_jsonKPluginKey(), kplugin); return KPluginMetaData(obj, lib, metaDataFile); } void KPluginInfoPrivate::setMetaData(const KPluginMetaData& md, bool warnOnOldStyle) { const QJsonObject json = md.rawData(); if (!json.contains(s_jsonKPluginKey())) { // "KPlugin" key does not exists -> convert from compatibility mode metaData = fromCompatibilityJson(json, md.fileName(), md.metaDataFileName(), warnOnOldStyle); } else { metaData = md; } } KPluginInfo::KPluginInfo(const KPluginMetaData &md) :d(new KPluginInfoPrivate) { d->setMetaData(md, true); if (!d->metaData.isValid()) { d.reset(); } } KPluginInfo::KPluginInfo(const QString &filename /*, QStandardPaths::StandardLocation resource*/) : d(new KPluginInfoPrivate) { KDesktopFile file(/*resource,*/ filename); KConfigGroup cg = file.desktopGroup(); if (!cg.exists()) { qWarning() << filename << "has no desktop group, cannot construct a KPluginInfo object from it."; d.reset(); return; } d->hidden = cg.readEntry(s_hiddenKey(), false); if (d->hidden) { return; } d->setMetaData(KPluginMetaData(file.fileName()), true); if (!d->metaData.isValid()) { qWarning() << "Failed to read metadata from .desktop file" << file.fileName(); d.reset(); } } KPluginInfo::KPluginInfo(const QVariantList &args, const QString &libraryPath) : d(new KPluginInfoPrivate) { const QString metaData = QStringLiteral("MetaData"); foreach (const QVariant &v, args) { if (v.canConvert()) { const QVariantMap &m = v.toMap(); const QVariant &_metadata = m.value(metaData); if (_metadata.canConvert()) { const QVariantMap &map = _metadata.toMap(); if (map.value(s_hiddenKey()).toBool()) { d->hidden = true; break; } d->setMetaData(KPluginMetaData(QJsonObject::fromVariantMap(map), libraryPath), true); break; } } } if (!d->metaData.isValid()) { d.reset(); } } #ifndef KSERVICE_NO_DEPRECATED KPluginInfo::KPluginInfo(const KService::Ptr service) : d(new KPluginInfoPrivate) { if (!service) { - d = 0; // isValid() == false + d = nullptr; // isValid() == false return; } d->service = service; if (service->isDeleted()) { d->hidden = true; return; } KSycoca::self()->ensureCacheValid(); QJsonObject json; foreach (const QString &key, service->propertyNames()) { QVariant::Type t = KSycocaPrivate::self()->serviceTypeFactory()->findPropertyTypeByName(key); if (t == QVariant::Invalid) { t = QVariant::String; // default to string if the type is not known } QVariant v = service->property(key, t); if (v.isValid()) { json[key] = QJsonValue::fromVariant(v); } } // reintroduce the separation between MimeType= and X-KDE-ServiceTypes= // we could do this by modifying KService and KSyCoCa, but as this is only compatibility // code we just query QMimeDatabase whether a ServiceType is a valid MIME type. // TODO: should we also make sure invalid MimeType= entries end up in KPluginMetaData::mimeTypes()? const QStringList services = service->serviceTypes(); if (!services.isEmpty()) { QMimeDatabase db; QStringList mimeTypes; mimeTypes.reserve(services.size()); QStringList newServiceTypes; newServiceTypes.reserve(services.size()); foreach (const QString& s, services) { if (db.mimeTypeForName(s).isValid()) { mimeTypes << s; } else { newServiceTypes << s; } } json[s_mimeTypeKey()] = QJsonArray::fromStringList(mimeTypes); json[s_xKDEServiceTypes()] = QJsonArray::fromStringList(newServiceTypes); json[s_serviceTypesKey()] = QJsonValue(); } d->setMetaData(KPluginMetaData(json, service->library(), service->entryPath()), false); if (!d->metaData.isValid()) { d.reset(); } } #endif KPluginInfo::KPluginInfo() - : d(0) // isValid() == false + : d(nullptr) // isValid() == false { } bool KPluginInfo::isValid() const { - return d.data() != 0; + return d.data() != nullptr; } KPluginInfo::KPluginInfo(const KPluginInfo &rhs) : d(rhs.d) { } KPluginInfo &KPluginInfo::operator=(const KPluginInfo &rhs) { d = rhs.d; return *this; } bool KPluginInfo::operator==(const KPluginInfo &rhs) const { return d == rhs.d; } bool KPluginInfo::operator!=(const KPluginInfo &rhs) const { return d != rhs.d; } bool KPluginInfo::operator<(const KPluginInfo &rhs) const { if (category() < rhs.category()) { return true; } if (category() == rhs.category()) { return name() < rhs.name(); } return false; } bool KPluginInfo::operator>(const KPluginInfo &rhs) const { if (category() > rhs.category()) { return true; } if (category() == rhs.category()) { return name() > rhs.name(); } return false; } KPluginInfo::~KPluginInfo() { } #ifndef KSERVICE_NO_DEPRECATED QList KPluginInfo::fromServices(const KService::List &services, const KConfigGroup &config) { QList infolist; for (KService::List::ConstIterator it = services.begin(); it != services.end(); ++it) { KPluginInfo info(*it); if (info.isValid()) { info.setConfig(config); infolist += info; } } return infolist; } #endif QList KPluginInfo::fromFiles(const QStringList &files, const KConfigGroup &config) { QList infolist; for (QStringList::ConstIterator it = files.begin(); it != files.end(); ++it) { KPluginInfo info(*it); info.setConfig(config); infolist += info; } return infolist; } QList KPluginInfo::fromKPartsInstanceName(const QString &name, const KConfigGroup &config) { QStringList files; const QStringList dirs = QStandardPaths::locateAll(QStandardPaths::GenericDataLocation, name + QStringLiteral("/kpartplugins"), QStandardPaths::LocateDirectory); Q_FOREACH (const QString &dir, dirs) { QDirIterator it(dir, QStringList() << QStringLiteral("*.desktop")); while (it.hasNext()) { files.append(it.next()); } } return fromFiles(files, config); } bool KPluginInfo::isHidden() const { KPLUGININFO_ISVALID_ASSERTION; return d->hidden; } void KPluginInfo::setPluginEnabled(bool enabled) { KPLUGININFO_ISVALID_ASSERTION; //qDebug() << Q_FUNC_INFO; d->pluginenabled = enabled; } bool KPluginInfo::isPluginEnabled() const { KPLUGININFO_ISVALID_ASSERTION; //qDebug() << Q_FUNC_INFO; return d->pluginenabled; } bool KPluginInfo::isPluginEnabledByDefault() const { KPLUGININFO_ISVALID_ASSERTION; //qDebug() << Q_FUNC_INFO; return d->metaData.isEnabledByDefault(); } QString KPluginInfo::name() const { KPLUGININFO_ISVALID_ASSERTION; return d->metaData.name(); } QString KPluginInfo::comment() const { KPLUGININFO_ISVALID_ASSERTION; return d->metaData.description(); } QString KPluginInfo::icon() const { KPLUGININFO_ISVALID_ASSERTION; return d->metaData.iconName(); } QString KPluginInfo::entryPath() const { KPLUGININFO_ISVALID_ASSERTION; return d->metaData.metaDataFileName(); } QString KPluginInfo::author() const { KPLUGININFO_ISVALID_ASSERTION; const QList &authors = d->metaData.authors(); return authors.isEmpty() ? QString() : authors[0].name(); } QString KPluginInfo::email() const { KPLUGININFO_ISVALID_ASSERTION; const QList &authors = d->metaData.authors(); return authors.isEmpty() ? QString() : authors[0].emailAddress(); } QString KPluginInfo::category() const { KPLUGININFO_ISVALID_ASSERTION; return d->metaData.category(); } QStringList KPluginInfo::formFactors() const { KPLUGININFO_ISVALID_ASSERTION; return d->metaData.formFactors(); } QString KPluginInfo::pluginName() const { KPLUGININFO_ISVALID_ASSERTION; return d->metaData.pluginId(); } QString KPluginInfo::libraryPath() const { KPLUGININFO_ISVALID_ASSERTION; return d->metaData.fileName(); } QString KPluginInfo::version() const { KPLUGININFO_ISVALID_ASSERTION; return d->metaData.version(); } QString KPluginInfo::website() const { KPLUGININFO_ISVALID_ASSERTION; return d->metaData.website(); } QString KPluginInfo::license() const { KPLUGININFO_ISVALID_ASSERTION; return d->metaData.license(); } #if 0 KAboutLicense KPluginInfo::fullLicense() const { KPLUGININFO_ISVALID_ASSERTION; return KAboutLicense::byKeyword(d->license); } #endif QStringList KPluginInfo::dependencies() const { KPLUGININFO_ISVALID_ASSERTION; return d->metaData.dependencies(); } QStringList KPluginInfo::serviceTypes() const { KPLUGININFO_ISVALID_ASSERTION; // KService/KPluginInfo include the MIME types in serviceTypes() return d->metaData.serviceTypes() + d->metaData.mimeTypes(); } KService::Ptr KPluginInfo::service() const { KPLUGININFO_ISVALID_ASSERTION; return d->service; } QList KPluginInfo::kcmServices() const { KPLUGININFO_ISVALID_ASSERTION; if (!d->kcmservicesCached) { d->kcmservices = KServiceTypeTrader::self()->query(QStringLiteral("KCModule"), QLatin1Char('\'') + pluginName() + QStringLiteral("' in [X-KDE-ParentComponents]")); //qDebug() << "found" << d->kcmservices.count() << "offers for" << d->pluginName; d->kcmservicesCached = true; } return d->kcmservices; } void KPluginInfo::setConfig(const KConfigGroup &config) { KPLUGININFO_ISVALID_ASSERTION; d->config = config; } KConfigGroup KPluginInfo::config() const { KPLUGININFO_ISVALID_ASSERTION; return d->config; } QVariant KPluginInfo::property(const QString &key) const { KPLUGININFO_ISVALID_ASSERTION; if (d->service) { return d->service->property(key); } QVariant result = d->metaData.rawData().value(key).toVariant(); if (result.isValid()) { KSycoca::self()->ensureCacheValid(); const QVariant::Type t = KSycocaPrivate::self()->serviceTypeFactory()->findPropertyTypeByName(key); //special case if we want a stringlist: split values by ',' or ';' and construct the list if (t == QVariant::StringList) { if (result.canConvert()) { result = KPluginInfoPrivate::deserializeList(result.toString()); } else if (result.canConvert()) { QVariantList list = result.toList(); QStringList newResult; foreach (const QVariant &value, list) { newResult += value.toString(); } result = newResult; } else qWarning() << "Cannot interpret" << result << "into a string list"; } return result; } // If the key was not found check compatibility for old key names and print a warning // These warnings should only happen if JSON was generated with kcoreaddons_desktop_to_json // but the application uses KPluginTrader::query() instead of KPluginLoader::findPlugins() // TODO: KF6 remove #define RETURN_WITH_DEPRECATED_WARNING(ret) \ qWarning("Calling KPluginInfo::property(\"%s\") is deprecated, use KPluginInfo::" #ret " in \"%s\" instead.", qPrintable(key), qPrintable(d->metaData.fileName()));\ return ret; if (key == s_authorKey()) { RETURN_WITH_DEPRECATED_WARNING(author()); } else if (key == s_categoryKey()) { RETURN_WITH_DEPRECATED_WARNING(category()); } else if (key == s_commentKey()) { RETURN_WITH_DEPRECATED_WARNING(comment()); } else if (key == s_dependenciesKey()) { RETURN_WITH_DEPRECATED_WARNING(dependencies()); } else if (key == s_emailKey()) { RETURN_WITH_DEPRECATED_WARNING(email()); } else if (key == s_enabledbyDefaultKey()) { RETURN_WITH_DEPRECATED_WARNING(isPluginEnabledByDefault()); } else if (key == s_libraryKey()) { RETURN_WITH_DEPRECATED_WARNING(libraryPath()); } else if (key == s_licenseKey()) { RETURN_WITH_DEPRECATED_WARNING(license()); } else if (key == s_nameKey()) { RETURN_WITH_DEPRECATED_WARNING(name()); } else if (key == s_pluginNameKey()) { RETURN_WITH_DEPRECATED_WARNING(pluginName()); } else if (key == s_serviceTypesKey()) { RETURN_WITH_DEPRECATED_WARNING(serviceTypes()); } else if (key == s_versionKey()) { RETURN_WITH_DEPRECATED_WARNING(version()); } else if (key == s_websiteKey()) { RETURN_WITH_DEPRECATED_WARNING(website()); } else if (key == s_xKDEServiceTypes()) { RETURN_WITH_DEPRECATED_WARNING(serviceTypes()); } else if (key == s_formFactorsKey()) { RETURN_WITH_DEPRECATED_WARNING(formFactors()); } #undef RETURN_WITH_DEPRECATED_WARNING // not a compatibility key -> return invalid QVariant return result; } QVariantMap KPluginInfo::properties() const { return d->metaData.rawData().toVariantMap(); } void KPluginInfo::save(KConfigGroup config) { KPLUGININFO_ISVALID_ASSERTION; //qDebug() << Q_FUNC_INFO; if (config.isValid()) { config.writeEntry(pluginName() + s_enabledKey(), isPluginEnabled()); } else { if (!d->config.isValid()) { qWarning() << "no KConfigGroup, cannot save"; return; } d->config.writeEntry(pluginName() + s_enabledKey(), isPluginEnabled()); } } void KPluginInfo::load(const KConfigGroup &config) { KPLUGININFO_ISVALID_ASSERTION; //qDebug() << Q_FUNC_INFO; if (config.isValid()) { setPluginEnabled(config.readEntry(pluginName() + s_enabledKey(), isPluginEnabledByDefault())); } else { if (!d->config.isValid()) { qWarning() << "no KConfigGroup, cannot load"; return; } setPluginEnabled(d->config.readEntry(pluginName() + s_enabledKey(), isPluginEnabledByDefault())); } } void KPluginInfo::defaults() { //qDebug() << Q_FUNC_INFO; setPluginEnabled(isPluginEnabledByDefault()); } uint qHash(const KPluginInfo &p) { return qHash(reinterpret_cast(p.d.data())); } KPluginInfo KPluginInfo::fromMetaData(const KPluginMetaData &md) { return KPluginInfo(md); } KPluginMetaData KPluginInfo::toMetaData() const { KPLUGININFO_ISVALID_ASSERTION; return d->metaData; } KPluginMetaData KPluginInfo::toMetaData(const KPluginInfo& info) { return info.toMetaData(); } KPluginInfo::List KPluginInfo::fromMetaData(const QVector &list) { KPluginInfo::List ret; ret.reserve(list.size()); foreach(const KPluginMetaData &md, list) { ret.append(KPluginInfo::fromMetaData(md)); } return ret; } QVector KPluginInfo::toMetaData(const KPluginInfo::List &list) { QVector ret; ret.reserve(list.size()); foreach(const KPluginInfo &info, list) { ret.append(info.toMetaData()); } return ret; } #undef KPLUGININFO_ISVALID_ASSERTION diff --git a/src/services/kservice.h b/src/services/kservice.h index de1e013..ff2f21a 100644 --- a/src/services/kservice.h +++ b/src/services/kservice.h @@ -1,588 +1,588 @@ /* This file is part of the KDE project Copyright (C) 1998, 1999 Torben Weis Copyright 1999-2006 David Faure This library is free software; you can redistribute it and/or modify it under the terms of the GNU Library General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Library General Public License for more details. You should have received a copy of the GNU Library General Public License along with this library; see the file COPYING.LIB. If not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #ifndef KSERVICE_H #define KSERVICE_H #include "kserviceaction.h" #include #include #include #include #include #include #include class KServiceType; class QDataStream; class KDesktopFile; class QWidget; class KServicePrivate; /** * Represent a service, like an application or plugin * bound to one or several mimetypes (or servicetypes) as written * in its desktop entry file. * * The starting point you need is often the static methods, like createInstance(). * The types of service a plugin provides is taken from the accompanying desktop file * where the 'ServiceTypes=' field is used. * * For a tutorial on how to build a plugin-loading mechanism and how to write plugins * in general, see http://techbase.kde.org/Development/Tutorials#Services:_Applications_and_Plugins * * @see KServiceType * @see KServiceGroup * @author Torben Weis */ class KSERVICE_EXPORT KService : public KSycocaEntry { public: typedef QExplicitlySharedDataPointer Ptr; typedef QList List; /** * Construct a temporary service with a given name, exec-line and icon. * @param name the name of the service * @param exec the executable * @param icon the name of the icon */ KService(const QString &name, const QString &exec, const QString &icon); /** * Construct a service and take all information from a config file. * * @param fullpath Full path to the config file. */ explicit KService(const QString &fullpath); /** * Construct a service and take all information from a desktop file. * @param config the desktop file to read * @param optional relative path to store for findByName */ explicit KService(const KDesktopFile *config, const QString &entryPath = QString()); virtual ~KService(); /** * Services are either applications (executables) or dlopened libraries (plugins). * @return true if this service is an application, i.e. it has Type=Application in its * .desktop file and exec() will not be empty. */ bool isApplication() const; /** * Returns the executable. * @return the command that the service executes, * or QString() if not set */ QString exec() const; /** * Returns the name of the service's library. * @return the name of the library that contains the service's * implementation, or QString() if not set */ QString library() const; /** * Returns the name of the icon. * @return the icon associated with the service, * or QString() if not set */ QString icon() const; /** * Checks whether the service should be run in a terminal. * @return true if the service is to be run in a terminal. */ bool terminal() const; /** * Returns any options associated with the terminal the service * runs in, if it requires a terminal. * * The service must be a tty-oriented program. * @return the terminal options, * or QString() if not set */ QString terminalOptions() const; /** * Checks whether the service runs on a discrete graphics card * @return true if the service has to run under a discrete graphics card * @since 5.30 */ bool runOnDiscreteGpu() const; /** * Checks whether the service runs with a different user id. * @return true if the service has to be run under a different uid. * @see username() */ bool substituteUid() const; /** * Returns the user name, if the service runs with a * different user id. * @return the username under which the service has to be run, * or QString() if not set * @see substituteUid() */ QString username() const; /** * Returns the filename of the service desktop entry without any * extension. E.g. "kppp" * @return the name of the desktop entry without path or extension, * or QString() if not set */ QString desktopEntryName() const; /** * Returns the menu ID of the service desktop entry. * The menu ID is used to add or remove the entry to a menu. * @return the menu ID */ QString menuId() const; /** * Returns a normalized ID suitable for storing in configuration files. * It will be based on the menu-id when available and otherwise falls * back to entryPath() * @return the storage ID */ QString storageId() const; /** * Describes the DBUS Startup type of the service. * @li None - This service has no DBUS support * @li Unique - This service provides a unique DBUS service. * The service name is equal to the desktopEntryName. * @li Multi - This service provides a DBUS service which can be run * with multiple instances in parallel. The service name of * an instance is equal to the desktopEntryName + "-" + * the PID of the process. */ enum DBusStartupType { DBusNone = 0, DBusUnique, DBusMulti }; /** * Returns the DBUSStartupType supported by this service. * @return the DBUSStartupType supported by this service */ DBusStartupType dbusStartupType() const; /** * Returns the working directory to run the program in. * @return the working directory to run the program in, * or QString() if not set */ QString path() const; /** * Returns the descriptive comment for the service, if there is one. * @return the descriptive comment for the service, or QString() * if not set */ QString comment() const; /** * Returns the generic name for the service, if there is one * (e.g. "Mail Client"). * @return the generic name, * or QString() if not set */ QString genericName() const; /** * Returns the untranslated (US English) generic name * for the service, if there is one * (e.g. "Mail Client"). * @return the generic name, * or QString() if not set */ QString untranslatedGenericName() const; /** * Returns a list of descriptive keywords the service, if there are any. * @return the list of keywords */ QStringList keywords() const; /** * Returns a list of VFolder categories. * @return the list of VFolder categories */ QStringList categories() const; /** * Returns the list of mime types that this service supports. * Note that this doesn't include inherited mimetypes, * only the mimetypes types listed in the .desktop file. * @since 4.8.3 */ QStringList mimeTypes() const; /** * Returns the service types that this service supports. * @return the list of service types that are supported * Note that this doesn't include inherited servicetypes or mimetypes, * only the service types listed in the .desktop file. */ QStringList serviceTypes() const; /** * Checks whether the service supports this service type * @param serviceTypePtr The name of the service type you are * interested in determining whether this service supports. * * @return true if the service type you specified is supported, otherwise false. */ bool hasServiceType(const QString &serviceTypePtr) const; /** * Checks whether the service supports this mime type * @param mimeType The name of the mime type you are * interested in determining whether this service supports. * @since 4.6 */ bool hasMimeType(const QString &mimeType) const; /** * Set to true if it is allowed to use this service as the default (main) * action for the files it supports (e.g. Left Click in a file manager, or KRun in general). * * If not, then this service is only available in RMB popups, so it must * be selected explicitly by the user in order to be used. * Note that servicemenus supersede this functionality though, at least in konqueror. * * @return true if the service may be used as the default (main) handler */ bool allowAsDefault() const; /** * Returns the actions defined in this desktop file */ QList actions() const; /** * Checks whether this service can handle several files as * startup arguments. * @return true if multiple files may be passed to this service at * startup. False if only one file at a time may be passed. */ bool allowMultipleFiles() const; /** * What preference to associate with this service initially (before * the user has had any chance to define a profile for it). * The bigger the value, the most preferred the service is. * @return the service preference level of the service */ int initialPreference() const; /** * Whether the entry should be suppressed in the K menu. * @return true to suppress this service * * Such services still appear in trader queries, i.e. in * "Open With" popup menus for instance. */ bool noDisplay() const; /** * Whether the service should be shown in KDE at all * (including in context menus). * @return true if the service should be shown. * * KMimeTypeTrader honors this and removes such services * from its results. * * @since 4.5 * @deprecated since 5.0, use showInCurrentDesktop() */ #ifndef KSERVICE_NO_DEPRECATED KSERVICE_DEPRECATED bool showInKDE() const { return showInCurrentDesktop(); } #endif /** * Whether the service should be shown in the current desktop * (including in context menus). * @return true if the service should be shown. * * KMimeTypeTrader honors this and removes such services * from its results. * * @since 5.0 */ bool showInCurrentDesktop() const; /** * Whether the service should be shown on the current * platform (e.g. on xcb or on wayland). * @return true if the service should be shown * * @since 5.0 */ bool showOnCurrentPlatform() const; /** * Name of the application this service belongs to. * (Useful for e.g. plugins) * @return the parent application, or QString() if not set */ QString parentApp() const; /** * The keyword to be used when constructing the plugin using KPluginFactory. The keyword is * defined with X-KDE-PluginKeyword in the .desktop file and with K_REGISTER_PLUGIN_WITH_KEYWORD * when implementing the plugin. */ QString pluginKeyword() const; /** * The path to the documentation for this service. * @since 4.2 * @return the documentation path, or QString() if not set */ QString docPath() const; /** * Returns the requested property. * * @param _name the name of the property * @param t the assumed type of the property * @return the property, or invalid if not found * @see KServiceType */ QVariant property(const QString &_name, QVariant::Type t) const; using KSycocaEntry::property; /** * Returns a path that can be used for saving changes to this * service * @return path that can be used for saving changes to this service */ QString locateLocal() const; /** * @internal * Set the menu id */ void setMenuId(const QString &menuId); /** * @internal * Sets whether to use a terminal or not */ void setTerminal(bool b); /** * @internal * Sets the terminal options to use */ void setTerminalOptions(const QString &options); /** * Overrides the "Exec=" line of the service. * * If @ref exec is not empty, its value will override the one * the one set when this service was created. * * Please note that @ref entryPath is also cleared so the service * will no longer be associated with a specific config file. * * @internal * @since 4.11 */ void setExec(const QString &exec); /** * Find a service based on its path as returned by entryPath(). * It's usually better to use serviceByStorageId() instead. * * @param _path the path of the configuration file * @return a pointer to the requested service or 0 if the service is * unknown. * @em Very @em important: Don't store the result in a KService* ! */ static Ptr serviceByDesktopPath(const QString &_path); /** * Find a service by the name of its desktop file, not depending on * its actual location (as long as it's under the applications or service * directories). For instance "konqbrowser" or "kcookiejar". Note that * the ".desktop" extension is implicit. * * This is the recommended method (safe even if the user moves stuff) * but note that it assumes that no two entries have the same filename. * * @param _name the name of the configuration file * @return a pointer to the requested service or 0 if the service is * unknown. * @em Very @em important: Don't store the result in a KService* ! */ static Ptr serviceByDesktopName(const QString &_name); /** * Find a service by its menu-id * * @param _menuId the menu id of the service * @return a pointer to the requested service or 0 if the service is * unknown. * @em Very @em important: Don't store the result in a KService* ! */ static Ptr serviceByMenuId(const QString &_menuId); /** * Find a service by its storage-id or desktop-file path. This * function will try very hard to find a matching service. * * @param _storageId the storage id or desktop-file path of the service * @return a pointer to the requested service or 0 if the service is * unknown. * @em Very @em important: Don't store the result in a KService* ! */ static Ptr serviceByStorageId(const QString &_storageId); /** * Returns the whole list of services. * * Useful for being able to * to display them in a list box, for example. * More memory consuming than the ones above, don't use unless * really necessary. * @return the list of all services */ static List allServices(); /** * Returns a path that can be used to create a new KService based * on @p suggestedName. * @param showInMenu true, if the service should be shown in the KDE menu * false, if the service should be hidden from the menu * This argument isn't used anymore, use NoDisplay=true to hide the service. * @param suggestedName name to base the file on, if a service with such * name already exists, a prefix will be added to make it unique. * @param menuId If provided, menuId will be set to the menu id to use for * the KService * @param reservedMenuIds If provided, the path and menu id will be chosen * in such a way that the new menu id does not conflict with any * of the reservedMenuIds * @return The path to use for the new KService. */ static QString newServicePath(bool showInMenu, const QString &suggestedName, - QString *menuId = 0, - const QStringList *reservedMenuIds = 0); + QString *menuId = nullptr, + const QStringList *reservedMenuIds = nullptr); /** * This template allows to load the library for the specified service and ask the * factory to create an instance of the given template type. * * @param parent The parent object (see QObject constructor) * @param args A list of arguments, passed to the factory and possibly * to the component (see KPluginFactory) * @param error A pointer to QString which should receive the error description or 0 * * @return A pointer to the newly created object or a null pointer if the * factory was unable to create an object of the given type. */ template - T *createInstance(QObject *parent = 0, - const QVariantList &args = QVariantList(), QString *error = 0) const + T *createInstance(QObject *parent = nullptr, + const QVariantList &args = QVariantList(), QString *error = nullptr) const { return createInstance(0, parent, args, error); } /** * This template allows to load the library for the specified service and ask the * factory to create an instance of the given template type. * * @param parentWidget A parent widget for the service. This is used e. g. for parts * @param parent The parent object (see QObject constructor) * @param args A list of arguments, passed to the factory and possibly * to the component (see KPluginFactory) * @param error A pointer to QString which should receive the error description or 0 * * @return A pointer to the newly created object or a null pointer if the * factory was unable to create an object of the given type. */ template T *createInstance(QWidget *parentWidget, QObject *parent, - const QVariantList &args = QVariantList(), QString *error = 0) const + const QVariantList &args = QVariantList(), QString *error = nullptr) const { KPluginLoader pluginLoader(*this); KPluginFactory *factory = pluginLoader.factory(); if (factory) { QVariantList argsWithMetaData = args; argsWithMetaData << pluginLoader.metaData().toVariantMap(); T *o = factory->template create(parentWidget, parent, pluginKeyword(), argsWithMetaData); if (!o && error) *error = QCoreApplication::translate("", "The service '%1' does not provide an interface '%2' with keyword '%3'") .arg(name(), QString::fromLatin1(T::staticMetaObject.className()), pluginKeyword()); return o; } else if (error) { *error = pluginLoader.errorString(); pluginLoader.unload(); } return 0; } /** * Convert this KService to a KPluginName. * * This allows expressions like * @code * KPluginLoader(service); * @endcode * which will use library() as the name of the plugin. If the service * is not valid or does not have a library, KPluginLoader::errorString() * will be set appropriately. */ operator KPluginName() const; private: friend class KBuildServiceFactory; /// @internal for KBuildSycoca only struct ServiceTypeAndPreference { ServiceTypeAndPreference() : preference(-1), serviceType() {} ServiceTypeAndPreference(int pref, const QString &servType) : preference(pref), serviceType(servType) {} int preference; QString serviceType; // or mimetype }; /// @internal for KBuildSycoca only QVector &_k_accessServiceTypes(); friend QDataStream &operator>>(QDataStream &, ServiceTypeAndPreference &); friend QDataStream &operator<<(QDataStream &, const ServiceTypeAndPreference &); Q_DISABLE_COPY(KService) Q_DECLARE_PRIVATE(KService) friend class KServiceFactory; /** * @internal * Construct a service from a stream. * The stream must already be positionned at the correct offset. */ KService(QDataStream &str, int offset); }; #endif diff --git a/src/services/kservicefactory.cpp b/src/services/kservicefactory.cpp index 68b7b0a..dfe7d6c 100644 --- a/src/services/kservicefactory.cpp +++ b/src/services/kservicefactory.cpp @@ -1,355 +1,355 @@ /* This file is part of the KDE libraries * Copyright (C) 1999-2006 David Faure * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Library General Public * License version 2 as published by the Free Software Foundation; * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Library General Public License for more details. * * You should have received a copy of the GNU Library General Public License * along with this library; see the file COPYING.LIB. If not, write to * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, * Boston, MA 02110-1301, USA. **/ #include "kservicefactory_p.h" #include "ksycoca.h" #include "ksycocatype.h" #include "ksycocadict_p.h" #include "kservice.h" #include #include #include extern int servicesDebugArea(); KServiceFactory::KServiceFactory(KSycoca *db) : KSycocaFactory(KST_KServiceFactory, db), - m_nameDict(0), - m_relNameDict(0), - m_menuIdDict(0) + m_nameDict(nullptr), + m_relNameDict(nullptr), + m_menuIdDict(nullptr) { m_offerListOffset = 0; m_nameDictOffset = 0; m_relNameDictOffset = 0; m_menuIdDictOffset = 0; if (!sycoca()->isBuilding()) { QDataStream *str = stream(); Q_ASSERT(str); if (!str) { return; } // Read Header qint32 i; (*str) >> i; m_nameDictOffset = i; (*str) >> i; m_relNameDictOffset = i; (*str) >> i; m_offerListOffset = i; (*str) >> i; m_menuIdDictOffset = i; const int saveOffset = str->device()->pos(); // Init index tables m_nameDict = new KSycocaDict(str, m_nameDictOffset); // Init index tables m_relNameDict = new KSycocaDict(str, m_relNameDictOffset); // Init index tables m_menuIdDict = new KSycocaDict(str, m_menuIdDictOffset); str->device()->seek(saveOffset); } } KServiceFactory::~KServiceFactory() { delete m_nameDict; delete m_relNameDict; delete m_menuIdDict; } KService::Ptr KServiceFactory::findServiceByName(const QString &_name) { if (!sycocaDict()) { return KService::Ptr(); // Error! } // Warning : this assumes we're NOT building a database // But since findServiceByName isn't called in that case... // [ see KServiceTypeFactory for how to do it if needed ] int offset = sycocaDict()->find_string(_name); if (!offset) { return KService::Ptr(); // Not found } KService::Ptr newService(createEntry(offset)); // Check whether the dictionary was right. if (newService && (newService->name() != _name)) { // No it wasn't... return KService::Ptr(); } return newService; } KService::Ptr KServiceFactory::findServiceByDesktopName(const QString &_name) { if (!m_nameDict) { return KService::Ptr(); // Error! } // Warning : this assumes we're NOT building a database // KBuildServiceFactory reimplements it for the case where we are building one int offset = m_nameDict->find_string(_name); if (!offset) { return KService::Ptr(); // Not found } KService::Ptr newService(createEntry(offset)); // Check whether the dictionary was right. if (newService && (newService->desktopEntryName() != _name)) { // No it wasn't... return KService::Ptr(); } return newService; } KService::Ptr KServiceFactory::findServiceByDesktopPath(const QString &_name) { if (!m_relNameDict) { return KService::Ptr(); // Error! } // Warning : this assumes we're NOT building a database // KBuildServiceFactory reimplements it for the case where we are building one int offset = m_relNameDict->find_string(_name); if (!offset) { //qDebug() << "findServiceByDesktopPath:" << _name << "not found"; return KService::Ptr(); // Not found } KService::Ptr newService(createEntry(offset)); if (!newService) { qDebug() << "createEntry failed!"; } // Check whether the dictionary was right // It's ok that it's wrong, for the case where we're looking up an unknown service, // and the hash value gave us another one. if (newService && (newService->entryPath() != _name)) { // No it wasn't... return KService::Ptr(); } return newService; } KService::Ptr KServiceFactory::findServiceByMenuId(const QString &_menuId) { if (!m_menuIdDict) { return KService::Ptr(); // Error! } // Warning : this assumes we're NOT building a database // KBuildServiceFactory reimplements it for the case where we are building one int offset = m_menuIdDict->find_string(_menuId); if (!offset) { return KService::Ptr(); // Not found } KService::Ptr newService(createEntry(offset)); // Check whether the dictionary was right. if (newService && (newService->menuId() != _menuId)) { // No it wasn't... return KService::Ptr(); } return newService; } KService::Ptr KServiceFactory::findServiceByStorageId(const QString &_storageId) { KService::Ptr service = findServiceByMenuId(_storageId); if (service) { return service; } service = findServiceByDesktopPath(_storageId); if (service) { return service; } if (!QDir::isRelativePath(_storageId) && QFile::exists(_storageId)) { return KService::Ptr(new KService(_storageId)); } QString tmp = _storageId; tmp = tmp.mid(tmp.lastIndexOf(QLatin1Char('/')) + 1); // Strip dir if (tmp.endsWith(QLatin1String(".desktop"))) { tmp.truncate(tmp.length() - 8); } if (tmp.endsWith(QLatin1String(".kdelnk"))) { tmp.truncate(tmp.length() - 7); } service = findServiceByDesktopName(tmp); return service; } KService *KServiceFactory::createEntry(int offset) const { KSycocaType type; QDataStream *str = sycoca()->findEntry(offset, type); if (type != KST_KService) { qWarning() << "KServiceFactory: unexpected object entry in KSycoca database (type=" << int(type) << ")"; - return 0; + return nullptr; } KService *newEntry = new KService(*str, offset); if (!newEntry->isValid()) { qWarning() << "KServiceFactory: corrupt object in KSycoca database!"; delete newEntry; - newEntry = 0; + newEntry = nullptr; } return newEntry; } KService::List KServiceFactory::allServices() { KService::List result; const KSycocaEntry::List list = allEntries(); KSycocaEntry::List::const_iterator it = list.begin(); const KSycocaEntry::List::const_iterator end = list.end(); for (; it != end; ++it) { const KSycocaEntry::Ptr entry = *it; if (entry->isType(KST_KService)) { KService::Ptr service(static_cast(entry.data())); result.append(service); } } return result; } QStringList KServiceFactory::resourceDirs() { return KSycocaFactory::allDirectories(QStringLiteral("kservices5")) + KSycocaFactory::allDirectories(QStringLiteral("applications")); } QList KServiceFactory::offers(int serviceTypeOffset, int serviceOffersOffset) { QList list; // Jump to the offer list QDataStream *str = stream(); str->device()->seek(m_offerListOffset + serviceOffersOffset); qint32 aServiceTypeOffset, aServiceOffset, initialPreference, mimeTypeInheritanceLevel; while (true) { (*str) >> aServiceTypeOffset; if (aServiceTypeOffset) { (*str) >> aServiceOffset; (*str) >> initialPreference; (*str) >> mimeTypeInheritanceLevel; if (aServiceTypeOffset == serviceTypeOffset) { // Save stream position ! const int savedPos = str->device()->pos(); // Create Service KService *serv = createEntry(aServiceOffset); if (serv) { KService::Ptr servPtr(serv); list.append(KServiceOffer(servPtr, initialPreference, mimeTypeInheritanceLevel, servPtr->allowAsDefault())); } // Restore position str->device()->seek(savedPos); } else { break; // too far } } else { break; // 0 => end of list } } return list; } KService::List KServiceFactory::serviceOffers(int serviceTypeOffset, int serviceOffersOffset) { KService::List list; // Jump to the offer list QDataStream *str = stream(); str->device()->seek(m_offerListOffset + serviceOffersOffset); qint32 aServiceTypeOffset, aServiceOffset, initialPreference, mimeTypeInheritanceLevel; while (true) { (*str) >> aServiceTypeOffset; if (aServiceTypeOffset) { (*str) >> aServiceOffset; (*str) >> initialPreference; (*str) >> mimeTypeInheritanceLevel; if (aServiceTypeOffset == serviceTypeOffset) { // Save stream position ! const int savedPos = str->device()->pos(); // Create service KService *serv = createEntry(aServiceOffset); if (serv) { list.append(KService::Ptr(serv)); } // Restore position str->device()->seek(savedPos); } else { break; // too far } } else { break; // 0 => end of list } } return list; } bool KServiceFactory::hasOffer(int serviceTypeOffset, int serviceOffersOffset, int testedServiceOffset) { // Save stream position QDataStream *str = stream(); const int savedPos = str->device()->pos(); // Jump to the offer list str->device()->seek(m_offerListOffset + serviceOffersOffset); bool found = false; qint32 aServiceTypeOffset, aServiceOffset, initialPreference, mimeTypeInheritanceLevel; while (!found) { (*str) >> aServiceTypeOffset; if (aServiceTypeOffset) { (*str) >> aServiceOffset; (*str) >> initialPreference; (*str) >> mimeTypeInheritanceLevel; if (aServiceTypeOffset == serviceTypeOffset) { if (aServiceOffset == testedServiceOffset) { found = true; } } else { break; // too far } } else { break; // 0 => end of list } } // Restore position str->device()->seek(savedPos); return found; } void KServiceFactory::virtual_hook(int id, void *data) { KSycocaFactory::virtual_hook(id, data); } diff --git a/src/services/kservicefactory_p.h b/src/services/kservicefactory_p.h index 1faa5d7..9dce4a8 100644 --- a/src/services/kservicefactory_p.h +++ b/src/services/kservicefactory_p.h @@ -1,136 +1,136 @@ /* This file is part of the KDE project Copyright (C) 1998, 1999 Torben Weis Copyright (C) 2006 David Faure This library is free software; you can redistribute it and/or modify it under the terms of the GNU Library General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Library General Public License for more details. You should have received a copy of the GNU Library General Public License along with this library; see the file COPYING.LIB. If not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #ifndef KSERVICEFACTORY_P_H #define KSERVICEFACTORY_P_H #include #include "kserviceoffer.h" #include "ksycocafactory_p.h" #include class KSycoca; class KSycocaDict; /** * @internal * A sycoca factory for services (e.g. applications) * It loads the services from parsing directories (e.g. prefix/share/applications/) * but can also create service from data streams or single config files * * Exported for unit tests */ class KSERVICE_EXPORT KServiceFactory : public KSycocaFactory { K_SYCOCAFACTORY(KST_KServiceFactory) public: /** * Create factory */ KServiceFactory(KSycoca *sycoca); virtual ~KServiceFactory(); /** * Construct a KService from a config file. */ KSycocaEntry *createEntry(const QString &) const Q_DECL_OVERRIDE { assert(0); - return 0; + return nullptr; } /** * Find a service (by translated name, e.g. "Terminal") * (Not virtual because not used inside kbuildsycoca4, only an external service for some KDE apps) */ KService::Ptr findServiceByName(const QString &_name); /** * Find a service (by desktop file name, e.g. "konsole") */ virtual KService::Ptr findServiceByDesktopName(const QString &_name); /** * Find a service ( by desktop path, e.g. "System/konsole.desktop") */ virtual KService::Ptr findServiceByDesktopPath(const QString &_name); /** * Find a service ( by menu id, e.g. "kde-konsole.desktop") */ virtual KService::Ptr findServiceByMenuId(const QString &_menuId); KService::Ptr findServiceByStorageId(const QString &_storageId); /** * @return the services supporting the given service type * The @p serviceOffersOffset allows to jump to the right entries directly. */ KServiceOfferList offers(int serviceTypeOffset, int serviceOffersOffset); /** * @return the services supporting the given service type; without information about initialPreference * The @p serviceOffersOffset allows to jump to the right entries directly. */ KService::List serviceOffers(int serviceTypeOffset, int serviceOffersOffset); /** * Test if a specific service is associated with a specific servicetype * @param serviceTypeOffset the offset of the service type being tested * @param serviceOffersOffset allows to jump to the right entries for the service type directly. * @param testedServiceOffset the offset of the service being tested */ bool hasOffer(int serviceTypeOffset, int serviceOffersOffset, int testedServiceOffset); /** * @return all services. Very memory consuming, avoid using. */ KService::List allServices(); /** * Returns the directories to watch for this factory. */ static QStringList resourceDirs(); /** * @return the unique service factory, creating it if necessary */ static KServiceFactory *self(); protected: KService *createEntry(int offset) const Q_DECL_OVERRIDE; // All those variables are used by KBuildServiceFactory too int m_offerListOffset; KSycocaDict *m_nameDict; int m_nameDictOffset; KSycocaDict *m_relNameDict; int m_relNameDictOffset; KSycocaDict *m_menuIdDict; int m_menuIdDictOffset; protected: void virtual_hook(int id, void *data) Q_DECL_OVERRIDE; private: class KServiceFactoryPrivate *d; }; #endif diff --git a/src/services/kservicegroup.cpp b/src/services/kservicegroup.cpp index 56286d1..b2be184 100644 --- a/src/services/kservicegroup.cpp +++ b/src/services/kservicegroup.cpp @@ -1,724 +1,724 @@ /* This file is part of the KDE libraries * Copyright (C) 2000 Waldo Bastian * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Library General Public * License version 2 as published by the Free Software Foundation; * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Library General Public License for more details. * * You should have received a copy of the GNU Library General Public License * along with this library; see the file COPYING.LIB. If not, write to * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, * Boston, MA 02110-1301, USA. **/ #include "kservicegroup.h" #include "kservicegroup_p.h" #include "kservicefactory_p.h" #include "kservicegroupfactory_p.h" #include "kservice.h" #include "ksycoca_p.h" #include #include #include #include KServiceGroup::KServiceGroup(const QString &name) : KSycocaEntry(*new KServiceGroupPrivate(name)) { } KServiceGroup::KServiceGroup(const QString &configFile, const QString &_relpath) : KSycocaEntry(*new KServiceGroupPrivate(_relpath)) { Q_D(KServiceGroup); QString cfg = configFile; if (cfg.isEmpty()) { cfg = _relpath + QLatin1String(".directory"); } d->load(cfg); } void KServiceGroupPrivate::load(const QString &cfg) { directoryEntryPath = cfg; const KDesktopFile desktopFile(cfg); const KConfigGroup config = desktopFile.desktopGroup(); m_strCaption = config.readEntry("Name"); m_strIcon = config.readEntry("Icon"); m_strComment = config.readEntry("Comment"); deleted = config.readEntry("Hidden", false); m_bNoDisplay = desktopFile.noDisplay(); m_strBaseGroupName = config.readEntry("X-KDE-BaseGroup"); suppressGenericNames = config.readEntry("X-KDE-SuppressGenericNames", QStringList()); // d->sortOrder = config.readEntry("SortOrder", QStringList()); // Fill in defaults. if (m_strCaption.isEmpty()) { m_strCaption = path; if (m_strCaption.endsWith(QLatin1Char('/'))) { m_strCaption = m_strCaption.left(m_strCaption.length() - 1); } int i = m_strCaption.lastIndexOf(QLatin1Char('/')); if (i > 0) { m_strCaption = m_strCaption.mid(i + 1); } } if (m_strIcon.isEmpty()) { m_strIcon = QStringLiteral("folder"); } } KServiceGroup::KServiceGroup(QDataStream &_str, int offset, bool deep) : KSycocaEntry(*new KServiceGroupPrivate(_str, offset)) { Q_D(KServiceGroup); d->m_bDeep = deep; d->load(_str); } KServiceGroup::~KServiceGroup() { } QString KServiceGroup::relPath() const { return entryPath(); } QString KServiceGroup::caption() const { Q_D(const KServiceGroup); return d->m_strCaption; } QString KServiceGroup::icon() const { Q_D(const KServiceGroup); return d->m_strIcon; } QString KServiceGroup::comment() const { Q_D(const KServiceGroup); return d->m_strComment; } int KServiceGroup::childCount() const { Q_D(const KServiceGroup); return d->childCount(); } int KServiceGroupPrivate::childCount() const { if (m_childCount == -1) { m_childCount = 0; for (KServiceGroup::List::ConstIterator it = m_serviceList.begin(); it != m_serviceList.end(); ++it) { KSycocaEntry::Ptr p = *it; if (p->isType(KST_KService)) { KService::Ptr service(static_cast(p.data())); if (!service->noDisplay()) { m_childCount++; } } else if (p->isType(KST_KServiceGroup)) { KServiceGroup::Ptr serviceGroup(static_cast(p.data())); m_childCount += serviceGroup->childCount(); } } } return m_childCount; } bool KServiceGroup::showInlineHeader() const { Q_D(const KServiceGroup); return d->m_bShowInlineHeader; } bool KServiceGroup::showEmptyMenu() const { Q_D(const KServiceGroup); return d->m_bShowEmptyMenu; } bool KServiceGroup::inlineAlias() const { Q_D(const KServiceGroup); return d->m_bInlineAlias; } void KServiceGroup::setInlineAlias(bool _b) { Q_D(KServiceGroup); d->m_bInlineAlias = _b; } void KServiceGroup::setShowEmptyMenu(bool _b) { Q_D(KServiceGroup); d->m_bShowEmptyMenu = _b; } void KServiceGroup::setShowInlineHeader(bool _b) { Q_D(KServiceGroup); d->m_bShowInlineHeader = _b; } int KServiceGroup::inlineValue() const { Q_D(const KServiceGroup); return d->m_inlineValue; } void KServiceGroup::setInlineValue(int _val) { Q_D(KServiceGroup); d->m_inlineValue = _val; } bool KServiceGroup::allowInline() const { Q_D(const KServiceGroup); return d->m_bAllowInline; } void KServiceGroup::setAllowInline(bool _b) { Q_D(KServiceGroup); d->m_bAllowInline = _b; } bool KServiceGroup::noDisplay() const { Q_D(const KServiceGroup); return d->m_bNoDisplay || d->m_strCaption.startsWith(QLatin1Char('.')); } QStringList KServiceGroup::suppressGenericNames() const { Q_D(const KServiceGroup); return d->suppressGenericNames; } void KServiceGroupPrivate::load(QDataStream &s) { QStringList groupList; qint8 noDisplay; qint8 _showEmptyMenu; qint8 inlineHeader; qint8 _inlineAlias; qint8 _allowInline; s >> m_strCaption >> m_strIcon >> m_strComment >> groupList >> m_strBaseGroupName >> m_childCount >> noDisplay >> suppressGenericNames >> directoryEntryPath >> sortOrder >> _showEmptyMenu >> inlineHeader >> _inlineAlias >> _allowInline; m_bNoDisplay = (noDisplay != 0); m_bShowEmptyMenu = (_showEmptyMenu != 0); m_bShowInlineHeader = (inlineHeader != 0); m_bInlineAlias = (_inlineAlias != 0); m_bAllowInline = (_allowInline != 0); if (m_bDeep) { Q_FOREACH (const QString &path, groupList) { if (path.endsWith(QLatin1Char('/'))) { KServiceGroup::Ptr serviceGroup; serviceGroup = KSycocaPrivate::self()->serviceGroupFactory()->findGroupByDesktopPath(path, false); if (serviceGroup) { m_serviceList.append(KServiceGroup::SPtr(serviceGroup)); } } else { KService::Ptr service; service = KSycocaPrivate::self()->serviceFactory()->findServiceByDesktopPath(path); if (service) { m_serviceList.append(KServiceGroup::SPtr(service)); } } } } } void KServiceGroup::addEntry(const KSycocaEntry::Ptr &entry) { Q_D(KServiceGroup); d->m_serviceList.append(entry); } void KServiceGroupPrivate::save(QDataStream &s) { KSycocaEntryPrivate::save(s); QStringList groupList; Q_FOREACH (KSycocaEntry::Ptr p, m_serviceList) { if (p->isType(KST_KService)) { KService::Ptr service(static_cast(p.data())); groupList.append(service->entryPath()); } else if (p->isType(KST_KServiceGroup)) { KServiceGroup::Ptr serviceGroup(static_cast(p.data())); groupList.append(serviceGroup->relPath()); } else { //fprintf(stderr, "KServiceGroup: Unexpected object in list!\n"); } } (void) childCount(); qint8 noDisplay = m_bNoDisplay ? 1 : 0; qint8 _showEmptyMenu = m_bShowEmptyMenu ? 1 : 0; qint8 inlineHeader = m_bShowInlineHeader ? 1 : 0; qint8 _inlineAlias = m_bInlineAlias ? 1 : 0; qint8 _allowInline = m_bAllowInline ? 1 : 0; s << m_strCaption << m_strIcon << m_strComment << groupList << m_strBaseGroupName << m_childCount << noDisplay << suppressGenericNames << directoryEntryPath << sortOrder << _showEmptyMenu << inlineHeader << _inlineAlias << _allowInline; } QList KServiceGroup::groupEntries(EntriesOptions options) { Q_D(KServiceGroup); bool sort = options & SortEntries || options & AllowSeparators; QList list; List tmp = d->entries(this, sort, options & ExcludeNoDisplay, options & AllowSeparators, options & SortByGenericName); foreach (const SPtr &ptr, tmp) { if (ptr->isType(KST_KServiceGroup)) { KServiceGroup::Ptr serviceGroup(static_cast(ptr.data())); list.append(serviceGroup); } else if (ptr->isType(KST_KServiceSeparator)) { list.append(KServiceGroup::Ptr(static_cast(new KSycocaEntry()))); } else if (sort && ptr->isType(KST_KService)) { break; } } return list; } KService::List KServiceGroup::serviceEntries(EntriesOptions options) { Q_D(KServiceGroup); bool sort = options & SortEntries || options & AllowSeparators; QList list; List tmp = d->entries(this, sort, options & ExcludeNoDisplay, options & AllowSeparators, options & SortByGenericName); bool foundService = false; foreach (const SPtr &ptr, tmp) { if (ptr->isType(KST_KService)) { list.append(KService::Ptr(static_cast(ptr.data()))); foundService = true; } else if (ptr->isType(KST_KServiceSeparator) && foundService) { list.append(KService::Ptr(static_cast(new KSycocaEntry()))); } } return list; } KServiceGroup::List KServiceGroup::entries(bool sort) { Q_D(KServiceGroup); return d->entries(this, sort, true, false, false); } KServiceGroup::List KServiceGroup::entries(bool sort, bool excludeNoDisplay) { Q_D(KServiceGroup); return d->entries(this, sort, excludeNoDisplay, false, false); } KServiceGroup::List KServiceGroup::entries(bool sort, bool excludeNoDisplay, bool allowSeparators, bool sortByGenericName) { Q_D(KServiceGroup); return d->entries(this, sort, excludeNoDisplay, allowSeparators, sortByGenericName); } static void addItem(KServiceGroup::List &sorted, const KSycocaEntry::Ptr &p, bool &addSeparator) { if (addSeparator && !sorted.isEmpty()) { sorted.append(KSycocaEntry::Ptr(new KServiceSeparator())); } sorted.append(p); addSeparator = false; } KServiceGroup::List KServiceGroupPrivate::entries(KServiceGroup *group, bool sort, bool excludeNoDisplay, bool allowSeparators, bool sortByGenericName) { KSycoca::self()->ensureCacheValid(); // If the entries haven't been loaded yet, we have to reload ourselves // together with the entries. We can't only load the entries afterwards // since the offsets could have been changed if the database has changed. KServiceGroup::Ptr grp; if (!m_bDeep) { grp = KSycocaPrivate::self()->serviceGroupFactory()->findGroupByDesktopPath(path, true); group = grp.data(); - if (0 == group) { // No guarantee that we still exist! + if (nullptr == group) { // No guarantee that we still exist! return KServiceGroup::List(); } } if (!sort) { return group->d_func()->m_serviceList; } // Sort the list alphabetically, according to locale. // Groups come first, then services. // We use a QMap, for sorting using a stored temporary key. typedef QMap SortedContainer; SortedContainer slist; SortedContainer glist; Q_FOREACH (KSycocaEntry::Ptr p, group->d_func()->m_serviceList) { bool noDisplay = p->isType(KST_KServiceGroup) ? static_cast(p.data())->noDisplay() : static_cast(p.data())->noDisplay(); if (excludeNoDisplay && noDisplay) { continue; } // Choose the right list SortedContainer &list = p->isType(KST_KServiceGroup) ? glist : slist; QString name; if (p->isType(KST_KServiceGroup)) { name = static_cast(p.data())->caption(); } else if (sortByGenericName) { name = static_cast(p.data())->genericName() + QLatin1Char(' ') + p->name(); } else { name = p->name() + QLatin1Char(' ') + static_cast(p.data())->genericName(); } const QByteArray nameStr = name.toLocal8Bit(); QByteArray key; // strxfrm() crashes on Solaris and strxfrm is not defined under wince #if !defined(USE_SOLARIS) && !defined(_WIN32_WCE) // maybe it'd be better to use wcsxfrm() where available key.resize(name.length() * 4 + 1); size_t ln = strxfrm(key.data(), nameStr.constData(), key.size()); if (ln != size_t(-1)) { key.resize(ln); if (int(ln) >= key.size()) { // didn't fit? ln = strxfrm(key.data(), nameStr.constData(), key.size()); if (ln == size_t(-1)) { key = nameStr; } } } else #endif { key = nameStr; } list.insert(key, KServiceGroup::SPtr(p)); } if (sortOrder.isEmpty()) { sortOrder << QStringLiteral(":M"); sortOrder << QStringLiteral(":F"); sortOrder << QStringLiteral(":OIH IL[4]"); //just inline header } QString rp = path; if (rp == QLatin1String("/")) { rp.clear(); } // Iterate through the sort spec list. // If an entry gets mentioned explicitly, we remove it from the sorted list Q_FOREACH (const QString &item, sortOrder) { if (item.isEmpty()) { continue; } if (item[0] == QLatin1Char('/')) { QString groupPath = rp + item.mid(1) + QLatin1Char('/'); // Remove entry from sorted list of services. for (SortedContainer::iterator it2 = glist.begin(); it2 != glist.end(); ++it2) { const KServiceGroup::Ptr group(static_cast(it2.value().data())); if (group->relPath() == groupPath) { glist.erase(it2); break; } } } else if (item[0] != QLatin1Char(':')) { // Remove entry from sorted list of services. // TODO: Remove item from sortOrder-list if not found // TODO: This prevents duplicates for (SortedContainer::iterator it2 = slist.begin(); it2 != slist.end(); ++it2) { const KService::Ptr service(static_cast(it2.value().data())); if (service->menuId() == item) { slist.erase(it2); break; } } } } KServiceGroup::List sorted; bool needSeparator = false; // Iterate through the sort spec list. // Add the entries to the list according to the sort spec. for (QStringList::ConstIterator it(sortOrder.constBegin()); it != sortOrder.constEnd(); ++it) { const QString &item = *it; if (item.isEmpty()) { continue; } if (item[0] == QLatin1Char(':')) { // Special condition... if (item == QLatin1String(":S")) { if (allowSeparators) { needSeparator = true; } } else if (item.contains(QLatin1String(":O"))) { //todo parse attribute: QString tmp(item); tmp = tmp.remove(QStringLiteral(":O")); QStringList optionAttribute = tmp.split(QLatin1Char(' '), QString::SkipEmptyParts); if (optionAttribute.isEmpty()) { optionAttribute.append(tmp); } bool showEmptyMenu = false; bool showInline = false; bool showInlineHeader = false; bool showInlineAlias = false; int inlineValue = -1; for (QStringList::Iterator it3 = optionAttribute.begin(); it3 != optionAttribute.end(); ++it3) { parseAttribute(*it3, showEmptyMenu, showInline, showInlineHeader, showInlineAlias, inlineValue); } for (SortedContainer::Iterator it2 = glist.begin(); it2 != glist.end(); ++it2) { KServiceGroup::Ptr group(static_cast(it2.value().data())); group->setShowEmptyMenu(showEmptyMenu); group->setAllowInline(showInline); group->setShowInlineHeader(showInlineHeader); group->setInlineAlias(showInlineAlias); group->setInlineValue(inlineValue); } } else if (item == QLatin1String(":M")) { // Add sorted list of sub-menus for (SortedContainer::const_iterator it2 = glist.constBegin(); it2 != glist.constEnd(); ++it2) { addItem(sorted, it2.value(), needSeparator); } } else if (item == QLatin1String(":F")) { // Add sorted list of services for (SortedContainer::const_iterator it2 = slist.constBegin(); it2 != slist.constEnd(); ++it2) { addItem(sorted, it2.value(), needSeparator); } } else if (item == QLatin1String(":A")) { // Add sorted lists of services and submenus SortedContainer::Iterator it_s = slist.begin(); SortedContainer::Iterator it_g = glist.begin(); while (true) { if (it_s == slist.end()) { if (it_g == glist.end()) { break; // Done } // Insert remaining sub-menu addItem(sorted, it_g.value(), needSeparator); it_g++; } else if (it_g == glist.end()) { // Insert remaining service addItem(sorted, it_s.value(), needSeparator); it_s++; } else if (it_g.key() < it_s.key()) { // Insert sub-menu first addItem(sorted, it_g.value(), needSeparator); it_g++; } else { // Insert service first addItem(sorted, it_s.value(), needSeparator); it_s++; } } } } else if (item[0] == QLatin1Char('/')) { QString groupPath = rp + item.mid(1) + QLatin1Char('/'); for (KServiceGroup::List::ConstIterator it2(group->d_func()->m_serviceList.constBegin()); it2 != group->d_func()->m_serviceList.constEnd(); ++it2) { if (!(*it2)->isType(KST_KServiceGroup)) { continue; } KServiceGroup::Ptr group(static_cast((*it2).data())); if (group->relPath() == groupPath) { if (!excludeNoDisplay || !group->noDisplay()) { ++it; const QString &nextItem = (it == sortOrder.constEnd()) ? QString() : *it; if (nextItem.startsWith(QLatin1String(":O"))) { QString tmp(nextItem); tmp = tmp.remove(QStringLiteral(":O")); QStringList optionAttribute = tmp.split(QLatin1Char(' '), QString::SkipEmptyParts); if (optionAttribute.isEmpty()) { optionAttribute.append(tmp); } bool bShowEmptyMenu = false; bool bShowInline = false; bool bShowInlineHeader = false; bool bShowInlineAlias = false; int inlineValue = -1; Q_FOREACH (const QString &opt_attr, optionAttribute) { parseAttribute(opt_attr, bShowEmptyMenu, bShowInline, bShowInlineHeader, bShowInlineAlias, inlineValue); group->setShowEmptyMenu(bShowEmptyMenu); group->setAllowInline(bShowInline); group->setShowInlineHeader(bShowInlineHeader); group->setInlineAlias(bShowInlineAlias); group->setInlineValue(inlineValue); } } else { it--; } addItem(sorted, KServiceGroup::SPtr(group), needSeparator); } break; } } } else { for (KServiceGroup::List::ConstIterator it2(group->d_func()->m_serviceList.constBegin()); it2 != group->d_func()->m_serviceList.constEnd(); ++it2) { if (!(*it2)->isType(KST_KService)) { continue; } const KService::Ptr service(static_cast((*it2).data())); if (service->menuId() == item) { if (!excludeNoDisplay || !service->noDisplay()) { addItem(sorted, (*it2), needSeparator); } break; } } } } return sorted; } void KServiceGroupPrivate::parseAttribute(const QString &item, bool &showEmptyMenu, bool &showInline, bool &showInlineHeader, bool &showInlineAlias, int &inlineValue) { if (item == QLatin1String("ME")) { //menu empty showEmptyMenu = true; } else if (item == QLatin1String("NME")) { //not menu empty showEmptyMenu = false; } else if (item == QLatin1String("I")) { //inline menu ! showInline = true; } else if (item == QLatin1String("NI")) { //not inline menu! showInline = false; } else if (item == QLatin1String("IH")) { //inline header! showInlineHeader = true; } else if (item == QLatin1String("NIH")) { //not inline header! showInlineHeader = false; } else if (item == QLatin1String("IA")) { //inline alias! showInlineAlias = true; } else if (item == QLatin1String("NIA")) { //not inline alias! showInlineAlias = false; } else if ((item).contains(QLatin1String("IL"))) { //inline limite! QString tmp(item); tmp = tmp.remove(QStringLiteral("IL[")); tmp = tmp.remove(QLatin1Char(']')); bool ok; int _inlineValue = tmp.toInt(&ok); if (!ok) { //error _inlineValue = -1; } inlineValue = _inlineValue; } else { qDebug() << "This attribute is not supported:" << item; } } void KServiceGroup::setLayoutInfo(const QStringList &layout) { Q_D(KServiceGroup); d->sortOrder = layout; } QStringList KServiceGroup::layoutInfo() const { Q_D(const KServiceGroup); return d->sortOrder; } KServiceGroup::Ptr KServiceGroup::root() { KSycoca::self()->ensureCacheValid(); return KSycocaPrivate::self()->serviceGroupFactory()->findGroupByDesktopPath(QStringLiteral("/"), true); } KServiceGroup::Ptr KServiceGroup::group(const QString &relPath) { if (relPath.isEmpty()) { return root(); } KSycoca::self()->ensureCacheValid(); return KSycocaPrivate::self()->serviceGroupFactory()->findGroupByDesktopPath(relPath, true); } KServiceGroup::Ptr KServiceGroup::childGroup(const QString &parent) { KSycoca::self()->ensureCacheValid(); return KSycocaPrivate::self()->serviceGroupFactory()->findGroupByDesktopPath(QString::fromLatin1("#parent#") + parent, true); } QString KServiceGroup::baseGroupName() const { return d_func()->m_strBaseGroupName; } QString KServiceGroup::directoryEntryPath() const { Q_D(const KServiceGroup); return d->directoryEntryPath; } class KServiceSeparatorPrivate : public KSycocaEntryPrivate { public: K_SYCOCATYPE(KST_KServiceSeparator, KSycocaEntryPrivate) KServiceSeparatorPrivate(const QString &name) : KSycocaEntryPrivate(name) { } QString name() const Q_DECL_OVERRIDE; }; QString KServiceSeparatorPrivate::name() const { return QStringLiteral("separator"); } KServiceSeparator::KServiceSeparator() : KSycocaEntry(*new KServiceSeparatorPrivate(QStringLiteral("separator"))) { } KServiceSeparator::~KServiceSeparator() { } diff --git a/src/services/kservicegroupfactory.cpp b/src/services/kservicegroupfactory.cpp index 5ba8872..6d51619 100644 --- a/src/services/kservicegroupfactory.cpp +++ b/src/services/kservicegroupfactory.cpp @@ -1,126 +1,126 @@ /* This file is part of the KDE libraries * Copyright (C) 2000 Waldo Bastian * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Library General Public * License version 2 as published by the Free Software Foundation; * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Library General Public License for more details. * * You should have received a copy of the GNU Library General Public License * along with this library; see the file COPYING.LIB. If not, write to * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, * Boston, MA 02110-1301, USA. **/ #include "kservicegroupfactory_p.h" #include "ksycoca.h" #include "ksycocatype.h" #include "ksycocadict_p.h" #include "kservice.h" #include KServiceGroupFactory::KServiceGroupFactory(KSycoca *db) : KSycocaFactory(KST_KServiceGroupFactory, db) - , m_baseGroupDict(0) + , m_baseGroupDict(nullptr) , m_baseGroupDictOffset(0) { if (!sycoca()->isBuilding()) { QDataStream *str = stream(); if (!str) { return; } // Read Header qint32 i; (*str) >> i; m_baseGroupDictOffset = i; const int saveOffset = str->device()->pos(); // Init index tables m_baseGroupDict = new KSycocaDict(str, m_baseGroupDictOffset); str->device()->seek(saveOffset); } } KServiceGroupFactory::~KServiceGroupFactory() { delete m_baseGroupDict; } KServiceGroup::Ptr KServiceGroupFactory::findGroupByDesktopPath(const QString &_name, bool deep) { if (!sycocaDict()) { return KServiceGroup::Ptr(); // Error! } int offset = sycocaDict()->find_string(_name); if (!offset) { return KServiceGroup::Ptr(); // Not found } KServiceGroup::Ptr newGroup(createGroup(offset, deep)); // Check whether the dictionary was right. if (newGroup && (newGroup->relPath() != _name)) { // No it wasn't... - newGroup = 0; // Not found + newGroup = nullptr; // Not found } return newGroup; } KServiceGroup::Ptr KServiceGroupFactory::findBaseGroup(const QString &_baseGroupName, bool deep) { if (!m_baseGroupDict) { return KServiceGroup::Ptr(); // Error! } // Warning : this assumes we're NOT building a database // But since findBaseGroup isn't called in that case... // [ see KServiceTypeFactory for how to do it if needed ] int offset = m_baseGroupDict->find_string(_baseGroupName); if (!offset) { return KServiceGroup::Ptr(); // Not found } KServiceGroup::Ptr newGroup(createGroup(offset, deep)); // Check whether the dictionary was right. if (newGroup && (newGroup->baseGroupName() != _baseGroupName)) { // No it wasn't... - newGroup = 0; // Not found + newGroup = nullptr; // Not found } return newGroup; } KServiceGroup *KServiceGroupFactory::createGroup(int offset, bool deep) const { KSycocaType type; QDataStream *str = sycoca()->findEntry(offset, type); if (type != KST_KServiceGroup) { qWarning() << "KServiceGroupFactory: unexpected object entry in KSycoca database (type = " << int(type) << ")"; - return 0; + return nullptr; } KServiceGroup *newEntry = new KServiceGroup(*str, offset, deep); if (!newEntry->isValid()) { qWarning() << "KServiceGroupFactory: corrupt object in KSycoca database!"; delete newEntry; - newEntry = 0; + newEntry = nullptr; } return newEntry; } KServiceGroup *KServiceGroupFactory::createEntry(int offset) const { return createGroup(offset, true); } void KServiceGroupFactory::virtual_hook(int id, void *data) { KSycocaFactory::virtual_hook(id, data); } diff --git a/src/services/kservicegroupfactory_p.h b/src/services/kservicegroupfactory_p.h index dfd6581..fe46284 100644 --- a/src/services/kservicegroupfactory_p.h +++ b/src/services/kservicegroupfactory_p.h @@ -1,83 +1,83 @@ /* This file is part of the KDE project Copyright (C) 2000 Waldo Bastian This library is free software; you can redistribute it and/or modify it under the terms of the GNU Library General Public License version 2 as published by the Free Software Foundation. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Library General Public License for more details. You should have received a copy of the GNU Library General Public License along with this library; see the file COPYING.LIB. If not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #ifndef KSERVICEGROUPFACTORY_P_H #define KSERVICEGROUPFACTORY_P_H #include #include "kservicegroup.h" #include "ksycocafactory_p.h" #include class KSycoca; class KSycocaDict; /** * @internal * A sycoca factory for service groups (e.g. list of applications) * It loads the services from parsing directories (e.g. share/applications/) * * Exported for kbuildsycoca, but not installed. */ class KServiceGroupFactory : public KSycocaFactory { K_SYCOCAFACTORY(KST_KServiceGroupFactory) public: /** * Create factory */ KServiceGroupFactory(KSycoca *db); virtual ~KServiceGroupFactory(); /** * Construct a KServiceGroup from a config file. */ KSycocaEntry *createEntry(const QString &) const Q_DECL_OVERRIDE { assert(0); - return 0; + return nullptr; } /** * Find a group ( by desktop path, e.g. "Applications/Editors") */ virtual KServiceGroup::Ptr findGroupByDesktopPath(const QString &_name, bool deep = true); /** * Find a base group by name, e.g. "settings" */ KServiceGroup::Ptr findBaseGroup(const QString &_baseGroupName, bool deep = true); /** * @return the unique service group factory, creating it if necessary */ static KServiceGroupFactory *self(); protected: KServiceGroup *createGroup(int offset, bool deep) const; KServiceGroup *createEntry(int offset) const Q_DECL_OVERRIDE; KSycocaDict *m_baseGroupDict; int m_baseGroupDictOffset; protected: void virtual_hook(int id, void *data) Q_DECL_OVERRIDE; private: class KServiceGroupFactoryPrivate *d; }; #endif diff --git a/src/services/kserviceoffer.cpp b/src/services/kserviceoffer.cpp index a950fd7..745b7ec 100644 --- a/src/services/kserviceoffer.cpp +++ b/src/services/kserviceoffer.cpp @@ -1,131 +1,131 @@ /* This file is part of the KDE libraries Copyright (C) 2000 Torben Weis Copyright (C) 2006 David Faure This library is free software; you can redistribute it and/or modify it under the terms of the GNU Library General Public License version 2 as published by the Free Software Foundation. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Library General Public License for more details. You should have received a copy of the GNU Library General Public License along with this library; see the file COPYING.LIB. If not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #include "kserviceoffer.h" class KServiceOfferPrivate { public: KServiceOfferPrivate() : preference(-1), mimeTypeInheritanceLevel(0), bAllowAsDefault(false), - pService(0) + pService(nullptr) { } int preference; int mimeTypeInheritanceLevel; bool bAllowAsDefault; KService::Ptr pService; }; KServiceOffer::KServiceOffer() : d(new KServiceOfferPrivate) { } KServiceOffer::KServiceOffer(const KServiceOffer &_o) : d(new KServiceOfferPrivate) { *d = *_o.d; } KServiceOffer::KServiceOffer(const KService::Ptr &_service, int _pref, int mimeTypeInheritanceLevel, bool _default) : d(new KServiceOfferPrivate) { d->pService = _service; d->preference = _pref; d->mimeTypeInheritanceLevel = mimeTypeInheritanceLevel; d->bAllowAsDefault = _default; } KServiceOffer::~KServiceOffer() { delete d; } KServiceOffer &KServiceOffer::operator=(const KServiceOffer &rhs) { if (this == &rhs) { return *this; } *d = *rhs.d; return *this; } bool KServiceOffer::operator< (const KServiceOffer &_o) const { // First check mimetype inheritance level. // Direct mimetype association is preferred above association via parent mimetype // So, the smaller the better. if (d->mimeTypeInheritanceLevel != _o.d->mimeTypeInheritanceLevel) { return d->mimeTypeInheritanceLevel < _o.d->mimeTypeInheritanceLevel; } // Put offers allowed as default FIRST. if (_o.d->bAllowAsDefault && !d->bAllowAsDefault) { return false; // _o is default and not 'this'. } if (!_o.d->bAllowAsDefault && d->bAllowAsDefault) { return true; // 'this' is default but not _o. } // Both offers are allowed or not allowed as default // Finally, use preference to sort them // The bigger the better, but we want the better FIRST return _o.d->preference < d->preference; } bool KServiceOffer::allowAsDefault() const { return d->bAllowAsDefault; } int KServiceOffer::preference() const { return d->preference; } void KServiceOffer::setPreference(int p) { d->preference = p; } KService::Ptr KServiceOffer::service() const { return d->pService; } bool KServiceOffer::isValid() const { return d->preference >= 0; } void KServiceOffer::setMimeTypeInheritanceLevel(int level) { d->mimeTypeInheritanceLevel = level; } int KServiceOffer::mimeTypeInheritanceLevel() const { return d->mimeTypeInheritanceLevel; } diff --git a/src/services/kservicetypefactory.cpp b/src/services/kservicetypefactory.cpp index 8bd24ec..d175944 100644 --- a/src/services/kservicetypefactory.cpp +++ b/src/services/kservicetypefactory.cpp @@ -1,137 +1,137 @@ /* This file is part of the KDE libraries * Copyright (C) 1999 Waldo Bastian * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Library General Public * License version 2 as published by the Free Software Foundation; * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Library General Public License for more details. * * You should have received a copy of the GNU Library General Public License * along with this library; see the file COPYING.LIB. If not, write to * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, * Boston, MA 02110-1301, USA. **/ #include "kservicetypefactory_p.h" #include "ksycoca.h" #include "ksycocautils_p.h" #include "ksycocatype.h" #include "ksycocadict_p.h" #include "kservicetypeprofile.h" #include #include KServiceTypeFactory::KServiceTypeFactory(KSycoca *db) : KSycocaFactory(KST_KServiceTypeFactory, db) { if (!sycoca()->isBuilding()) { QDataStream *str = stream(); Q_ASSERT_X(str, "KServiceTypeFactory::KServiceTypeFactory()", "Could not open sycoca database, you must run kbuildsycoca first!"); if (str) { // Read Header qint32 n; (*str) >> n; if (n > 1024) { KSycoca::flagError(); } else { QString string; qint32 i; for (; n; --n) { *str >> string >> i; m_propertyTypeDict.insert(string, i); } } } } } KServiceTypeFactory::~KServiceTypeFactory() { if (!sycoca()->isBuilding()) { KServiceTypeProfile::clearCache(); } } KServiceType::Ptr KServiceTypeFactory::findServiceTypeByName(const QString &_name) { if (!sycocaDict()) { return KServiceType::Ptr(); // Error! } assert(!sycoca()->isBuilding()); int offset = sycocaDict()->find_string(_name); if (!offset) { return KServiceType::Ptr(); // Not found } KServiceType::Ptr newServiceType(createEntry(offset)); // Check whether the dictionary was right. if (newServiceType && (newServiceType->name() != _name)) { // No it wasn't... - newServiceType = 0; // Not found + newServiceType = nullptr; // Not found } return newServiceType; } QVariant::Type KServiceTypeFactory::findPropertyTypeByName(const QString &_name) { if (!sycocaDict()) { return QVariant::Invalid; // Error! } assert(!sycoca()->isBuilding()); return static_cast(m_propertyTypeDict.value(_name, QVariant::Invalid)); } KServiceType::List KServiceTypeFactory::allServiceTypes() { KServiceType::List result; const KSycocaEntry::List list = allEntries(); for (KSycocaEntry::List::ConstIterator it = list.begin(); it != list.end(); ++it) { if ((*it)->isType(KST_KServiceType)) { KServiceType::Ptr newServiceType(static_cast((*it).data())); result.append(newServiceType); } } return result; } QStringList KServiceTypeFactory::resourceDirs() { return KSycocaFactory::allDirectories(QStringLiteral("kservicetypes5")); } KServiceType *KServiceTypeFactory::createEntry(int offset) const { KSycocaType type; QDataStream *str = sycoca()->findEntry(offset, type); if (!str) { - return 0; + return nullptr; } if (type != KST_KServiceType) { qWarning() << "KServiceTypeFactory: unexpected object entry in KSycoca database (type=" << int(type) << ")"; - return 0; + return nullptr; } KServiceType *newEntry = new KServiceType(*str, offset); if (newEntry && !newEntry->isValid()) { qWarning() << "KServiceTypeFactory: corrupt object in KSycoca database!"; delete newEntry; - newEntry = 0; + newEntry = nullptr; } return newEntry; } void KServiceTypeFactory::virtual_hook(int id, void *data) { KSycocaFactory::virtual_hook(id, data); } diff --git a/src/services/kservicetypefactory_p.h b/src/services/kservicetypefactory_p.h index 4e50120..2fb78a7 100644 --- a/src/services/kservicetypefactory_p.h +++ b/src/services/kservicetypefactory_p.h @@ -1,100 +1,100 @@ /* This file is part of the KDE project Copyright (C) 1998, 1999 Torben Weis This library is free software; you can redistribute it and/or modify it under the terms of the GNU Library General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Library General Public License for more details. You should have received a copy of the GNU Library General Public License along with this library; see the file COPYING.LIB. If not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #ifndef KSERVICETYPEFACTORY_P_H #define KSERVICETYPEFACTORY_P_H #include #include #include "ksycocafactory_p.h" #include "kservicetype.h" class KSycoca; class KServiceType; /** * @internal * A sycoca factory for service types * It loads the service types from parsing directories (e.g. servicetypes/) * but can also create service types from data streams or single config files * @see KServiceType */ class KServiceTypeFactory : public KSycocaFactory { K_SYCOCAFACTORY(KST_KServiceTypeFactory) public: /** * Create factory */ KServiceTypeFactory(KSycoca *db); virtual ~KServiceTypeFactory(); /** * Not meant to be called at this level */ KSycocaEntry *createEntry(const QString &) const Q_DECL_OVERRIDE { assert(0); - return 0; + return nullptr; } /** * Find a service type in the database file (allocates it) * Overloaded by KBuildServiceTypeFactory to return a memory one. */ virtual KServiceType::Ptr findServiceTypeByName(const QString &_name); /** * Find a the property type of a named property. */ QVariant::Type findPropertyTypeByName(const QString &_name); /** * @return all servicetypes * Slow and memory consuming, avoid using */ KServiceType::List allServiceTypes(); /** * Returns the directories to watch for this factory. */ static QStringList resourceDirs(); /** * @return the unique servicetype factory, creating it if necessary */ static KServiceTypeFactory *self(); protected: KServiceType *createEntry(int offset) const Q_DECL_OVERRIDE; // protected for KBuildServiceTypeFactory QMap m_propertyTypeDict; protected: void virtual_hook(int id, void *data) Q_DECL_OVERRIDE; private: class KServiceTypeFactoryPrivate *d; }; #endif diff --git a/src/services/kservicetypeprofile.cpp b/src/services/kservicetypeprofile.cpp index 52411c3..d6d48c2 100644 --- a/src/services/kservicetypeprofile.cpp +++ b/src/services/kservicetypeprofile.cpp @@ -1,229 +1,229 @@ /* This file is part of the KDE libraries * Copyright (C) 1999 Torben Weis * Copyright (C) 2006 David Faure * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Library General Public * License version 2 as published by the Free Software Foundation; * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Library General Public License for more details. * * You should have received a copy of the GNU Library General Public License * along with this library; see the file COPYING.LIB. If not, write to * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, * Boston, MA 02110-1301, USA. */ #include "kservicetypeprofile.h" #include "kservicetypeprofile_p.h" #include "kservice.h" #include "kserviceoffer.h" #include "kservicetype.h" #include "ksycoca_p.h" #include #include #include #include #include // servicetype -> profile class KServiceTypeProfiles : public QHash { public: KServiceTypeProfiles() { m_parsed = false; ensureParsed(); } ~KServiceTypeProfiles() { clear(); } void clear() { QMutexLocker lock(&m_mutex); qDeleteAll(*this); QHash::clear(); m_parsed = false; } bool hasProfile(const QString &serviceType) { QMutexLocker lock(&m_mutex); ensureParsed(); return contains(serviceType); } void ensureParsed(); // mutex must be locked when calling this QMutex m_mutex; private: bool m_parsed; }; Q_GLOBAL_STATIC(KServiceTypeProfiles, s_serviceTypeProfiles) void KServiceTypeProfiles::ensureParsed() { if (m_parsed) { return; } m_parsed = true; // Read the service type profiles from servicetype_profilerc // See writeServiceTypeProfile for a description of the file format. // ### Since this new format names groups after servicetypes maybe we can even // avoid doing any init upfront, and just look up the group when asked... KConfig configFile(QStringLiteral("servicetype_profilerc"), KConfig::NoGlobals); const QStringList tmpList = configFile.groupList(); for (QStringList::const_iterator aIt = tmpList.begin(); aIt != tmpList.end(); ++aIt) { const QString type = *aIt; KConfigGroup config(&configFile, type); const int count = config.readEntry("NumberOfEntries", 0); - KServiceTypeProfileEntry *p = this->value(type, 0); + KServiceTypeProfileEntry *p = this->value(type, nullptr); if (!p) { p = new KServiceTypeProfileEntry(); this->insert(type, p); } for (int i = 0; i < count; ++i) { const QString num = QString::fromLatin1("Entry") + QString::number(i); const QString serviceId = config.readEntry(num + QLatin1String("_Service"), QString()); if (!serviceId.isEmpty()) { const int pref = config.readEntry(num + QLatin1String("_Preference"), 0); //qDebug() << "adding service " << serviceId << " to profile for " << type << " with preference " << pref; p->addService(serviceId, pref); } } } } //static void KServiceTypeProfile::clearCache() { if (s_serviceTypeProfiles.exists()) s_serviceTypeProfiles()->clear(); } /** * Returns the offers in the profile for the requested service type. * @param list list of offers (including initialPreference) * @param servicetype the service type * @return the weighted and sorted offer list * @internal used by KServiceTypeTrader */ namespace KServiceTypeProfile { KServiceOfferList sortServiceTypeOffers(const KServiceOfferList &list, const QString &servicetype); } KServiceOfferList KServiceTypeProfile::sortServiceTypeOffers(const KServiceOfferList &list, const QString &serviceType) { QMutexLocker lock(&s_serviceTypeProfiles()->m_mutex); s_serviceTypeProfiles()->ensureParsed(); - KServiceTypeProfileEntry *profile = s_serviceTypeProfiles()->value(serviceType, 0); + KServiceTypeProfileEntry *profile = s_serviceTypeProfiles()->value(serviceType, nullptr); KServiceOfferList offers; KServiceOfferList::const_iterator it = list.begin(); const KServiceOfferList::const_iterator end = list.end(); for (; it != end; ++it) { const KService::Ptr servPtr = (*it).service(); //qDebug() << "KServiceTypeProfile::offers considering " << servPtr->storageId(); // Look into the profile (if there's one), to find this service's preference. bool foundInProfile = false; if (profile) { QMap::ConstIterator it2 = profile->m_mapServices.constFind(servPtr->storageId()); if (it2 != profile->m_mapServices.constEnd()) { const int pref = it2.value(); //qDebug() << "found in mapServices pref=" << pref; if (pref > 0) { // 0 disables the service offers.append(KServiceOffer(servPtr, pref, 0, servPtr->allowAsDefault())); } foundInProfile = true; } } if (!foundInProfile) { // This offer isn't in the profile // This can be because we have no profile at all, or because the // services have been installed after the profile was written, // but it's also the case for any service that's neither App nor ReadOnlyPart, e.g. RenameDlg/Plugin //qDebug() << "not found in mapServices. Appending."; // If there's a profile, we use 0 as the preference to ensure new apps don't take over existing apps (which default to 1) offers.append(KServiceOffer(servPtr, profile ? 0 : (*it).preference(), 0, servPtr->allowAsDefault())); } } qStableSort(offers); //qDebug() << "KServiceTypeProfile::offers returning " << offers.count() << " offers"; return offers; } bool KServiceTypeProfile::hasProfile(const QString &serviceType) { return s_serviceTypeProfiles()->hasProfile(serviceType); } void KServiceTypeProfile::writeServiceTypeProfile(const QString &serviceType, const KService::List &services, const KService::List &disabledServices) { /* * [ServiceType] * NumEntries=3 * Entry0_Service=serv.desktop * Entry0_Preference=10 * Entry1_Service=otherserv.desktop * Entry1_Preference=5 * Entry2_Service=broken_service.desktop * Entry2_Preference=0 */ KConfig configFile(QStringLiteral("servicetype_profilerc"), KConfig::SimpleConfig); configFile.deleteGroup(serviceType); KConfigGroup config(&configFile, serviceType); const int count = services.count(); config.writeEntry("NumberOfEntries", count + disabledServices.count()); KService::List::ConstIterator servit = services.begin(); int i = 0; for (; servit != services.end(); ++servit, ++i) { if (*servit) { const QString num = QString::fromLatin1("Entry") + QString::number(i); config.writeEntry(num + QLatin1String("_Service"), (*servit)->storageId()); config.writeEntry(num + QLatin1String("_Preference"), count - i); } } servit = disabledServices.begin(); for (; servit != disabledServices.end(); ++servit, ++i) { if (*servit) { const QString num = QString::fromLatin1("Entry") + QString::number(i); config.writeEntry(num + QLatin1String("_Service"), (*servit)->storageId()); config.writeEntry(num + QLatin1String("_Preference"), 0); } } configFile.sync(); // Drop the whole cache... clearCache(); } void KServiceTypeProfile::deleteServiceTypeProfile(const QString &serviceType) { KConfig config(QStringLiteral("servicetype_profilerc"), KConfig::SimpleConfig); config.deleteGroup(serviceType); config.sync(); // Not threadsafe, but well the whole idea of using this method isn't // threadsafe in the first place. if (s_serviceTypeProfiles.exists()) { delete s_serviceTypeProfiles()->take(serviceType); } } diff --git a/src/services/kservicetypetrader.cpp b/src/services/kservicetypetrader.cpp index a56c239..65be055 100644 --- a/src/services/kservicetypetrader.cpp +++ b/src/services/kservicetypetrader.cpp @@ -1,190 +1,190 @@ /* This file is part of the KDE libraries Copyright (C) 2000 Torben Weis Copyright (C) 2006 David Faure This library is free software; you can redistribute it and/or modify it under the terms of the GNU Library General Public License version 2 as published by the Free Software Foundation. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Library General Public License for more details. You should have received a copy of the GNU Library General Public License along with this library; see the file COPYING.LIB. If not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #include "kservicetypetrader.h" #include "ksycoca.h" #include "ksycoca_p.h" #include "ktraderparsetree_p.h" #include #include "kservicetype.h" #include "kservicetypefactory_p.h" #include "kservicefactory_p.h" #include using namespace KTraderParse; // -------------------------------------------------- namespace KServiceTypeProfile { KServiceOfferList sortServiceTypeOffers(const KServiceOfferList &list, const QString &servicetype); } class KServiceTypeTraderSingleton { public: KServiceTypeTrader instance; }; Q_GLOBAL_STATIC(KServiceTypeTraderSingleton, s_globalServiceTypeTrader) KServiceTypeTrader *KServiceTypeTrader::self() { return &s_globalServiceTypeTrader()->instance; } KServiceTypeTrader::KServiceTypeTrader() - : d(0) + : d(nullptr) { } KServiceTypeTrader::~KServiceTypeTrader() { } // shared with KMimeTypeTrader void KServiceTypeTrader::applyConstraints(KService::List &lst, const QString &constraint) { if (lst.isEmpty() || constraint.isEmpty()) { return; } const ParseTreeBase::Ptr constr = parseConstraints(constraint); // for ownership const ParseTreeBase *pConstraintTree = constr.data(); // for speed if (!constr) { // parse error lst.clear(); } else { // Find all services matching the constraint // and remove the other ones KService::List::iterator it = lst.begin(); while (it != lst.end()) { if (matchConstraint(pConstraintTree, (*it), lst) != 1) { it = lst.erase(it); } else { ++it; } } } } #if 0 static void dumpOfferList(const KServiceOfferList &offers) { // qDebug() << "Sorted list:"; OfferList::Iterator itOff = offers.begin(); for (; itOff != offers.end(); ++itOff) // qDebug() << (*itOff).service()->name() << " allow-as-default=" << (*itOff).allowAsDefault() << " preference=" << (*itOff).preference(); } #endif KServiceOfferList KServiceTypeTrader::weightedOffers(const QString &serviceType) // static, internal { //qDebug() << "KServiceTypeTrader::weightedOffers( " << serviceType << " )"; KSycoca::self()->ensureCacheValid(); KServiceType::Ptr servTypePtr = KSycocaPrivate::self()->serviceTypeFactory()->findServiceTypeByName(serviceType); if (!servTypePtr) { qWarning() << "KServiceTypeTrader: serviceType" << serviceType << "not found"; return KServiceOfferList(); } if (servTypePtr->serviceOffersOffset() == -1) { // no offers in ksycoca return KServiceOfferList(); } // First, get all offers known to ksycoca. const KServiceOfferList services = KSycocaPrivate::self()->serviceFactory()->offers(servTypePtr->offset(), servTypePtr->serviceOffersOffset()); const KServiceOfferList offers = KServiceTypeProfile::sortServiceTypeOffers(services, serviceType); //qDebug() << "Found profile: " << offers.count() << " offers"; #if 0 dumpOfferList(offers); #endif return offers; } KService::List KServiceTypeTrader::defaultOffers(const QString &serviceType, const QString &constraint) const { KSycoca::self()->ensureCacheValid(); KServiceType::Ptr servTypePtr = KSycocaPrivate::self()->serviceTypeFactory()->findServiceTypeByName(serviceType); if (!servTypePtr) { qWarning() << "KServiceTypeTrader: serviceType" << serviceType << "not found"; return KService::List(); } if (servTypePtr->serviceOffersOffset() == -1) { return KService::List(); } KService::List lst = KSycocaPrivate::self()->serviceFactory()->serviceOffers(servTypePtr->offset(), servTypePtr->serviceOffersOffset()); applyConstraints(lst, constraint); //qDebug() << "query for serviceType " << serviceType << constraint // << " : returning " << lst.count() << " offers" << endl; return lst; } KService::List KServiceTypeTrader::query(const QString &serviceType, const QString &constraint) const { if (!KServiceTypeProfile::hasProfile(serviceType)) { // Fast path: skip the profile stuff if there's none (to avoid kservice->serviceoffer->kservice) // The ordering according to initial preferences is done by kbuildsycoca return defaultOffers(serviceType, constraint); } KService::List lst; // Get all services of this service type. const KServiceOfferList offers = weightedOffers(serviceType); // Now extract only the services; the weighting was only used for sorting. KServiceOfferList::const_iterator itOff = offers.begin(); for (; itOff != offers.end(); ++itOff) { lst.append((*itOff).service()); } applyConstraints(lst, constraint); //qDebug() << "query for serviceType " << serviceType << constraint // << " : returning " << lst.count() << " offers" << endl; return lst; } KService::Ptr KServiceTypeTrader::preferredService(const QString &serviceType) const { const KServiceOfferList offers = weightedOffers(serviceType); KServiceOfferList::const_iterator itOff = offers.begin(); // Look for the first one that is allowed as default. // Since the allowed-as-default are first anyway, we only have // to look at the first one to know. if (itOff != offers.end() && (*itOff).allowAsDefault()) { return (*itOff).service(); } //qDebug() << "No offers, or none allowed as default"; return KService::Ptr(); } diff --git a/src/services/kservicetypetrader.h b/src/services/kservicetypetrader.h index 56994d8..483f690 100644 --- a/src/services/kservicetypetrader.h +++ b/src/services/kservicetypetrader.h @@ -1,223 +1,223 @@ /* This file is part of the KDE libraries Copyright (C) 2000 Torben Weis Copyright (C) 2006 David Faure This library is free software; you can redistribute it and/or modify it under the terms of the GNU Library General Public License version 2 as published by the Free Software Foundation. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Library General Public License for more details. You should have received a copy of the GNU Library General Public License along with this library; see the file COPYING.LIB. If not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #ifndef __kservicetypetrader_h__ #define __kservicetypetrader_h__ #include "kservice.h" class KServiceOffer; typedef QList KServiceOfferList; class KServiceTypeTraderPrivate; /** * KDE's trader interface (similar to the CORBA Trader), which provides a way * to query the KDE infrastructure for specific applications or components. * * Basically, KServiceTypeTrader provides a way for an application to query * all KDE services (that is, applications, components, plugins) that match * a specific set of requirements. This allows to find specific services * at run-time without having to hard-code their names and/or paths. * * For anything relating to mimetypes (type of files), ignore KServiceTypeTrader * and use KMimeTypeTrader instead. * * \par Example * * If you want to find all plugins for your application, * you would define a KMyApp/Plugin servicetype, and then you can query * the trader for it: * \code * KService::List offers = * KServiceTypeTrader::self()->query("KMyApp/Plugin"); * \endcode * * You can add a constraint in the "trader query language". For instance: * \code * KServiceTypeTrader::self()->query("KMyApp/Plugin", * "[X-KMyApp-InterfaceVersion] > 15"); * \endcode * * Please note that when including property names containing arithmetic operators like - or +, then you have * to put brackets around the property name, in order to correctly separate arithmetic operations from * the name. So for example a constraint expression like * \code * X-KMyApp-InterfaceVersion > 4 // wrong! * \endcode * needs to be written as * \code * [X-KMyApp-InterfaceVersion] > 4 * \endcode * otherwise it could also be interpreted as * Subtract the numeric value of the property "KMyApp" and "InterfaceVersion" from the * property "X" and make sure it is greater than 4.\n * Instead of the other meaning, make sure that the numeric value of "X-KMyApp-InterfaceVersion" is * greater than 4. * * @see KMimeTypeTrader, KService */ class KSERVICE_EXPORT KServiceTypeTrader { public: /** * Standard destructor */ ~KServiceTypeTrader(); /** * The main function in the KServiceTypeTrader class. * * It will return a list of services that match your * specifications. The only required parameter is the service * type. This is something like 'text/plain' or 'text/html'. The * constraint parameter is used to limit the possible choices * returned based on the constraints you give it. * * The @p constraint language is rather full. The most common * keywords are AND, OR, NOT, IN, and EXIST, all used in an * almost spoken-word form. An example is: * \code * (Type == 'Service') and (('KParts/ReadOnlyPart' in ServiceTypes) or (exist Exec)) * \endcode * * The keys used in the query (Type, ServiceType, Exec) are all * fields found in the .desktop files. * * @param servicetype A service type like 'KMyApp/Plugin' or 'KFilePlugin'. * @param constraint A constraint to limit the choices returned, QString() to * get all services of the given @p servicetype * * @return A list of services that satisfy the query * @see http://techbase.kde.org/Development/Tutorials/Services/Traders#The_KTrader_Query_Language */ KService::List query(const QString &servicetype, const QString &constraint = QString()) const; /** * Returns all offers associated with a given servicetype, IGNORING the * user preference. The sorting will be the one coming from the InitialPreference * in the .desktop files, and services disabled by the user will still be listed here. * This is used for "Revert to defaults" buttons in GUIs. */ KService::List defaultOffers(const QString &serviceType, const QString &constraint = QString()) const; /** * Returns the preferred service for @p serviceType. * * @param serviceType the service type (e.g. "KMyApp/Plugin") * @return the preferred service, or 0 if no service is available */ KService::Ptr preferredService(const QString &serviceType) const; /** * This is a static pointer to the KServiceTypeTrader singleton. * * You will need to use this to access the KServiceTypeTrader functionality since the * constructors are protected. * * @return Static KServiceTypeTrader instance */ static KServiceTypeTrader *self(); /** * Get a plugin from a trader query * * Example: * \code * KMyAppPlugin* plugin = KServiceTypeTrader::createInstanceFromQuery( serviceType, QString(), parentObject ); * if ( plugin ) { * .... * } * \endcode * * @param serviceType the type of service for which to find a plugin * @param constraint an optional constraint to pass to the trader (see KTrader) * @param parent the parent object for the part itself * @param args A list of arguments passed to the service component * @param error The string passed here will contain an error description. * @return A pointer to the newly created object or a null pointer if the * factory was unable to create an object of the given type. */ template static T *createInstanceFromQuery(const QString &serviceType, - const QString &constraint = QString(), QObject *parent = 0, - const QVariantList &args = QVariantList(), QString *error = 0) + const QString &constraint = QString(), QObject *parent = nullptr, + const QVariantList &args = QVariantList(), QString *error = nullptr) { return createInstanceFromQuery(serviceType, 0, parent, constraint, args, error); } /** * Get a plugin from a trader query * * This method works like * createInstanceFromQuery(const QString&, const QString&, QObject*, const QVariantList&, QString*), * but you can specify an additional parent widget. This is important for * a KPart, for example. * * @param serviceType the type of service for which to find a plugin * @param parentWidget the parent widget for the plugin * @param parent the parent object for the part itself * @param constraint an optional constraint to pass to the trader (see KTrader) * @param args A list of arguments passed to the service component * @param error The string passed here will contain an error description. * @return A pointer to the newly created object or a null pointer if the * factory was unable to create an object of the given type. */ template static T *createInstanceFromQuery(const QString &serviceType, QWidget *parentWidget, QObject *parent, const QString &constraint = QString(), - const QVariantList &args = QVariantList(), QString *error = 0) + const QVariantList &args = QVariantList(), QString *error = nullptr) { const KService::List offers = self()->query(serviceType, constraint); if (error) { error->clear(); } Q_FOREACH (const KService::Ptr &ptr, offers) { T *component = ptr->template createInstance(parentWidget, parent, args, error); if (component) { return component; } } if (error && error->isEmpty()) { *error = QCoreApplication::translate("", "No service matching the requirements was found"); } return 0; } /** * @internal (public for KMimeTypeTrader) */ static void applyConstraints(KService::List &lst, const QString &constraint); private: /** * @internal */ KServiceTypeTrader(); // disallow copy ctor and assignment operator KServiceTypeTrader(const KServiceTypeTrader &other); KServiceTypeTrader &operator=(const KServiceTypeTrader &rhs); static KServiceOfferList weightedOffers(const QString &serviceType); KServiceTypeTraderPrivate *const d; friend class KServiceTypeTraderSingleton; }; #endif diff --git a/src/services/ktraderparse.cpp b/src/services/ktraderparse.cpp index a38b260..25affe0 100644 --- a/src/services/ktraderparse.cpp +++ b/src/services/ktraderparse.cpp @@ -1,177 +1,177 @@ /* This file is part of the KDE project Copyright (C) 1998, 1999 Torben Weis This library is free software; you can redistribute it and/or modify it under the terms of the GNU Library General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Library General Public License for more details. You should have received a copy of the GNU Library General Public License along with this library; see the file COPYING.LIB. If not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ // TODO: Torben: On error free memory! (r932881 might serve as inspiration) extern "C" { #include "ktraderparse_p.h" void KTraderParse_mainParse(const char *_code); } #include "ktraderparsetree_p.h" #include #include #include #include namespace KTraderParse { struct ParsingData { ParseTreeBase::Ptr ptr; QByteArray buffer; }; } using namespace KTraderParse; Q_GLOBAL_STATIC(QThreadStorage, s_parsingData) ParseTreeBase::Ptr KTraderParse::parseConstraints(const QString &_constr) { ParsingData *data = new ParsingData(); s_parsingData()->setLocalData(data); data->buffer = _constr.toUtf8(); KTraderParse_mainParse(data->buffer.constData()); ParseTreeBase::Ptr ret = data->ptr; - s_parsingData()->setLocalData(0); + s_parsingData()->setLocalData(nullptr); return ret; } void KTraderParse_setParseTree(void *_ptr1) { ParsingData *data = s_parsingData()->localData(); data->ptr = static_cast(_ptr1); } void KTraderParse_error(const char *err) { qWarning() << "Parsing" << s_parsingData()->localData()->buffer << "gave:" << err; } void *KTraderParse_newOR(void *_ptr1, void *_ptr2) { return new ParseTreeOR(static_cast(_ptr1), static_cast(_ptr2)); } void *KTraderParse_newAND(void *_ptr1, void *_ptr2) { return new ParseTreeAND(static_cast(_ptr1), static_cast(_ptr2)); } void *KTraderParse_newCMP(void *_ptr1, void *_ptr2, int _i) { return new ParseTreeCMP(static_cast(_ptr1), static_cast(_ptr2), _i); } void *KTraderParse_newIN(void *_ptr1, void *_ptr2, int _cs) { return new ParseTreeIN(static_cast(_ptr1), static_cast(_ptr2), _cs == 1 ? Qt::CaseSensitive : Qt::CaseInsensitive); } void *KTraderParse_newSubstringIN(void *_ptr1, void *_ptr2, int _cs) { return new ParseTreeIN(static_cast(_ptr1), static_cast(_ptr2), _cs == 1 ? Qt::CaseSensitive : Qt::CaseInsensitive, true); } void *KTraderParse_newMATCH(void *_ptr1, void *_ptr2, int _cs) { return new ParseTreeMATCH(static_cast(_ptr1), static_cast(_ptr2), _cs == 1 ? Qt::CaseSensitive : Qt::CaseInsensitive); } void *KTraderParse_newCALC(void *_ptr1, void *_ptr2, int _i) { return new ParseTreeCALC(static_cast(_ptr1), static_cast(_ptr2), _i); } void *KTraderParse_newBRACKETS(void *_ptr1) { return new ParseTreeBRACKETS(static_cast(_ptr1)); } void *KTraderParse_newNOT(void *_ptr1) { return new ParseTreeNOT(static_cast(_ptr1)); } void *KTraderParse_newEXIST(char *_ptr1) { ParseTreeEXIST *t = new ParseTreeEXIST(_ptr1); free(_ptr1); return t; } void *KTraderParse_newID(char *_ptr1) { ParseTreeID *t = new ParseTreeID(_ptr1); free(_ptr1); return t; } void *KTraderParse_newSTRING(char *_ptr1) { ParseTreeSTRING *t = new ParseTreeSTRING(_ptr1); free(_ptr1); return t; } void *KTraderParse_newNUM(int _i) { return new ParseTreeNUM(_i); } void *KTraderParse_newFLOAT(float _f) { return new ParseTreeDOUBLE(_f); } void *KTraderParse_newBOOL(char _b) { return new ParseTreeBOOL(_b); } void *KTraderParse_newMAX2(char *_id) { ParseTreeMAX2 *t = new ParseTreeMAX2(_id); free(_id); return t; } void *KTraderParse_newMIN2(char *_id) { ParseTreeMIN2 *t = new ParseTreeMIN2(_id); free(_id); return t; } void KTraderParse_destroy(void *node) { ParsingData *data = s_parsingData()->localData(); ParseTreeBase *p = reinterpret_cast(node); if (p != data->ptr.data()) { delete p; } } diff --git a/src/services/ktraderparsetree_p.h b/src/services/ktraderparsetree_p.h index 6625083..f0ca89f 100644 --- a/src/services/ktraderparsetree_p.h +++ b/src/services/ktraderparsetree_p.h @@ -1,417 +1,417 @@ /* This file is part of the KDE project Copyright (C) 1998, 1999 Torben Weis This library is free software; you can redistribute it and/or modify it under the terms of the GNU Library General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Library General Public License for more details. You should have received a copy of the GNU Library General Public License along with this library; see the file COPYING.LIB. If not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #ifndef __ktrader_parse_tree_h__ #define __ktrader_parse_tree_h__ #include #include #include #include #include namespace KTraderParse { class ParseTreeBase; /** * @internal * @return 0 => Does not match * 1 => Does match * <0 => Error */ int matchConstraint(const ParseTreeBase *_tree, const KService::Ptr &, const KService::List &); int matchConstraintPlugin(const ParseTreeBase *_tree, KPluginInfo _info, const KPluginInfo::List &_list); /** * @internal */ struct KSERVICE_EXPORT PreferencesMaxima { PreferencesMaxima() : iMax(0), iMin(0), fMax(0), fMin(0) { } enum Type { PM_ERROR, PM_INVALID_INT, PM_INVALID_DOUBLE, PM_DOUBLE, PM_INT }; Type type; int iMax; int iMin; double fMax; double fMin; }; /** * @internal */ class ParseContext { public: /** * This is NOT a copy constructor. */ explicit ParseContext(const ParseContext *_ctx) : service(_ctx->service), info(_ctx->info), maxima(_ctx->maxima), offers(_ctx->offers), pluginOffers(_ctx->pluginOffers) {} ParseContext(const KService::Ptr &_service, const KService::List &_offers, QMap &_m) : service(_service), info(KPluginInfo()), maxima(_m), offers(_offers), pluginOffers(KPluginInfo::List()) {} ParseContext(KPluginInfo _info, const KPluginInfo::List &_offers, QMap &_m) - : service(0), info(_info), maxima(_m), offers(KService::List()), pluginOffers(_offers) {} + : service(nullptr), info(_info), maxima(_m), offers(KService::List()), pluginOffers(_offers) {} bool initMaxima(const QString &_prop); QVariant property(const QString &_key) const; enum Type { T_STRING = 1, T_DOUBLE = 2, T_NUM = 3, T_BOOL = 4, T_STR_SEQ = 5, T_SEQ = 6 }; QString str; int i; double f; bool b; QList seq; QStringList strSeq; Type type; KService::Ptr service; KPluginInfo info; QMap &maxima; KService::List offers; KPluginInfo::List pluginOffers; }; /** * @internal */ class ParseTreeBase : public QSharedData { public: typedef QExplicitlySharedDataPointer Ptr; ParseTreeBase() { } virtual ~ParseTreeBase(); virtual bool eval(ParseContext *_context) const = 0; }; ParseTreeBase::Ptr parseConstraints(const QString &_constr); /** * @internal */ class ParseTreeOR : public ParseTreeBase { public: ParseTreeOR(ParseTreeBase *_ptr1, ParseTreeBase *_ptr2) { m_pLeft = _ptr1; m_pRight = _ptr2; } bool eval(ParseContext *_context) const Q_DECL_OVERRIDE; protected: ParseTreeBase::Ptr m_pLeft; ParseTreeBase::Ptr m_pRight; }; /** * @internal */ class ParseTreeAND : public ParseTreeBase { public: ParseTreeAND(ParseTreeBase *_ptr1, ParseTreeBase *_ptr2) { m_pLeft = _ptr1; m_pRight = _ptr2; } bool eval(ParseContext *_context) const Q_DECL_OVERRIDE; protected: ParseTreeBase::Ptr m_pLeft; ParseTreeBase::Ptr m_pRight; }; /** * @internal */ class ParseTreeCMP : public ParseTreeBase { public: ParseTreeCMP(ParseTreeBase *_ptr1, ParseTreeBase *_ptr2, int _i) { m_pLeft = _ptr1; m_pRight = _ptr2; m_cmd = _i; } bool eval(ParseContext *_context) const Q_DECL_OVERRIDE; protected: ParseTreeBase::Ptr m_pLeft; ParseTreeBase::Ptr m_pRight; int m_cmd; }; /** * @internal */ class ParseTreeIN : public ParseTreeBase { public: ParseTreeIN(ParseTreeBase *ptr1, ParseTreeBase *ptr2, Qt::CaseSensitivity cs, bool substring = false) : m_pLeft(ptr1), m_pRight(ptr2), m_cs(cs), m_substring(substring) { } bool eval(ParseContext *_context) const Q_DECL_OVERRIDE; protected: ParseTreeBase::Ptr m_pLeft; ParseTreeBase::Ptr m_pRight; Qt::CaseSensitivity m_cs; bool m_substring; }; /** * @internal */ class ParseTreeMATCH : public ParseTreeBase { public: ParseTreeMATCH(ParseTreeBase *_ptr1, ParseTreeBase *_ptr2, Qt::CaseSensitivity cs) { m_pLeft = _ptr1; m_pRight = _ptr2; m_cs = cs; } bool eval(ParseContext *_context) const Q_DECL_OVERRIDE; protected: ParseTreeBase::Ptr m_pLeft; ParseTreeBase::Ptr m_pRight; Qt::CaseSensitivity m_cs; }; /** * @internal */ class ParseTreeCALC : public ParseTreeBase { public: ParseTreeCALC(ParseTreeBase *_ptr1, ParseTreeBase *_ptr2, int _i) { m_pLeft = _ptr1; m_pRight = _ptr2; m_cmd = _i; } bool eval(ParseContext *_context) const Q_DECL_OVERRIDE; protected: ParseTreeBase::Ptr m_pLeft; ParseTreeBase::Ptr m_pRight; int m_cmd; }; /** * @internal */ class ParseTreeBRACKETS : public ParseTreeBase { public: explicit ParseTreeBRACKETS(ParseTreeBase *_ptr) { m_pLeft = _ptr; } bool eval(ParseContext *_context) const Q_DECL_OVERRIDE; protected: ParseTreeBase::Ptr m_pLeft; }; /** * @internal */ class ParseTreeNOT : public ParseTreeBase { public: explicit ParseTreeNOT(ParseTreeBase *_ptr) { m_pLeft = _ptr; } bool eval(ParseContext *_context) const Q_DECL_OVERRIDE; protected: ParseTreeBase::Ptr m_pLeft; }; /** * @internal */ class ParseTreeEXIST : public ParseTreeBase { public: explicit ParseTreeEXIST(const char *_id) { m_id = QString::fromUtf8(_id); } bool eval(ParseContext *_context) const Q_DECL_OVERRIDE; protected: QString m_id; }; /** * @internal */ class ParseTreeID : public ParseTreeBase { public: explicit ParseTreeID(const char *arg) { m_str = QString::fromUtf8(arg); } bool eval(ParseContext *_context) const Q_DECL_OVERRIDE; protected: QString m_str; }; /** * @internal */ class ParseTreeSTRING : public ParseTreeBase { public: explicit ParseTreeSTRING(const char *arg) { m_str = QString::fromUtf8(arg); } bool eval(ParseContext *_context) const Q_DECL_OVERRIDE; protected: QString m_str; }; /** * @internal */ class ParseTreeNUM : public ParseTreeBase { public: explicit ParseTreeNUM(int arg) { m_int = arg; } bool eval(ParseContext *_context) const Q_DECL_OVERRIDE; protected: int m_int; }; /** * @internal */ class ParseTreeDOUBLE : public ParseTreeBase { public: explicit ParseTreeDOUBLE(double arg) { m_double = arg; } bool eval(ParseContext *_context) const Q_DECL_OVERRIDE; protected: double m_double; }; /** * @internal */ class ParseTreeBOOL : public ParseTreeBase { public: explicit ParseTreeBOOL(bool arg) { m_bool = arg; } bool eval(ParseContext *_context) const Q_DECL_OVERRIDE; protected: bool m_bool; }; /** * @internal */ class ParseTreeMAX2 : public ParseTreeBase { public: explicit ParseTreeMAX2(const char *_id) { m_strId = QString::fromUtf8(_id); } bool eval(ParseContext *_context) const Q_DECL_OVERRIDE; protected: QString m_strId; }; /** * @internal */ class ParseTreeMIN2 : public ParseTreeBase { public: explicit ParseTreeMIN2(const char *_id) { m_strId = QString::fromUtf8(_id); } bool eval(ParseContext *_context) const Q_DECL_OVERRIDE; protected: QString m_strId; }; } #endif diff --git a/src/sycoca/kbuildmimetypefactory.cpp b/src/sycoca/kbuildmimetypefactory.cpp index d615ef8..cf27122 100644 --- a/src/sycoca/kbuildmimetypefactory.cpp +++ b/src/sycoca/kbuildmimetypefactory.cpp @@ -1,107 +1,107 @@ /* This file is part of the KDE libraries * Copyright 1999-2007 David Faure * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Library General Public * License as published by the Free Software Foundation; either * version 2 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Library General Public License for more details. * * You should have received a copy of the GNU Library General Public License * along with this library; see the file COPYING.LIB. If not, write to * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, * Boston, MA 02110-1301, USA. */ #include "kbuildmimetypefactory_p.h" #include "ksycoca.h" #include "ksycocadict_p.h" #include "ksycocaresourcelist_p.h" #include #include #include #include KBuildMimeTypeFactory::KBuildMimeTypeFactory(KSycoca *db) : KMimeTypeFactory(db) { m_resourceList = new KSycocaResourceList; // We want all xml files under xdgdata/mime - but not mime/packages/*.xml m_resourceList->add("xdgdata-mime", QStringLiteral("mime"), QStringLiteral("*.xml")); } KBuildMimeTypeFactory::~KBuildMimeTypeFactory() { delete m_resourceList; } KSycocaEntry::List KBuildMimeTypeFactory::allEntries() const { assert(sycoca()->isBuilding()); return m_entryDict->values(); } KSycocaEntry *KBuildMimeTypeFactory::createEntry(const QString &file) const { // file=text/plain.xml -> name=plain.xml dirName=text Q_ASSERT(!file.startsWith(QLatin1String("mime/"))); const int pos = file.lastIndexOf('/'); if (pos == -1) { // huh? - return 0; + return nullptr; } const QStringRef dirName = file.leftRef(pos); if (dirName == QLatin1String("packages")) { // special subdir - return 0; + return nullptr; } const int dot = file.lastIndexOf('.'); if (dot == -1) { // huh? - return 0; + return nullptr; } const QString name = file.left(dot); //qDebug() << "Creating mimetype" << name << "from file" << file; MimeTypeEntry *e = new MimeTypeEntry(file, name); return e; } void KBuildMimeTypeFactory::saveHeader(QDataStream &str) { KSycocaFactory::saveHeader(str); } void KBuildMimeTypeFactory::save(QDataStream &str) { KSycocaFactory::save(str); str << qint32(0); const int endOfFactoryData = str.device()->pos(); // Update header (pass #3) saveHeader(str); // Seek to end. str.device()->seek(endOfFactoryData); } KMimeTypeFactory::MimeTypeEntry::Ptr KBuildMimeTypeFactory::createFakeMimeType(const QString &name) { const QString file = name; // hack KSycocaEntry::Ptr entry = m_entryDict->value(file); if (!entry) { MimeTypeEntry *e = new MimeTypeEntry(file, name); entry = e; } Q_ASSERT(entry && entry->isValid()); addEntry(entry); return KMimeTypeFactory::MimeTypeEntry::Ptr(static_cast(entry.data())); } diff --git a/src/sycoca/kbuildmimetypefactory_p.h b/src/sycoca/kbuildmimetypefactory_p.h index 8b87b96..a80791f 100644 --- a/src/sycoca/kbuildmimetypefactory_p.h +++ b/src/sycoca/kbuildmimetypefactory_p.h @@ -1,69 +1,69 @@ /* This file is part of the KDE project Copyright 1999-2007 David Faure This library is free software; you can redistribute it and/or modify it under the terms of the GNU Library General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Library General Public License for more details. You should have received a copy of the GNU Library General Public License along with this library; see the file COPYING.LIB. If not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #ifndef KBUILD_MIME_TYPE_FACTORY_H #define KBUILD_MIME_TYPE_FACTORY_H #include #include /** * Mime-type factory for building ksycoca * @internal */ class KBuildMimeTypeFactory : public KMimeTypeFactory { public: /** * Create factory */ KBuildMimeTypeFactory(KSycoca *db); virtual ~KBuildMimeTypeFactory(); KSycocaEntry::List allEntries() const Q_DECL_OVERRIDE; /** * Construct a KMimeType from a config file. */ KSycocaEntry *createEntry(const QString &file) const Q_DECL_OVERRIDE; MimeTypeEntry *createEntry(int) const Q_DECL_OVERRIDE { assert(0); - return 0L; + return nullptr; } KMimeTypeFactory::MimeTypeEntry::Ptr createFakeMimeType(const QString &name); /** * Write out mime type specific index files. */ void save(QDataStream &str) Q_DECL_OVERRIDE; /** * Write out header information * * Don't forget to call the parent first when you override * this function. */ void saveHeader(QDataStream &str) Q_DECL_OVERRIDE; }; #endif diff --git a/src/sycoca/kbuildservicefactory.cpp b/src/sycoca/kbuildservicefactory.cpp index df4af38..a3bc732 100644 --- a/src/sycoca/kbuildservicefactory.cpp +++ b/src/sycoca/kbuildservicefactory.cpp @@ -1,418 +1,418 @@ /* This file is part of the KDE libraries * Copyright (C) 1999, 2007 David Faure * 1999 Waldo Bastian * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Library General Public * License version 2 as published by the Free Software Foundation; * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Library General Public License for more details. * * You should have received a copy of the GNU Library General Public License * along with this library; see the file COPYING.LIB. If not, write to * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, * Boston, MA 02110-1301, USA. **/ #include "kbuildservicefactory_p.h" #include "kbuildservicegroupfactory_p.h" #include "kbuildmimetypefactory_p.h" #include "kservicetypefactory_p.h" #include "ksycoca.h" #include "ksycocadict_p.h" #include "ksycocaresourcelist_p.h" #include "kdesktopfile.h" #include "kservicetype.h" #include "sycocadebug.h" #include #include #include #include #include #include KBuildServiceFactory::KBuildServiceFactory(KServiceTypeFactory *serviceTypeFactory, KBuildMimeTypeFactory *mimeTypeFactory, KBuildServiceGroupFactory *serviceGroupFactory) : KServiceFactory(serviceTypeFactory->sycoca()), m_nameMemoryHash(), m_relNameMemoryHash(), m_menuIdMemoryHash(), m_dupeDict(), m_serviceTypeFactory(serviceTypeFactory), m_mimeTypeFactory(mimeTypeFactory), m_serviceGroupFactory(serviceGroupFactory) { m_resourceList = new KSycocaResourceList(); // We directly care about services desktop files. // All the application desktop files are parsed on demand from the vfolder menu code. m_resourceList->add("services", QStringLiteral("kservices5"), QStringLiteral("*.desktop")); m_nameDict = new KSycocaDict(); m_relNameDict = new KSycocaDict(); m_menuIdDict = new KSycocaDict(); } KBuildServiceFactory::~KBuildServiceFactory() { delete m_resourceList; } KService::Ptr KBuildServiceFactory::findServiceByDesktopName(const QString &name) { return m_nameMemoryHash.value(name); } KService::Ptr KBuildServiceFactory::findServiceByDesktopPath(const QString &name) { return m_relNameMemoryHash.value(name); } KService::Ptr KBuildServiceFactory::findServiceByMenuId(const QString &menuId) { return m_menuIdMemoryHash.value(menuId); } KSycocaEntry *KBuildServiceFactory::createEntry(const QString &file) const { Q_ASSERT(!file.startsWith(QLatin1String("kservices5/"))); // we add this ourselves, below const QStringRef name = file.midRef(file.lastIndexOf('/') + 1); // Is it a .desktop file? if (name.endsWith(QLatin1String(".desktop"))) { //qDebug() << file; KService *serv; if (QDir::isAbsolutePath(file)) { // vfolder sends us full paths for applications serv = new KService(file); } else { // we get relative paths for services KDesktopFile desktopFile(QStandardPaths::GenericDataLocation, QStringLiteral("kservices5/") + file); // Note that the second arg below MUST be 'file', unchanged. // If the entry path doesn't match the 'file' parameter to createEntry, reusing old entries // (via time dict, which uses the entry path as key) cannot work. serv = new KService(&desktopFile, file); } //qDebug() << "Creating KService from" << file << "entryPath=" << serv->entryPath(); // Note that the menuId will be set by the vfolder_menu.cpp code just after // createEntry returns. if (serv->isValid() && !serv->isDeleted()) { //qDebug() << "Creating KService from" << file << "entryPath=" << serv->entryPath() << "storageId=" << serv->storageId(); return serv; } else { if (!serv->isDeleted()) { qCWarning(SYCOCA) << "Invalid Service : " << file; } delete serv; - return 0; + return nullptr; } } // TODO else if a Windows application, new KService(name, exec, icon) - return 0; + return nullptr; } void KBuildServiceFactory::saveHeader(QDataStream &str) { KSycocaFactory::saveHeader(str); str << qint32(m_nameDictOffset); str << qint32(m_relNameDictOffset); str << qint32(m_offerListOffset); str << qint32(m_menuIdDictOffset); } void KBuildServiceFactory::save(QDataStream &str) { KSycocaFactory::save(str); m_nameDictOffset = str.device()->pos(); m_nameDict->save(str); m_relNameDictOffset = str.device()->pos(); m_relNameDict->save(str); saveOfferList(str); m_menuIdDictOffset = str.device()->pos(); m_menuIdDict->save(str); int endOfFactoryData = str.device()->pos(); // Update header (pass #3) saveHeader(str); // Seek to end. str.device()->seek(endOfFactoryData); } void KBuildServiceFactory::collectInheritedServices() { // For each mimetype, go up the parent-mimetype chains and collect offers. // For "removed associations" to work, we can't just grab everything from all parents. // We need to process parents before children, hence the recursive call in // collectInheritedServices(mime) and the QSet to process a given parent only once. QSet visitedMimes; Q_FOREACH (const QString &mimeType, m_mimeTypeFactory->allMimeTypes()) { collectInheritedServices(mimeType, visitedMimes); } } void KBuildServiceFactory::collectInheritedServices(const QString &mimeTypeName, QSet &visitedMimes) { if (visitedMimes.contains(mimeTypeName)) { return; } visitedMimes.insert(mimeTypeName); // With multiple inheritance, the "mimeTypeInheritanceLevel" isn't exactly // correct (it should only be increased when going up a level, not when iterating // through the multiple parents at a given level). I don't think we care, though. int mimeTypeInheritanceLevel = 0; QMimeDatabase db; QMimeType qmime = db.mimeTypeForName(mimeTypeName); Q_FOREACH (QString parentMimeType, qmime.parentMimeTypes()) { // Workaround issue in shared-mime-info and/or Qt, which sometimes return an alias as parent parentMimeType = db.mimeTypeForName(parentMimeType).name(); collectInheritedServices(parentMimeType, visitedMimes); ++mimeTypeInheritanceLevel; const QList &offers = m_offerHash.offersFor(parentMimeType); QList::const_iterator itserv = offers.begin(); const QList::const_iterator endserv = offers.end(); for (; itserv != endserv; ++itserv) { if (!m_offerHash.hasRemovedOffer(mimeTypeName, (*itserv).service())) { KServiceOffer offer(*itserv); offer.setMimeTypeInheritanceLevel(mimeTypeInheritanceLevel); //qDebug() << "INHERITANCE: Adding service" << (*itserv).service()->entryPath() << "to" << mimeTypeName << "mimeTypeInheritanceLevel=" << mimeTypeInheritanceLevel; m_offerHash.addServiceOffer(mimeTypeName, offer); } } } } void KBuildServiceFactory::postProcessServices() { // By doing all this here rather than in addEntry (and removing when replacing // with local override), we only do it for the final applications. // Note that this also affects resolution of the by-desktop-name lookup, // as name resolution is only performed *after* all the duplicates (based on // storage ID) have been removed. // For every service... KSycocaEntryDict::const_iterator itserv = m_entryDict->constBegin(); const KSycocaEntryDict::const_iterator endserv = m_entryDict->constEnd(); for (; itserv != endserv; ++itserv) { KSycocaEntry::Ptr entry = *itserv; KService::Ptr service(static_cast(entry.data())); if (!service->isDeleted()) { const QString parent = service->parentApp(); if (!parent.isEmpty()) { m_serviceGroupFactory->addNewChild(parent, KSycocaEntry::Ptr(service)); } } const QString name = service->desktopEntryName(); KService::Ptr dup = m_nameMemoryHash.value(name); if (dup) { // The rule is that searching for the desktop name "foo" should find // the desktop file with the storage id "foo.desktop" before it // finds "bar/foo.desktop" (or "bar-foo.desktop"). // "bar/foo.desktop" and "baz/foo.desktop" are arbitrarily ordered // (in practice, the one later in the alphabet wins). if (dup->storageId().endsWith(service->storageId())) { // allow dup to be overridden m_nameDict->remove(name); - dup = 0; + dup = nullptr; } } if (!dup) { m_nameDict->add(name, entry); m_nameMemoryHash.insert(name, service); } const QString relName = service->entryPath(); //qDebug() << "adding service" << service.data() << "isApp=" << service->isApplication() << "menuId=" << service->menuId() << "name=" << name << "relName=" << relName; m_relNameDict->add(relName, entry); m_relNameMemoryHash.insert(relName, service); // for KMimeAssociations const QString menuId = service->menuId(); if (!menuId.isEmpty()) { // empty for services, non-empty for applications m_menuIdDict->add(menuId, entry); m_menuIdMemoryHash.insert(menuId, service); // for KMimeAssociations } } populateServiceTypes(); } void KBuildServiceFactory::populateServiceTypes() { QMimeDatabase db; // For every service... KSycocaEntryDict::const_iterator itserv = m_entryDict->constBegin(); const KSycocaEntryDict::const_iterator endserv = m_entryDict->constEnd(); for (; itserv != endserv; ++itserv) { KService::Ptr service(static_cast((*itserv).data())); QVector serviceTypeList = service->_k_accessServiceTypes(); //bool hasAllAll = false; //bool hasAllFiles = false; // Add this service to all its servicetypes (and their parents) and to all its mimetypes for (int i = 0; i < serviceTypeList.count() /*don't cache it, it can change during iteration!*/; ++i) { const QString stName = serviceTypeList[i].serviceType; // It could be a servicetype or a mimetype. KServiceType::Ptr serviceType = m_serviceTypeFactory->findServiceTypeByName(stName); if (serviceType) { const int preference = serviceTypeList[i].preference; const QString parent = serviceType->parentServiceType(); if (!parent.isEmpty()) { serviceTypeList.append(KService::ServiceTypeAndPreference(preference, parent)); } //qDebug() << "Adding service" << service->entryPath() << "to" << serviceType->name() << "pref=" << preference; m_offerHash.addServiceOffer(stName, KServiceOffer(service, preference, 0, service->allowAsDefault())); } else { KServiceOffer offer(service, serviceTypeList[i].preference, 0, service->allowAsDefault()); QMimeType mime = db.mimeTypeForName(stName); if (!mime.isValid()) { if (stName.startsWith(QLatin1String("x-scheme-handler/"))) { // Create those on demand m_mimeTypeFactory->createFakeMimeType(stName); m_offerHash.addServiceOffer(stName, offer); } else { //qDebug() << service->entryPath() << "specifies undefined mimetype/servicetype" << stName; // technically we could call addServiceOffer here, 'mime' isn't used. But it // would be useless, since we have no mimetype entry where to write the offers offset. continue; } } else { bool shouldAdd = true; foreach (const QString &otherType, service->serviceTypes()) { // Skip derived types if the base class is listed (#321706) if (stName != otherType && mime.inherits(otherType)) { // But don't skip aliases (they got resolved into mime->name() already, but don't let two aliases cancel out) if (db.mimeTypeForName(otherType).name() != mime.name()) { //qDebug() << "Skipping" << mime->name() << "because of" << otherType << "(canonical" << KMimeTypeRepository::self()->canonicalName(otherType) << ") while parsing" << service->entryPath(); shouldAdd = false; } } } if (shouldAdd) { //qDebug() << "Adding service" << service->entryPath() << "to" << mime->name(); m_offerHash.addServiceOffer(mime.name(), offer); // mime->name so that we resolve aliases } } } } } // Read user preferences (added/removed associations) and add/remove serviceoffers to m_offerHash KMimeAssociations mimeAssociations(m_offerHash, this); mimeAssociations.parseAllMimeAppsList(); // Now for each mimetype, collect services from parent mimetypes collectInheritedServices(); // Now collect the offsets into the (future) offer list // The loops look very much like the ones in saveOfferList obviously. int offersOffset = 0; const int offerEntrySize = sizeof(qint32) * 4; // four qint32s, see saveOfferList. const auto &offerHash = m_offerHash.serviceTypeData(); auto it = offerHash.constBegin(); const auto end = offerHash.constEnd(); for ( ; it != end ; ++it ) { const QString stName = it.key(); const ServiceTypeOffersData offersData = it.value(); const int numOffers = offersData.offers.count(); KServiceType::Ptr serviceType = m_serviceTypeFactory->findServiceTypeByName(stName); if (serviceType) { serviceType->setServiceOffersOffset(offersOffset); offersOffset += offerEntrySize * numOffers; } else { KMimeTypeFactory::MimeTypeEntry::Ptr entry = m_mimeTypeFactory->findMimeTypeEntryByName(stName); if (entry) { entry->setServiceOffersOffset(offersOffset); offersOffset += offerEntrySize * numOffers; } else if (stName.startsWith(QLatin1String("x-scheme-handler/"))) { // Create those on demand entry = m_mimeTypeFactory->createFakeMimeType(stName); entry->setServiceOffersOffset(offersOffset); offersOffset += offerEntrySize * numOffers; } else { qWarning() << "Not found:" << stName; } } } } void KBuildServiceFactory::saveOfferList(QDataStream &str) { m_offerListOffset = str.device()->pos(); //qDebug() << "Saving offer list at offset" << m_offerListOffset; const auto &offerHash = m_offerHash.serviceTypeData(); auto it = offerHash.constBegin(); const auto end = offerHash.constEnd(); for ( ; it != end ; ++it ) { const QString stName = it.key(); const ServiceTypeOffersData offersData = it.value(); QList offers = offersData.offers; qStableSort(offers); // by initial preference int offset = -1; KServiceType::Ptr serviceType = m_serviceTypeFactory->findServiceTypeByName(stName); if (serviceType) { offset = serviceType->offset(); } else { KMimeTypeFactory::MimeTypeEntry::Ptr entry = m_mimeTypeFactory->findMimeTypeEntryByName(stName); if (entry) { offset = entry->offset(); //Q_ASSERT(str.device()->pos() == entry->serviceOffersOffset() + m_offerListOffset); } } if (offset == -1) { qDebug() << "Didn't find servicetype or mimetype" << stName; continue; } for (QList::const_iterator it2 = offers.constBegin(); it2 != offers.constEnd(); ++it2) { //qDebug() << stName << ":" << "writing offer" << (*it2).service()->desktopEntryName() << offset << (*it2).service()->offset() << "in sycoca at pos" << str.device()->pos(); Q_ASSERT((*it2).service()->offset() != 0); str << qint32(offset); str << qint32((*it2).service()->offset()); str << qint32((*it2).preference()); str << qint32((*it2).mimeTypeInheritanceLevel()); // update offerEntrySize in populateServiceTypes if you add/remove something here } } str << qint32(0); // End of list marker (0) } void KBuildServiceFactory::addEntry(const KSycocaEntry::Ptr &newEntry) { Q_ASSERT(newEntry); if (m_dupeDict.contains(newEntry)) { return; } const KService::Ptr service(static_cast(newEntry.data())); m_dupeDict.insert(newEntry); KSycocaFactory::addEntry(newEntry); } diff --git a/src/sycoca/kbuildservicefactory_p.h b/src/sycoca/kbuildservicefactory_p.h index d1086f8..b99b8a9 100644 --- a/src/sycoca/kbuildservicefactory_p.h +++ b/src/sycoca/kbuildservicefactory_p.h @@ -1,105 +1,105 @@ /* This file is part of the KDE project Copyright (C) 1999, 2007 David Faure 1999 Waldo Bastian This library is free software; you can redistribute it and/or modify it under the terms of the GNU Library General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Library General Public License for more details. You should have received a copy of the GNU Library General Public License along with this library; see the file COPYING.LIB. If not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #ifndef KBUILD_SERVICE_FACTORY_H #define KBUILD_SERVICE_FACTORY_H #include #include "kmimeassociations_p.h" #include // We export the services to the service group factory! class KBuildServiceGroupFactory; class KBuildMimeTypeFactory; class KServiceTypeFactory; /** * Service factory for building ksycoca * @internal */ class KBuildServiceFactory : public KServiceFactory { public: /** * Create factory */ KBuildServiceFactory(KServiceTypeFactory *serviceTypeFactory, KBuildMimeTypeFactory *mimeTypeFactory, KBuildServiceGroupFactory *serviceGroupFactory); virtual ~KBuildServiceFactory(); /// Reimplemented from KServiceFactory KService::Ptr findServiceByDesktopName(const QString &name) Q_DECL_OVERRIDE; /// Reimplemented from KServiceFactory KService::Ptr findServiceByDesktopPath(const QString &name) Q_DECL_OVERRIDE; /// Reimplemented from KServiceFactory KService::Ptr findServiceByMenuId(const QString &menuId) Q_DECL_OVERRIDE; /** * Construct a KService from a config file. */ KSycocaEntry *createEntry(const QString &file) const Q_DECL_OVERRIDE; KService *createEntry(int) const Q_DECL_OVERRIDE { assert(0); - return 0; + return nullptr; } /** * Add a new entry. */ void addEntry(const KSycocaEntry::Ptr &newEntry) Q_DECL_OVERRIDE; /** * Write out service specific index files. */ void save(QDataStream &str) Q_DECL_OVERRIDE; /** * Write out header information * * Don't forget to call the parent first when you override * this function. */ void saveHeader(QDataStream &str) Q_DECL_OVERRIDE; void postProcessServices(); private: void populateServiceTypes(); void saveOfferList(QDataStream &str); void collectInheritedServices(); void collectInheritedServices(const QString &mime, QSet &visitedMimes); QHash m_nameMemoryHash; // m_nameDict is not useable while building ksycoca QHash m_relNameMemoryHash; // m_relNameDict is not useable while building ksycoca QHash m_menuIdMemoryHash; // m_menuIdDict is not useable while building ksycoca QSet m_dupeDict; KOfferHash m_offerHash; KServiceTypeFactory *m_serviceTypeFactory; KBuildMimeTypeFactory *m_mimeTypeFactory; KBuildServiceGroupFactory *m_serviceGroupFactory; }; #endif diff --git a/src/sycoca/kbuildservicegroupfactory.cpp b/src/sycoca/kbuildservicegroupfactory.cpp index 83ed532..486a851 100644 --- a/src/sycoca/kbuildservicegroupfactory.cpp +++ b/src/sycoca/kbuildservicegroupfactory.cpp @@ -1,175 +1,175 @@ /* This file is part of the KDE libraries * Copyright (C) 2000 Waldo Bastian * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Library General Public * License version 2 as published by the Free Software Foundation. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Library General Public License for more details. * * You should have received a copy of the GNU Library General Public License * along with this library; see the file COPYING.LIB. If not, write to * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, * Boston, MA 02110-1301, USA. **/ #include "kbuildservicegroupfactory_p.h" #include "ksycoca.h" #include "ksycocadict_p.h" #include "ksycocaresourcelist_p.h" #include #include "sycocadebug.h" #include #include #include KBuildServiceGroupFactory::KBuildServiceGroupFactory(KSycoca *db) : KServiceGroupFactory(db) { m_resourceList = new KSycocaResourceList; // m_resourceList->add( "apps", "*.directory" ); m_baseGroupDict = new KSycocaDict(); } KBuildServiceGroupFactory::~KBuildServiceGroupFactory() { delete m_resourceList; } KServiceGroup *KBuildServiceGroupFactory::createEntry(const QString &) const { // Unused qCWarning(SYCOCA) << "called!"; - return 0; + return nullptr; } void KBuildServiceGroupFactory::addNewEntryTo(const QString &menuName, const KService::Ptr &newEntry) { KSycocaEntry::Ptr ptr = m_entryDict->value(menuName); KServiceGroup::Ptr entry; if (ptr && ptr->isType(KST_KServiceGroup)) { entry = KServiceGroup::Ptr(static_cast(ptr.data())); } if (!entry) { qCWarning(SYCOCA) << "( " << menuName << ", " << newEntry->name() << " ): menu does not exists!"; return; } entry->addEntry(KSycocaEntry::Ptr(newEntry)); } KServiceGroup::Ptr KBuildServiceGroupFactory::addNew(const QString &menuName, const QString &file, KServiceGroup::Ptr entry, bool isDeleted) { KSycocaEntry::Ptr ptr = m_entryDict->value(menuName); if (ptr) { qCWarning(SYCOCA) << "( " << menuName << ", " << file << " ): menu already exists!"; return KServiceGroup::Ptr(static_cast(ptr.data())); } // Create new group entry if (!entry) { entry = new KServiceGroup(file, menuName); } entry->d_func()->m_childCount = -1; // Recalculate addEntry(KSycocaEntry::Ptr(entry)); if (menuName != "/") { // Make sure parent dir exists. QString parent = menuName.left(menuName.length() - 1); int i = parent.lastIndexOf('/'); if (i > 0) { parent = parent.left(i + 1); } else { parent = '/'; } KServiceGroup::Ptr parentEntry; ptr = m_entryDict->value(parent); if (ptr && ptr->isType(KST_KServiceGroup)) { parentEntry = KServiceGroup::Ptr(static_cast(ptr.data())); } if (!parentEntry) { qCWarning(SYCOCA) << "( " << menuName << ", " << file << " ): parent menu does not exist!"; } else { if (!isDeleted && !entry->isDeleted()) { parentEntry->addEntry(KSycocaEntry::Ptr(entry)); } } } return entry; } void KBuildServiceGroupFactory::addNewChild(const QString &parent, const KSycocaEntry::Ptr &newEntry) { QString name = "#parent#" + parent; KServiceGroup::Ptr entry; KSycocaEntry::Ptr ptr = m_entryDict->value(name); if (ptr && ptr->isType(KST_KServiceGroup)) { entry = KServiceGroup::Ptr(static_cast(ptr.data())); } if (!entry) { entry = new KServiceGroup(name); addEntry(KSycocaEntry::Ptr(entry)); } if (newEntry) { entry->addEntry(newEntry); } } void KBuildServiceGroupFactory::addEntry(const KSycocaEntry::Ptr &newEntry) { KSycocaFactory::addEntry(newEntry); KServiceGroup::Ptr serviceGroup(static_cast(newEntry.data())); serviceGroup->d_func()->m_serviceList.clear(); if (!serviceGroup->baseGroupName().isEmpty()) { m_baseGroupDict->add(serviceGroup->baseGroupName(), newEntry); } } void KBuildServiceGroupFactory::saveHeader(QDataStream &str) { KSycocaFactory::saveHeader(str); str << qint32(m_baseGroupDictOffset); } void KBuildServiceGroupFactory::save(QDataStream &str) { KSycocaFactory::save(str); m_baseGroupDictOffset = str.device()->pos(); m_baseGroupDict->save(str); int endOfFactoryData = str.device()->pos(); // Update header (pass #3) saveHeader(str); // Seek to end. str.device()->seek(endOfFactoryData); } KServiceGroup::Ptr KBuildServiceGroupFactory::findGroupByDesktopPath(const QString &_name, bool deep) { assert(sycoca()->isBuilding()); Q_UNUSED(deep); // ? // We're building a database - the service type must be in memory KSycocaEntry::Ptr group = m_entryDict->value(_name); return KServiceGroup::Ptr(static_cast(group.data())); } diff --git a/src/sycoca/kbuildservicegroupfactory_p.h b/src/sycoca/kbuildservicegroupfactory_p.h index 01c1230..5dbd048 100644 --- a/src/sycoca/kbuildservicegroupfactory_p.h +++ b/src/sycoca/kbuildservicegroupfactory_p.h @@ -1,92 +1,92 @@ /* This file is part of the KDE project Copyright (C) 2000 Waldo Bastian This library is free software; you can redistribute it and/or modify it under the terms of the GNU Library General Public License version 2as published by the Free Software Foundation. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Library General Public License for more details. You should have received a copy of the GNU Library General Public License along with this library; see the file COPYING.LIB. If not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #ifndef KBUILD_SERVICE_GROUP_FACTORY_H #define KBUILD_SERVICE_GROUP_FACTORY_H #include #include #include /** * Service group factory for building ksycoca * @internal */ class KBuildServiceGroupFactory : public KServiceGroupFactory { public: /** * Create factory */ KBuildServiceGroupFactory(KSycoca *db); virtual ~KBuildServiceGroupFactory(); /** * Create new entry. */ KServiceGroup *createEntry(const QString &) const Q_DECL_OVERRIDE; KServiceGroup *createEntry(int) const Q_DECL_OVERRIDE { assert(0); - return 0L; + return nullptr; } /** * Adds the entry @p newEntry to the menu @p menuName */ void addNewEntryTo(const QString &menuName, const KService::Ptr &newEntry); /** * Adds the entry @p newEntry to the "parent group" @p parent, creating * the group if necassery. * A "parent group" is a group of services that all have the same * "X-KDE-ParentApp". */ void addNewChild(const QString &parent, const KSycocaEntry::Ptr &newEntry); /** * Add new menu @p menuName defined by @p file * When @p entry is non-null it is re-used, otherwise a new group is created. * A pointer to the group is returned. */ KServiceGroup::Ptr addNew(const QString &menuName, const QString &file, KServiceGroup::Ptr entry, bool isDeleted); /** * Find a group ( by desktop path, e.g. "Applications/Editors") */ KServiceGroup::Ptr findGroupByDesktopPath(const QString &_name, bool deep = true) Q_DECL_OVERRIDE; /** * Add a new menu entry */ void addEntry(const KSycocaEntry::Ptr &newEntry) Q_DECL_OVERRIDE; /** * Write out servicegroup specific index files. */ void save(QDataStream &str) Q_DECL_OVERRIDE; /** * Write out header information */ void saveHeader(QDataStream &str) Q_DECL_OVERRIDE; }; #endif diff --git a/src/sycoca/kbuildservicetypefactory.cpp b/src/sycoca/kbuildservicetypefactory.cpp index a8ddb52..12d37fc 100644 --- a/src/sycoca/kbuildservicetypefactory.cpp +++ b/src/sycoca/kbuildservicetypefactory.cpp @@ -1,144 +1,144 @@ /* This file is part of the KDE libraries * Copyright (C) 1999 David Faure * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Library General Public * License version 2 as published by the Free Software Foundation; * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Library General Public License for more details. * * You should have received a copy of the GNU Library General Public License * along with this library; see the file COPYING.LIB. If not, write to * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, * Boston, MA 02110-1301, USA. **/ #include "kbuildservicetypefactory_p.h" #include "ksycoca.h" #include "ksycocadict_p.h" #include "ksycocaresourcelist_p.h" #include "sycocadebug.h" #include #include #include #include #include #include KBuildServiceTypeFactory::KBuildServiceTypeFactory(KSycoca *db) : KServiceTypeFactory(db) { m_resourceList = new KSycocaResourceList; m_resourceList->add("servicetypes", QStringLiteral("kservicetypes5"), QStringLiteral("*.desktop")); } KBuildServiceTypeFactory::~KBuildServiceTypeFactory() { delete m_resourceList; } KServiceType::Ptr KBuildServiceTypeFactory::findServiceTypeByName(const QString &_name) { assert(sycoca()->isBuilding()); // We're building a database - the service type must be in memory KSycocaEntry::Ptr servType = m_entryDict->value(_name); return KServiceType::Ptr(static_cast(servType.data())); } KSycocaEntry *KBuildServiceTypeFactory::createEntry(const QString &file) const { QString name = file; int pos = name.lastIndexOf('/'); if (pos != -1) { name = name.mid(pos + 1); } if (name.isEmpty()) { - return 0; + return nullptr; } KDesktopFile desktopFile(QStandardPaths::GenericDataLocation, "kservicetypes5/" + file); const KConfigGroup desktopGroup = desktopFile.desktopGroup(); if (desktopGroup.readEntry("Hidden", false) == true) { - return 0; + return nullptr; } const QString type = desktopGroup.readEntry("Type"); if (type != QLatin1String("ServiceType")) { qCWarning(SYCOCA) << "The service type config file " << desktopFile.fileName() << " has Type=" << type << " instead of Type=ServiceType"; - return 0; + return nullptr; } const QString serviceType = desktopGroup.readEntry("X-KDE-ServiceType"); if (serviceType.isEmpty()) { qCWarning(SYCOCA) << "The service type config file " << desktopFile.fileName() << " does not contain a ServiceType=... entry"; - return 0; + return nullptr; } KServiceType *e = new KServiceType(&desktopFile); if (e->isDeleted()) { delete e; - return 0; + return nullptr; } if (!(e->isValid())) { qCWarning(SYCOCA) << "Invalid ServiceType : " << file; delete e; - return 0; + return nullptr; } return e; } void KBuildServiceTypeFactory::saveHeader(QDataStream &str) { KSycocaFactory::saveHeader(str); str << qint32(m_propertyTypeDict.count()); for (QMap::ConstIterator it = m_propertyTypeDict.constBegin(); it != m_propertyTypeDict.constEnd(); ++it) { str << it.key() << qint32(it.value()); } } void KBuildServiceTypeFactory::save(QDataStream &str) { KSycocaFactory::save(str); #if 0 // not needed since don't have any additional index anymore int endOfFactoryData = str.device()->pos(); // Update header (pass #3) saveHeader(str); // Seek to end. str.device()->seek(endOfFactoryData); #endif } void KBuildServiceTypeFactory::addEntry(const KSycocaEntry::Ptr &newEntry) { KSycocaFactory::addEntry(newEntry); KServiceType::Ptr serviceType(static_cast(newEntry.data())); const QMap &pd = serviceType->propertyDefs(); QMap::ConstIterator pit = pd.begin(); for (; pit != pd.end(); ++pit) { const QString property = pit.key(); QMap::iterator dictit = m_propertyTypeDict.find(property); if (dictit == m_propertyTypeDict.end()) { m_propertyTypeDict.insert(property, pit.value()); } else if (*dictit != static_cast(pit.value())) { qCWarning(SYCOCA) << "Property '" << property << "' is defined multiple times (" << serviceType->name() << ")"; } } } diff --git a/src/sycoca/kbuildservicetypefactory_p.h b/src/sycoca/kbuildservicetypefactory_p.h index 347e753..cad7015 100644 --- a/src/sycoca/kbuildservicetypefactory_p.h +++ b/src/sycoca/kbuildservicetypefactory_p.h @@ -1,76 +1,76 @@ /* This file is part of the KDE project Copyright (C) 1999 David Faure This library is free software; you can redistribute it and/or modify it under the terms of the GNU Library General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Library General Public License for more details. You should have received a copy of the GNU Library General Public License along with this library; see the file COPYING.LIB. If not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #ifndef KBUILD_SERVICE_TYPE_FACTORY_H #define KBUILD_SERVICE_TYPE_FACTORY_H #include #include /** * Service-type factory for building ksycoca * @internal */ class KBuildServiceTypeFactory : public KServiceTypeFactory { public: /** * Create factory */ KBuildServiceTypeFactory(KSycoca *db); virtual ~KBuildServiceTypeFactory(); /** * Find a service type in the database file * @return a pointer to the servicetype in the memory dict (don't free!) */ KServiceType::Ptr findServiceTypeByName(const QString &_name) Q_DECL_OVERRIDE; /** * Construct a KServiceType from a config file. */ KSycocaEntry *createEntry(const QString &file) const Q_DECL_OVERRIDE; KServiceType *createEntry(int) const Q_DECL_OVERRIDE { assert(0); - return 0L; + return nullptr; } /** * Add entry */ void addEntry(const KSycocaEntry::Ptr &newEntry) Q_DECL_OVERRIDE; /** * Write out service type specific index files. */ void save(QDataStream &str) Q_DECL_OVERRIDE; /** * Write out header information * * Don't forget to call the parent first when you override * this function. */ void saveHeader(QDataStream &str) Q_DECL_OVERRIDE; }; #endif diff --git a/src/sycoca/kbuildsycoca.cpp b/src/sycoca/kbuildsycoca.cpp index 5e63907..c3af3bc 100644 --- a/src/sycoca/kbuildsycoca.cpp +++ b/src/sycoca/kbuildsycoca.cpp @@ -1,662 +1,662 @@ /* This file is part of the KDE libraries * Copyright (C) 1999 David Faure * Copyright (C) 2002-2003 Waldo Bastian * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Library General Public * License version 2 as published by the Free Software Foundation; * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Library General Public License for more details. * * You should have received a copy of the GNU Library General Public License * along with this library; see the file COPYING.LIB. If not, write to * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, * Boston, MA 02110-1301, USA. **/ #include "kbuildsycoca_p.h" #include "ksycoca_p.h" #include "ksycocaresourcelist_p.h" #include "vfolder_menu_p.h" #include "ksycocautils_p.h" #include "sycocadebug.h" #include #include #include #include "kbuildservicetypefactory_p.h" #include "kbuildmimetypefactory_p.h" #include "kbuildservicefactory_p.h" #include "kbuildservicegroupfactory_p.h" #include "kctimefactory_p.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include // auto_ptr #include #include -static const char *s_cSycocaPath = 0; +static const char *s_cSycocaPath = nullptr; KBuildSycocaInterface::~KBuildSycocaInterface() {} KBuildSycoca::KBuildSycoca(bool globalDatabase) : KSycoca(true), - m_allEntries(0), - m_currentFactory(0), - m_ctimeFactory(0), - m_ctimeDict(0), - m_currentEntryDict(0), - m_serviceGroupEntryDict(0), - m_vfolder(0), + m_allEntries(nullptr), + m_currentFactory(nullptr), + m_ctimeFactory(nullptr), + m_ctimeDict(nullptr), + m_currentEntryDict(nullptr), + m_serviceGroupEntryDict(nullptr), + m_vfolder(nullptr), m_newTimestamp(0), m_globalDatabase(globalDatabase), m_menuTest(false), m_changed(false) { } KBuildSycoca::~KBuildSycoca() { // Delete the factories while we exist, so that the virtual isBuilding() still works qDeleteAll(*factories()); factories()->clear(); } KSycocaEntry::Ptr KBuildSycoca::createEntry(const QString &file, bool addToFactory) { quint32 timeStamp = m_ctimeFactory->dict()->ctime(file, m_resource); if (!timeStamp) { timeStamp = calcResourceHash(m_resourceSubdir, file); } KSycocaEntry::Ptr entry; if (m_allEntries) { Q_ASSERT(m_ctimeDict); quint32 oldTimestamp = m_ctimeDict->ctime(file, m_resource); if (file.contains(QLatin1String("fake"))) { qDebug() << "m_ctimeDict->ctime(" << file << ") = " << oldTimestamp << "compared with" << timeStamp; } if (timeStamp && (timeStamp == oldTimestamp)) { // Re-use old entry if (m_currentFactory == d->m_serviceFactory) { // Strip .directory from service-group entries entry = m_currentEntryDict->value(file.left(file.length() - 10)); } else { entry = m_currentEntryDict->value(file); } // remove from m_ctimeDict; if m_ctimeDict is not empty // after all files have been processed, it means // some files were removed since last time if (file.contains(QLatin1String("fake"))) { qDebug() << "reusing (and removing) old entry for:" << file << "entry=" << entry; } m_ctimeDict->remove(file, m_resource); } else if (oldTimestamp) { m_changed = true; m_ctimeDict->remove(file, m_resource); qDebug() << "modified:" << file; } else { m_changed = true; qDebug() << "new:" << file; } } m_ctimeFactory->dict()->addCTime(file, m_resource, timeStamp); if (!entry) { // Create a new entry entry = m_currentFactory->createEntry(file); } if (entry && entry->isValid()) { if (addToFactory) { m_currentFactory->addEntry(entry); } else { m_tempStorage.append(entry); } return entry; } return KSycocaEntry::Ptr(); } KService::Ptr KBuildSycoca::createService(const QString &path) { KSycocaEntry::Ptr entry = createEntry(path, false); return KService::Ptr(static_cast(entry.data())); } // returns false if the database is up to date, true if it needs to be saved bool KBuildSycoca::build() { typedef QList KBSEntryDictList; KBSEntryDictList entryDictList; - KBSEntryDict *serviceEntryDict = 0; + KBSEntryDict *serviceEntryDict = nullptr; // Convert for each factory the entryList to a Dict. entryDictList.reserve(factories()->size()); int i = 0; // For each factory Q_FOREACH (KSycocaFactory* factory, *factories()) { KBSEntryDict *entryDict = new KBSEntryDict; if (m_allEntries) { // incremental build Q_FOREACH (const KSycocaEntry::Ptr &entry, (*m_allEntries)[i++]) { //if (entry->entryPath().contains("fake")) // qDebug() << "inserting into entryDict:" << entry->entryPath() << entry; entryDict->insert(entry->entryPath(), entry); } } if (factory == d->m_serviceFactory) { serviceEntryDict = entryDict; } else if (factory == m_buildServiceGroupFactory) { m_serviceGroupEntryDict = entryDict; } entryDictList.append(entryDict); } // Save the mtime of each dir, just before we list them // ## should we convert to UTC to avoid surprises when summer time kicks in? Q_FOREACH (const QString &dir, factoryResourceDirs()) { qint64 stamp = 0; KSycocaUtilsPrivate::visitResourceDirectory(dir, [&stamp] (const QFileInfo &info) { stamp = qMax(stamp, info.lastModified().toMSecsSinceEpoch()); return true; }); m_allResourceDirs.insert(dir, stamp); } QMap allResourcesSubDirs; // dirs, kstandarddirs-resource-name // For each factory Q_FOREACH (KSycocaFactory* factory, *factories()) { // For each resource the factory deals with const KSycocaResourceList *list = factory->resourceList(); if (!list) { continue; } Q_FOREACH (const KSycocaResource &res, *list) { // With this we would get dirs, but not a unique list of relative files (for global+local merging to work) //const QStringList dirs = QStandardPaths::locateAll(QStandardPaths::GenericDataLocation, res.subdir, QStandardPaths::LocateDirectory); //allResourcesSubDirs[res.resource] += dirs; allResourcesSubDirs.insert(res.subdir, res.resource); } } m_ctimeFactory = new KCTimeFactory(this); // This is a build factory too, don't delete!! for (QMap::ConstIterator it1 = allResourcesSubDirs.constBegin(); it1 != allResourcesSubDirs.constEnd(); ++it1) { m_changed = false; m_resourceSubdir = it1.key(); m_resource = it1.value(); QSet relFiles; const QStringList dirs = QStandardPaths::locateAll(QStandardPaths::GenericDataLocation, m_resourceSubdir, QStandardPaths::LocateDirectory); Q_FOREACH (const QString &dir, dirs) { QDirIterator it(dir, QDirIterator::Subdirectories); while (it.hasNext()) { const QString filePath = it.next(); Q_ASSERT(filePath.startsWith(dir)); // due to the line below... const QString relPath = filePath.mid(dir.length() + 1); relFiles.insert(relPath); } } // Now find all factories that use this resource.... // For each factory KBSEntryDictList::const_iterator ed_it = entryDictList.constBegin(); const KBSEntryDictList::const_iterator ed_end = entryDictList.constEnd(); KSycocaFactoryList::const_iterator it = factories()->constBegin(); const KSycocaFactoryList::const_iterator end = factories()->constEnd(); for (; it != end; ++it, ++ed_it) { m_currentFactory = (*it); // m_ctimeInfo gets created after the initial loop, so it has no entryDict. - m_currentEntryDict = ed_it == ed_end ? 0 : *ed_it; + m_currentEntryDict = ed_it == ed_end ? nullptr : *ed_it; // For each resource the factory deals with const KSycocaResourceList *list = m_currentFactory->resourceList(); if (!list) { continue; } Q_FOREACH (const KSycocaResource &res, *list) { if (res.resource != (*it1)) { continue; } // For each file in the resource for (auto entryPath = relFiles.constBegin(); entryPath != relFiles.constEnd(); ++entryPath) { // Check if file matches filter if ((*entryPath).endsWith(res.extension)) { createEntry(*entryPath, true); } } } } if (m_changed || !m_allEntries) { //qDebug() << "CHANGED:" << m_resource; m_changedResources.append(m_resource); } } if (m_ctimeDict && !m_ctimeDict->isEmpty()) { //qDebug() << "Still in time dict:"; //m_ctimeDict->dump(); // ## It seems entries filtered out by vfolder are still in there, // so on a real system we end up always adding "apps" to m_changedResources // Get the list of resources from which some files were deleted const QStringList resources = m_ctimeDict->remainingResourceList(); qDebug() << "Still in the time dict (i.e. deleted files)" << resources; m_changedResources += resources; } bool result = true; const bool createVFolder = !m_changedResources.isEmpty() || (m_ctimeDict && !m_ctimeDict->isEmpty()); if (createVFolder || m_menuTest) { m_resource = "apps"; m_resourceSubdir = QStringLiteral("applications"); m_currentFactory = d->m_serviceFactory; m_currentEntryDict = serviceEntryDict; m_changed = false; m_vfolder = new VFolderMenu(d->m_serviceFactory, this); if (!m_trackId.isEmpty()) { m_vfolder->setTrackId(m_trackId); } VFolderMenu::SubMenu *kdeMenu = m_vfolder->parseMenu(QStringLiteral(APPLICATIONS_MENU_NAME)); KServiceGroup::Ptr entry = m_buildServiceGroupFactory->addNew(QStringLiteral("/"), kdeMenu->directoryFile, KServiceGroup::Ptr(), false); entry->setLayoutInfo(kdeMenu->layoutList); createMenu(QString(), QString(), kdeMenu); // Storing the mtime *after* looking at these dirs is a tiny race condition, // but I'm not sure how to get the vfolder dirs upfront... Q_FOREACH (QString dir, m_vfolder->allDirectories()) { if (dir.endsWith('/')) { dir.chop(1); // remove trailing slash, to avoid having ~/.local/share/applications twice } if (!m_allResourceDirs.contains(dir)) { qint64 stamp = 0; KSycocaUtilsPrivate::visitResourceDirectory(dir, [&stamp] (const QFileInfo &info) { stamp = qMax(stamp, info.lastModified().toMSecsSinceEpoch()); return true; }); m_allResourceDirs.insert(dir, stamp); } } if (m_changed || !m_allEntries) { //qDebug() << "CHANGED:" << m_resource; m_changedResources.append(m_resource); } if (m_menuTest) { result = false; } } qDeleteAll(entryDictList); return result; } void KBuildSycoca::createMenu(const QString &caption_, const QString &name_, VFolderMenu::SubMenu *menu) { QString caption = caption_; QString name = name_; foreach (VFolderMenu::SubMenu *subMenu, menu->subMenus) { QString subName = name + subMenu->name + '/'; QString directoryFile = subMenu->directoryFile; if (directoryFile.isEmpty()) { directoryFile = subName + QStringLiteral(".directory"); } quint32 timeStamp = m_ctimeFactory->dict()->ctime(directoryFile, m_resource); if (!timeStamp) { timeStamp = calcResourceHash(m_resourceSubdir, directoryFile); } KServiceGroup::Ptr entry; if (m_allEntries) { const quint32 oldTimestamp = m_ctimeDict->ctime(directoryFile, m_resource); if (timeStamp && (timeStamp == oldTimestamp)) { KSycocaEntry::Ptr group = m_serviceGroupEntryDict->value(subName); if (group) { entry = KServiceGroup::Ptr(static_cast(group.data())); if (entry->directoryEntryPath() != directoryFile) { - entry = 0; // Can't reuse this one! + entry = nullptr; // Can't reuse this one! } } } } if (timeStamp) { // bug? (see calcResourceHash). There might not be a .directory file... m_ctimeFactory->dict()->addCTime(directoryFile, m_resource, timeStamp); } entry = m_buildServiceGroupFactory->addNew(subName, subMenu->directoryFile, entry, subMenu->isDeleted); entry->setLayoutInfo(subMenu->layoutList); if (!(m_menuTest && entry->noDisplay())) { createMenu(caption + entry->caption() + '/', subName, subMenu); } } if (caption.isEmpty()) { caption += '/'; } if (name.isEmpty()) { name += '/'; } foreach (const KService::Ptr &p, menu->items) { if (m_menuTest) { if (!menu->isDeleted && !p->noDisplay()) printf("%s\t%s\t%s\n", qPrintable(caption), qPrintable(p->menuId()), qPrintable(QStandardPaths::locate(QStandardPaths::ApplicationsLocation, p->entryPath()))); } else { m_buildServiceGroupFactory->addNewEntryTo(name, p); } } } bool KBuildSycoca::recreate(bool incremental) { QFileInfo fi(KSycoca::absoluteFilePath(m_globalDatabase ? KSycoca::GlobalDatabase : KSycoca::LocalDatabase)); if (!QDir().mkpath(fi.absolutePath())) { qCWarning(SYCOCA) << "Couldn't create" << fi.absolutePath(); return false; } QString path(fi.absoluteFilePath()); QLockFile lockFile(path + QLatin1String(".lock")); if (!lockFile.tryLock()) { qDebug() << "Waiting for already running" << KBUILDSYCOCA_EXENAME << "to finish."; if (!lockFile.lock()) { qCWarning(SYCOCA) << "Couldn't lock" << path + ".lock"; return false; } if (!needsRebuild()) { //qDebug() << "Up-to-date, skipping."; return true; } } QByteArray qSycocaPath = QFile::encodeName(path); s_cSycocaPath = qSycocaPath.data(); - m_allEntries = 0; - m_ctimeDict = 0; + m_allEntries = nullptr; + m_ctimeDict = nullptr; if (incremental && checkGlobalHeader()) { qDebug() << "Reusing existing ksycoca"; KSycoca *oldSycoca = KSycoca::self(); m_allEntries = new KSycocaEntryListList; m_ctimeDict = new KCTimeDict; // Must be in same order as in KBuildSycoca::recreate()! m_allEntries->append(KSycocaPrivate::self()->serviceTypeFactory()->allEntries()); m_allEntries->append(KSycocaPrivate::self()->mimeTypeFactory()->allEntries()); m_allEntries->append(KSycocaPrivate::self()->serviceGroupFactory()->allEntries()); m_allEntries->append(KSycocaPrivate::self()->serviceFactory()->allEntries()); KCTimeFactory *ctimeInfo = new KCTimeFactory(oldSycoca); *m_ctimeDict = ctimeInfo->loadDict(); } - s_cSycocaPath = 0; + s_cSycocaPath = nullptr; QSaveFile database(path); bool openedOK = database.open(QIODevice::WriteOnly); if (!openedOK && database.error() == QFile::WriteError && QFile::exists(path)) { QFile::remove(path); openedOK = database.open(QIODevice::WriteOnly); } if (!openedOK) { qCWarning(SYCOCA) << "ERROR creating database" << path << ":" << database.errorString(); return false; } QDataStream *str = new QDataStream(&database); str->setVersion(QDataStream::Qt_5_3); m_newTimestamp = QDateTime::currentMSecsSinceEpoch(); qDebug().nospace() << "Recreating ksycoca file (" << path << ", version " << KSycoca::version() << ")"; // It is very important to build the servicetype one first KBuildServiceTypeFactory *buildServiceTypeFactory = new KBuildServiceTypeFactory(this); d->m_serviceTypeFactory = buildServiceTypeFactory; KBuildMimeTypeFactory *buildMimeTypeFactory = new KBuildMimeTypeFactory(this); d->m_mimeTypeFactory = buildMimeTypeFactory; m_buildServiceGroupFactory = new KBuildServiceGroupFactory(this); d->m_serviceGroupFactory = m_buildServiceGroupFactory; d->m_serviceFactory = new KBuildServiceFactory(buildServiceTypeFactory, buildMimeTypeFactory, m_buildServiceGroupFactory); if (build()) { // Parse dirs save(str); // Save database if (str->status() != QDataStream::Ok) { // Probably unnecessary now in Qt5, since QSaveFile detects write errors database.cancelWriting(); // Error } delete str; - str = 0; + str = nullptr; //if we are currently via sudo, preserve the original owner //as $HOME may also be that of another user rather than /root #ifdef Q_OS_UNIX if (qEnvironmentVariableIsSet("SUDO_UID")) { const int uid = QString(qgetenv("SUDO_UID")).toInt(); const int gid = QString(qgetenv("SUDO_GID")).toInt(); if (uid && gid) { fchown(database.handle(), uid, gid); } } #endif if (!database.commit()) { qCWarning(SYCOCA) << "ERROR writing database" << database.fileName() << ". Disk full?"; return false; } if (!m_globalDatabase) { // Compatibility code for KF < 5.15: provide a ksycoca5 symlink after the filename change, for old apps to keep working during the upgrade const QString oldSycoca = QStandardPaths::writableLocation(QStandardPaths::GenericCacheLocation) + QLatin1String("/ksycoca5"); if (QFile::exists(oldSycoca)) { QFile::remove(oldSycoca); #ifdef Q_OS_UNIX if (::link(QFile::encodeName(path).constData(), QFile::encodeName(oldSycoca).constData()) != 0) { QFile::copy(path, oldSycoca); } #else QFile::copy(path, oldSycoca); #endif } } } else { delete str; - str = 0; + str = nullptr; database.cancelWriting(); if (m_menuTest) { return true; } qDebug() << "Database is up to date"; } if (m_globalDatabase) { // These directories may have been created with 0700 permission // better delete them if they are empty QString appsDir = QStandardPaths::writableLocation(QStandardPaths::ApplicationsLocation); QDir().remove(appsDir); // was doing the same with servicetypes, but I don't think any of these gets created-by-mistake anymore. } if (d->m_sycocaStrategy == KSycocaPrivate::StrategyMemFile) { KMemFile::fileContentsChanged(path); } delete m_ctimeDict; delete m_allEntries; delete m_vfolder; return true; } void KBuildSycoca::save(QDataStream *str) { // Write header (#pass 1) str->device()->seek(0); (*str) << qint32(KSycoca::version()); //KSycocaFactory * servicetypeFactory = 0; //KBuildMimeTypeFactory * mimeTypeFactory = 0; - KBuildServiceFactory *serviceFactory = 0; + KBuildServiceFactory *serviceFactory = nullptr; Q_FOREACH (KSycocaFactory* factory, *factories()) { qint32 aId; qint32 aOffset; aId = factory->factoryId(); //if ( aId == KST_KServiceTypeFactory ) // servicetypeFactory = factory; //else if ( aId == KST_KMimeTypeFactory ) // mimeTypeFactory = static_cast( factory ); if (aId == KST_KServiceFactory) { serviceFactory = static_cast(factory); } aOffset = factory->offset(); // not set yet, so always 0 (*str) << aId; (*str) << aOffset; } (*str) << qint32(0); // No more factories. // Write XDG_DATA_DIRS (*str) << QStandardPaths::standardLocations(QStandardPaths::GenericDataLocation).join(QString(QLatin1Char(':'))); (*str) << m_newTimestamp; (*str) << QLocale().bcp47Name(); // This makes it possible to trigger a ksycoca update for all users (KIOSK feature) (*str) << calcResourceHash(QStringLiteral("kservices5"), QStringLiteral("update_ksycoca")); (*str) << m_allResourceDirs.keys(); for (auto it = m_allResourceDirs.constBegin(); it != m_allResourceDirs.constEnd(); ++it) { (*str) << it.value(); } // Calculate per-servicetype/mimetype data if (serviceFactory) serviceFactory->postProcessServices(); // Here so that it's the last debug message qDebug() << "Saving"; // Write factory data.... Q_FOREACH (KSycocaFactory* factory, *factories()) { factory->save(*str); if (str->status() != QDataStream::Ok) { // ######## TODO: does this detect write errors, e.g. disk full? return; // error } } int endOfData = str->device()->pos(); // Write header (#pass 2) str->device()->seek(0); (*str) << qint32(KSycoca::version()); Q_FOREACH (KSycocaFactory* factory, *factories()) { qint32 aId; qint32 aOffset; aId = factory->factoryId(); aOffset = factory->offset(); (*str) << aId; (*str) << aOffset; } (*str) << qint32(0); // No more factories. // Jump to end of database str->device()->seek(endOfData); } QStringList KBuildSycoca::factoryResourceDirs() { - static QStringList *dirs = NULL; - if (dirs != NULL) { + static QStringList *dirs = nullptr; + if (dirs != nullptr) { return *dirs; } dirs = new QStringList; // these are all resource dirs cached by ksycoca *dirs += KServiceTypeFactory::resourceDirs(); *dirs += KMimeTypeFactory::resourceDirs(); *dirs += KServiceFactory::resourceDirs(); return *dirs; } QStringList KBuildSycoca::existingResourceDirs() { - static QStringList *dirs = NULL; - if (dirs != NULL) { + static QStringList *dirs = nullptr; + if (dirs != nullptr) { return *dirs; } dirs = new QStringList(factoryResourceDirs()); for (QStringList::Iterator it = dirs->begin(); it != dirs->end();) { QFileInfo inf(*it); if (!inf.exists() || !inf.isReadable()) { it = dirs->erase(it); } else { ++it; } } return *dirs; } static quint32 updateHash(const QString &file, quint32 hash) { QFileInfo fi(file); if (fi.isReadable() && fi.isFile()) { // This was using buff.st_ctime (in Waldo's initial commit to kstandarddirs.cpp in 2001), but that looks wrong? // Surely we want to catch manual editing, while a chmod doesn't matter much? hash += fi.lastModified().toTime_t(); } return hash; } quint32 KBuildSycoca::calcResourceHash(const QString &resourceSubDir, const QString &filename) { quint32 hash = 0; if (!QDir::isRelativePath(filename)) { return updateHash(filename, hash); } const QStringList files = QStandardPaths::locateAll(QStandardPaths::GenericDataLocation, resourceSubDir + QLatin1Char('/') + filename); Q_FOREACH (const QString &file, files) { hash = updateHash(file, hash); } if (hash == 0 && !filename.endsWith(QLatin1String("update_ksycoca")) && !filename.endsWith(QLatin1String(".directory")) // bug? needs investigation from someone who understands the VFolder spec ) { qCWarning(SYCOCA) << "File not found or not readable:" << filename << "found:" << files; Q_ASSERT(hash != 0); } return hash; } bool KBuildSycoca::checkGlobalHeader() { // Since it's part of the filename, we are 99% sure that the locale and prefixes will match. const QString current_language = QLocale().bcp47Name(); const quint32 current_update_sig = KBuildSycoca::calcResourceHash(QStringLiteral("kservices5"), QStringLiteral("update_ksycoca")); const QString current_prefixes = QStandardPaths::standardLocations(QStandardPaths::GenericDataLocation).join(QString(QLatin1Char(':'))); const KSycocaHeader header = KSycocaPrivate::self()->readSycocaHeader(); Q_ASSERT(!header.prefixes.split(':').contains(QDir::homePath())); return (current_update_sig == header.updateSignature) && (current_language == header.language) && (current_prefixes == header.prefixes) && (header.timeStamp != 0); } const char *KBuildSycoca::sycocaPath() { return s_cSycocaPath; } diff --git a/src/sycoca/kctimefactory_p.h b/src/sycoca/kctimefactory_p.h index 24ae8e5..aaa9e77 100644 --- a/src/sycoca/kctimefactory_p.h +++ b/src/sycoca/kctimefactory_p.h @@ -1,97 +1,97 @@ /* This file is part of the KDE project Copyright (C) 2000 Waldo Bastian This library is free software; you can redistribute it and/or modify it under the terms of the GNU Library General Public License version 2as published by the Free Software Foundation. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Library General Public License for more details. You should have received a copy of the GNU Library General Public License along with this library; see the file COPYING.LIB. If not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #ifndef KCTIME_FACTORY_H #define KCTIME_FACTORY_H #include #include /** * Simple dict for assocating a timestamp with each file in ksycoca */ class KCTimeDict { public: void addCTime(const QString &path, const QByteArray &resource, quint32 ctime); quint32 ctime(const QString &path, const QByteArray &resource) const; void remove(const QString &path, const QByteArray &resource); void dump() const; bool isEmpty() const { return m_hash.isEmpty(); } QStringList remainingResourceList() const; void load(QDataStream &str); void save(QDataStream &str) const; private: typedef QHash Hash; Hash m_hash; }; /** * Internal factory for storing the timestamp of each file in ksycoca * @internal */ class KCTimeFactory : public KSycocaFactory { K_SYCOCAFACTORY(KST_CTimeInfo) public: /** * Create factory */ KCTimeFactory(KSycoca *db); virtual ~KCTimeFactory(); /** * Write out header information */ void saveHeader(QDataStream &str) Q_DECL_OVERRIDE; /** * Write out data */ void save(QDataStream &str) Q_DECL_OVERRIDE; KSycocaEntry *createEntry(const QString &) const Q_DECL_OVERRIDE { - return 0; + return nullptr; } KSycocaEntry *createEntry(int) const Q_DECL_OVERRIDE { - return 0; + return nullptr; } // Loads the dict and returns it; does not set m_ctimeDict; // this is only used in incremental mode for loading the old timestamps. KCTimeDict loadDict() const; // The API for inserting/looking up entries is in KCTimeDict. KCTimeDict *dict() { return &m_ctimeDict; } private: KCTimeDict m_ctimeDict; int m_dictOffset; }; #endif diff --git a/src/sycoca/kmemfile_p.h b/src/sycoca/kmemfile_p.h index 532cd0e..67a581d 100644 --- a/src/sycoca/kmemfile_p.h +++ b/src/sycoca/kmemfile_p.h @@ -1,100 +1,100 @@ /* This file is part of the KDE libraries Copyright (C) 2008 Christian Ehrlicher This library is free software; you can redistribute it and/or modify it under the terms of the GNU Library General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Library General Public License for more details. You should have received a copy of the GNU Library General Public License along with this library; see the file COPYING.LIB. If not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #ifndef KMEMFILE_H #define KMEMFILE_H #ifndef QT_NO_SHAREDMEMORY #include #include /** * @internal * Simple QIODevice for QSharedMemory to keep ksycoca cache in memory only once * The first call to open() loads the file into a shm segment. Every * subsequent call only attaches to this segment. When the file content changed, * you have to execute KMemFile::fileContentsChanged() to update the internal * structures. The next call to open() creates a new shm segment. The old one * is automatically destroyed when the last process closed KMemFile. */ class KMemFile : public QIODevice { public: /** * ctor * * @param filename the file to load into memory * @param parent our parent */ - explicit KMemFile(const QString &filename, QObject *parent = 0); + explicit KMemFile(const QString &filename, QObject *parent = nullptr); /** * dtor */ virtual ~KMemFile(); /** * closes the KMemFile * * @reimp */ void close() Q_DECL_OVERRIDE; /** * As KMemFile is a random access device, it returns false * * @reimp */ bool isSequential() const Q_DECL_OVERRIDE; /** * @reimp * @param mode only QIODevice::ReadOnly is accepted */ bool open(OpenMode mode) Q_DECL_OVERRIDE; /** * Sets the current read/write position to pos * @reimp * @param pos the new read/write position */ bool seek(qint64 pos) Q_DECL_OVERRIDE; /** * Returns the size of the file * @reimp */ qint64 size() const Q_DECL_OVERRIDE; /** * This static function updates the internal information about the file * loaded into shared memory. The next time the file is opened, the file is * reread from the file system. */ static void fileContentsChanged(const QString &filename); protected: /** @reimp */ qint64 readData(char *data, qint64 maxSize) Q_DECL_OVERRIDE; /** @reimp */ qint64 writeData(const char *data, qint64 maxSize) Q_DECL_OVERRIDE; private: class Private; friend class Private; Private *const d; }; #endif //QT_NO_SHAREDMEMORY #endif // KMEMFILE_H diff --git a/src/sycoca/ksycoca.cpp b/src/sycoca/ksycoca.cpp index 4c8bc75..5fd4905 100644 --- a/src/sycoca/ksycoca.cpp +++ b/src/sycoca/ksycoca.cpp @@ -1,846 +1,846 @@ /* This file is part of the KDE libraries * Copyright (C) 1999-2000 Waldo Bastian * Copyright (C) 2005-2009 David Faure * Copyright (C) 2008 Hamish Rodda * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Library General Public * License version 2 as published by the Free Software Foundation; * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Library General Public License for more details. * * You should have received a copy of the GNU Library General Public License * along with this library; see the file COPYING.LIB. If not, write to * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, * Boston, MA 02110-1301, USA. **/ #include "ksycoca.h" #include "ksycoca_p.h" #include "ksycocautils_p.h" #include "ksycocatype.h" #include "ksycocafactory_p.h" #include "kconfiggroup.h" #include "ksharedconfig.h" #include "sycocadebug.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "kbuildsycoca_p.h" #include "ksycocadevices_p.h" #ifdef Q_OS_UNIX #include #include #endif /** * Sycoca file version number. * If the existing file is outdated, it will not get read * but instead we'll regenerate a new one. * However running apps should still be able to read it, so * only add to the data, never remove/modify. */ #define KSYCOCA_VERSION 303 #if HAVE_MADVISE || HAVE_MMAP #include // This #include was checked when looking for posix_madvise #endif #ifndef MAP_FAILED #define MAP_FAILED ((void *) -1) #endif QDataStream &operator>>(QDataStream &in, KSycocaHeader &h) { in >> h.prefixes >> h.timeStamp >> h.language >> h.updateSignature; return in; } // The following limitations are in place: // Maximum length of a single string: 8192 bytes // Maximum length of a string list: 1024 strings // Maximum number of entries: 8192 // // The purpose of these limitations is to limit the impact // of database corruption. Q_DECLARE_OPERATORS_FOR_FLAGS(KSycocaPrivate::BehaviorsIfNotFound) KSycocaPrivate::KSycocaPrivate(KSycoca *q) : databaseStatus(DatabaseNotOpen), readError(false), timeStamp(0), m_databasePath(), updateSig(0), m_haveListeners(false), m_globalDatabase(false), q(q), sycoca_size(0), - sycoca_mmap(0), - m_mmapFile(0), - m_device(0), - m_mimeTypeFactory(0), - m_serviceTypeFactory(0), - m_serviceFactory(0), - m_serviceGroupFactory(0) + sycoca_mmap(nullptr), + m_mmapFile(nullptr), + m_device(nullptr), + m_mimeTypeFactory(nullptr), + m_serviceTypeFactory(nullptr), + m_serviceFactory(nullptr), + m_serviceGroupFactory(nullptr) { #ifdef Q_OS_WIN /* on windows we use KMemFile (QSharedMemory) to avoid problems with mmap (can't delete a mmap'd file) */ m_sycocaStrategy = StrategyMemFile; #else m_sycocaStrategy = StrategyMmap; #endif KConfigGroup config(KSharedConfig::openConfig(), "KSycoca"); setStrategyFromString(config.readEntry("strategy")); } void KSycocaPrivate::setStrategyFromString(const QString &strategy) { if (strategy == QLatin1String("mmap")) { m_sycocaStrategy = StrategyMmap; } else if (strategy == QLatin1String("file")) { m_sycocaStrategy = StrategyFile; } else if (strategy == QLatin1String("sharedmem")) { m_sycocaStrategy = StrategyMemFile; } else if (!strategy.isEmpty()) { qCWarning(SYCOCA) << "Unknown sycoca strategy:" << strategy; } } bool KSycocaPrivate::tryMmap() { #if HAVE_MMAP Q_ASSERT(!m_databasePath.isEmpty()); m_mmapFile = new QFile(m_databasePath); const bool canRead = m_mmapFile->open(QIODevice::ReadOnly); Q_ASSERT(canRead); if (!canRead) { return false; } fcntl(m_mmapFile->handle(), F_SETFD, FD_CLOEXEC); sycoca_size = m_mmapFile->size(); - void *mmapRet = mmap(0, sycoca_size, + void *mmapRet = mmap(nullptr, sycoca_size, PROT_READ, MAP_SHARED, m_mmapFile->handle(), 0); /* POSIX mandates only MAP_FAILED, but we are paranoid so check for null pointer too. */ - if (mmapRet == MAP_FAILED || mmapRet == 0) { + if (mmapRet == MAP_FAILED || mmapRet == nullptr) { qCDebug(SYCOCA).nospace() << "mmap failed. (length = " << sycoca_size << ")"; - sycoca_mmap = 0; + sycoca_mmap = nullptr; return false; } else { sycoca_mmap = static_cast(mmapRet); #if HAVE_MADVISE (void) posix_madvise(mmapRet, sycoca_size, POSIX_MADV_WILLNEED); #endif // HAVE_MADVISE return true; } #else return false; #endif // HAVE_MMAP } int KSycoca::version() { return KSYCOCA_VERSION; } class KSycocaSingleton { public: KSycocaSingleton() { } ~KSycocaSingleton() { } bool hasSycoca() const { return m_threadSycocas.hasLocalData(); } KSycoca *sycoca() { if (!m_threadSycocas.hasLocalData()) { m_threadSycocas.setLocalData(new KSycoca); } return m_threadSycocas.localData(); } void setSycoca(KSycoca *s) { m_threadSycocas.setLocalData(s); } private: QThreadStorage m_threadSycocas; }; Q_GLOBAL_STATIC(KSycocaSingleton, ksycocaInstance) QString KSycocaPrivate::findDatabase() { Q_ASSERT(databaseStatus == DatabaseNotOpen); m_globalDatabase = false; QString path = KSycoca::absoluteFilePath(); QFileInfo info(path); bool canRead = info.isReadable(); if (!canRead) { const QString globalPath = KSycoca::absoluteFilePath(KSycoca::GlobalDatabase); if (!globalPath.isEmpty()) { info.setFile(globalPath); canRead = info.isReadable(); if (canRead) { m_globalDatabase = true; path = globalPath; } } } if (canRead) { if (m_haveListeners) { m_fileWatcher.addFile(path); } return path; } // Let's be notified when it gets created - by another process or by ourselves m_fileWatcher.addFile(path); return QString(); } // Read-only constructor // One instance per thread KSycoca::KSycoca() : d(new KSycocaPrivate(this)) { // We always delete and recreate the DB, so KDirWatch normally emits created connect(&d->m_fileWatcher, &KDirWatch::created, this, [this](){ d->slotDatabaseChanged(); }); // In some cases, KDirWatch only thinks the file was modified though connect(&d->m_fileWatcher, &KDirWatch::dirty, this, [this](){ d->slotDatabaseChanged(); }); } bool KSycocaPrivate::openDatabase(bool openDummyIfNotFound) { Q_ASSERT(databaseStatus == DatabaseNotOpen); - delete m_device; m_device = 0; + delete m_device; m_device = nullptr; if (m_databasePath.isEmpty()) { m_databasePath = findDatabase(); } bool result = true; if (!m_databasePath.isEmpty()) { qCDebug(SYCOCA) << "Opening ksycoca from" << m_databasePath; m_dbLastModified = QFileInfo(m_databasePath).lastModified(); checkVersion(); } else { // No database file //qCDebug(SYCOCA) << "Could not open ksycoca"; m_databasePath.clear(); if (openDummyIfNotFound) { // We open a dummy database instead. //qCDebug(SYCOCA) << "No database, opening a dummy one."; m_sycocaStrategy = StrategyDummyBuffer; QDataStream *str = stream(); *str << qint32(KSYCOCA_VERSION); *str << qint32(0); } else { result = false; } } return result; } KSycocaAbstractDevice *KSycocaPrivate::device() { if (m_device) { return m_device; } Q_ASSERT(!m_databasePath.isEmpty()); KSycocaAbstractDevice *device = m_device; if (m_sycocaStrategy == StrategyDummyBuffer) { device = new KSycocaBufferDevice; device->device()->open(QIODevice::ReadOnly); // can't fail } else { #if HAVE_MMAP if (m_sycocaStrategy == StrategyMmap && tryMmap()) { device = new KSycocaMmapDevice(sycoca_mmap, sycoca_size); if (!device->device()->open(QIODevice::ReadOnly)) { - delete device; device = 0; + delete device; device = nullptr; } } #endif #ifndef QT_NO_SHAREDMEMORY if (!device && m_sycocaStrategy == StrategyMemFile) { device = new KSycocaMemFileDevice(m_databasePath); if (!device->device()->open(QIODevice::ReadOnly)) { - delete device; device = 0; + delete device; device = nullptr; } } #endif if (!device) { device = new KSycocaFileDevice(m_databasePath); if (!device->device()->open(QIODevice::ReadOnly)) { qCWarning(SYCOCA) << "Couldn't open" << m_databasePath << "even though it is readable? Impossible."; //delete device; device = 0; // this would crash in the return statement... } } } if (device) { m_device = device; } return m_device; } QDataStream *&KSycocaPrivate::stream() { if (!m_device) { if (databaseStatus == DatabaseNotOpen) { checkDatabase(KSycocaPrivate::IfNotFoundRecreate | KSycocaPrivate::IfNotFoundOpenDummy); } device(); // create m_device } return m_device->stream(); } void KSycocaPrivate::slotDatabaseChanged() { // We don't have information anymore on what resources changed, so emit them all changeList = QStringList() << QStringLiteral("services") << QStringLiteral("servicetypes") << QStringLiteral("xdgdata-mime") << QStringLiteral("apps"); qCDebug(SYCOCA) << QThread::currentThread() << "got a notifyDatabaseChanged signal"; // KDirWatch tells us the database file changed // We would have found out in the next call to ensureCacheValid(), but for // now keep the call to closeDatabase, to help refcounting to 0 the old mmaped file earlier. closeDatabase(); // Start monitoring the new file right away m_databasePath = findDatabase(); // Now notify applications emit q->databaseChanged(); emit q->databaseChanged(changeList); } KMimeTypeFactory *KSycocaPrivate::mimeTypeFactory() { if (!m_mimeTypeFactory) { m_mimeTypeFactory = new KMimeTypeFactory(q); } return m_mimeTypeFactory; } KServiceTypeFactory *KSycocaPrivate::serviceTypeFactory() { if (!m_serviceTypeFactory) { m_serviceTypeFactory = new KServiceTypeFactory(q); } return m_serviceTypeFactory; } KServiceFactory *KSycocaPrivate::serviceFactory() { if (!m_serviceFactory) { m_serviceFactory = new KServiceFactory(q); } return m_serviceFactory; } KServiceGroupFactory *KSycocaPrivate::serviceGroupFactory() { if (!m_serviceGroupFactory) { m_serviceGroupFactory = new KServiceGroupFactory(q); } return m_serviceGroupFactory; } // Add local paths to the list of dirs we got from the global database void KSycocaPrivate::addLocalResourceDir(const QString &path) { // If any local path is more recent than the time the global sycoca was created, build a local sycoca. allResourceDirs.insert(path, timeStamp); } // Read-write constructor - only for KBuildSycoca KSycoca::KSycoca(bool /* dummy */) : d(new KSycocaPrivate(this)) { } KSycoca *KSycoca::self() { KSycoca *s = ksycocaInstance()->sycoca(); Q_ASSERT(s); return s; } KSycoca::~KSycoca() { d->closeDatabase(); delete d; //if (ksycocaInstance.exists() // && ksycocaInstance->self == this) // ksycocaInstance->self = 0; } bool KSycoca::isAvailable() // TODO KF6: make it non-static (mostly useful for unittests) { return self()->d->checkDatabase(KSycocaPrivate::IfNotFoundDoNothing/* don't open dummy db if not found */); } void KSycocaPrivate::closeDatabase() { delete m_device; - m_device = 0; + m_device = nullptr; // It is very important to delete all factories here // since they cache information about the database file // But other threads might be using them, so this class is // refcounted, and deleted when the last thread is done with them qDeleteAll(m_factories); m_factories.clear(); - m_mimeTypeFactory = 0; - m_serviceFactory = 0; - m_serviceTypeFactory = 0; - m_serviceGroupFactory = 0; + m_mimeTypeFactory = nullptr; + m_serviceFactory = nullptr; + m_serviceTypeFactory = nullptr; + m_serviceGroupFactory = nullptr; #if HAVE_MMAP if (sycoca_mmap) { // Solaris has munmap(char*, size_t) and everything else should // be happy with a char* for munmap(void*, size_t) munmap(const_cast(sycoca_mmap), sycoca_size); - sycoca_mmap = 0; + sycoca_mmap = nullptr; } - delete m_mmapFile; m_mmapFile = 0; + delete m_mmapFile; m_mmapFile = nullptr; #endif databaseStatus = DatabaseNotOpen; m_databasePath.clear(); timeStamp = 0; } void KSycoca::addFactory(KSycocaFactory *factory) { d->addFactory(factory); } #ifndef KSERVICE_NO_DEPRECATED bool KSycoca::isChanged(const char *type) { return self()->d->changeList.contains(QString::fromLatin1(type)); } #endif QDataStream *KSycoca::findEntry(int offset, KSycocaType &type) { QDataStream *str = stream(); Q_ASSERT(str); //qCDebug(SYCOCA) << QString("KSycoca::_findEntry(offset=%1)").arg(offset,8,16); str->device()->seek(offset); qint32 aType; *str >> aType; type = KSycocaType(aType); //qCDebug(SYCOCA) << QString("KSycoca::found type %1").arg(aType); return str; } KSycocaFactoryList *KSycoca::factories() { return d->factories(); } // Warning, checkVersion rewinds to the beginning of stream(). bool KSycocaPrivate::checkVersion() { QDataStream *m_str = device()->stream(); Q_ASSERT(m_str); m_str->device()->seek(0); qint32 aVersion; *m_str >> aVersion; if (aVersion < KSYCOCA_VERSION) { qCDebug(SYCOCA) << "Found version" << aVersion << ", expecting version" << KSYCOCA_VERSION << "or higher."; databaseStatus = BadVersion; return false; } else { databaseStatus = DatabaseOK; return true; } } // This is now completely useless. KF6: remove extern KSERVICE_EXPORT bool kservice_require_kded; KSERVICE_EXPORT bool kservice_require_kded = true; // If it returns true, we have a valid database and the stream has rewinded to the beginning // and past the version number. bool KSycocaPrivate::checkDatabase(BehaviorsIfNotFound ifNotFound) { if (databaseStatus == DatabaseOK) { if (checkVersion()) { // we know the version is ok, but we must rewind the stream anyway return true; } } closeDatabase(); // close the dummy one // Check if new database already available if (openDatabase(ifNotFound & IfNotFoundOpenDummy)) { // Database exists, and version is ok, we can read it. if (qAppName() != KBUILDSYCOCA_EXENAME && ifNotFound != IfNotFoundDoNothing) { // Ensure it's uptodate, rebuild if needed checkDirectories(); // Don't check again for some time m_lastCheck.start(); } return true; } if (ifNotFound & IfNotFoundRecreate) { return buildSycoca(); } return false; } QDataStream *KSycoca::findFactory(KSycocaFactoryId id) { // Ensure we have a valid database (right version, and rewinded to beginning) if (!d->checkDatabase(KSycocaPrivate::IfNotFoundRecreate)) { - return 0; + return nullptr; } QDataStream *str = stream(); Q_ASSERT(str); qint32 aId; qint32 aOffset; while (true) { *str >> aId; if (aId == 0) { qCWarning(SYCOCA) << "Error, KSycocaFactory (id =" << int(id) << ") not found!"; break; } *str >> aOffset; if (aId == id) { //qCDebug(SYCOCA) << "KSycoca::findFactory(" << id << ") offset " << aOffset; str->device()->seek(aOffset); return str; } } - return 0; + return nullptr; } bool KSycoca::needsRebuild() { return d->needsRebuild(); } KSycocaHeader KSycocaPrivate::readSycocaHeader() { KSycocaHeader header; // do not try to launch kbuildsycoca from here; this code is also called by kbuildsycoca. if (!checkDatabase(KSycocaPrivate::IfNotFoundDoNothing)) { return header; } QDataStream *str = stream(); qint64 oldPos = str->device()->pos(); Q_ASSERT(str); qint32 aId; qint32 aOffset; // skip factories offsets while (true) { *str >> aId; if (aId) { *str >> aOffset; } else { break; // just read 0 } } // We now point to the header QStringList directoryList; *str >> header >> directoryList; allResourceDirs.clear(); for (int i = 0; i < directoryList.count(); ++i) { qint64 mtime; *str >> mtime; allResourceDirs.insert(directoryList.at(i), mtime); } str->device()->seek(oldPos); timeStamp = header.timeStamp; // for the useless public accessors. KF6: remove these two lines, the accessors and the vars. language = header.language; updateSig = header.updateSignature; if (m_globalDatabase) { // The global database doesn't point to the user's local dirs, but we need to check them too // to react on something being created there const QString dataHome = QStandardPaths::writableLocation(QStandardPaths::GenericDataLocation); addLocalResourceDir(dataHome + QLatin1String("/kservices5")); addLocalResourceDir(dataHome + QLatin1String("/kservicetypes5")); addLocalResourceDir(dataHome + QLatin1String("/mime")); addLocalResourceDir(dataHome + QLatin1String("/applications")); } return header; } class TimestampChecker { public: TimestampChecker() : m_now(QDateTime::currentDateTime()) {} // Check times of last modification of all directories on which ksycoca depends, // If none of them is newer than the mtime we stored for that directory at the // last rebuild, this means that there's no need to rebuild ksycoca. bool checkTimestamps(const QMap &dirs) { Q_ASSERT(!dirs.isEmpty()); //qCDebug(SYCOCA) << "checking file timestamps"; for (auto it = dirs.begin(); it != dirs.end(); ++it) { const QString dir = it.key(); const qint64 lastStamp = it.value(); auto visitor = [&] (const QFileInfo &fi) { const QDateTime mtime = fi.lastModified(); if (mtime.toMSecsSinceEpoch() > lastStamp) { if (mtime > m_now) { qCDebug(SYCOCA) << fi.filePath() << "has a modification time in the future" << mtime; } qCDebug(SYCOCA) << "timestamp changed:" << fi.filePath() << mtime << ">" << QDateTime::fromMSecsSinceEpoch(lastStamp); // no need to continue search return false; } return true; }; if (!KSycocaUtilsPrivate::visitResourceDirectory(dir, visitor)) { return false; } } return true; } private: QDateTime m_now; }; void KSycocaPrivate::checkDirectories() { if (needsRebuild()) { buildSycoca(); } } bool KSycocaPrivate::needsRebuild() { if (!timeStamp && databaseStatus == DatabaseOK) { (void) readSycocaHeader(); } // these days timeStamp is really a "bool headerFound", the value itself doesn't matter... // KF6: replace it with bool. return timeStamp != 0 && !TimestampChecker().checkTimestamps(allResourceDirs); } bool KSycocaPrivate::buildSycoca() { KBuildSycoca builder; if (!builder.recreate()) { return false; // error } closeDatabase(); // close the dummy one // Ok, the new database should be here now, open it. if (!openDatabase()) { qCDebug(SYCOCA) << "Still no database..."; return false; // Still no database - uh oh } if (!checkVersion()) { qCDebug(SYCOCA) << "Still outdated..."; return false; // Still outdated - uh oh } return true; } quint32 KSycoca::timeStamp() { if (!d->timeStamp) { (void) d->readSycocaHeader(); } return d->timeStamp / 1000; // from ms to s } quint32 KSycoca::updateSignature() { if (!d->timeStamp) { (void) d->readSycocaHeader(); } return d->updateSig; } QString KSycoca::absoluteFilePath(DatabaseType type) { const QStringList paths = QStandardPaths::standardLocations(QStandardPaths::GenericDataLocation); QString suffix = QLatin1Char('_') + QLocale().bcp47Name(); if (type == GlobalDatabase) { const QString fileName = QStringLiteral("ksycoca5") + suffix; QString path = QStandardPaths::locate(QStandardPaths::GenericDataLocation, QStringLiteral("ksycoca/") + fileName); if (path.isEmpty()) { // No existing global DB found, maybe we are going to make a new one. // We don't want it in $HOME, so skip the first entry in and pick the second one. if (paths.count() == 1) { // unlikely return QString(); } return paths.at(1) + QStringLiteral("/ksycoca/") + fileName; } return path; } const QByteArray ksycoca_env = qgetenv("KDESYCOCA"); if (ksycoca_env.isEmpty()) { const QByteArray pathHash = QCryptographicHash::hash(paths.join(QString(QLatin1Char(':'))).toUtf8(), QCryptographicHash::Sha1); suffix += QLatin1Char('_') + QString::fromLatin1(pathHash.toBase64()); suffix.replace('/', '_'); #ifdef Q_OS_WIN suffix.replace(':', '_'); #endif const QString fileName = QStringLiteral("ksycoca5") + suffix; return QStandardPaths::writableLocation(QStandardPaths::GenericCacheLocation) + QLatin1Char('/') + fileName; } else { return QFile::decodeName(ksycoca_env); } } QString KSycoca::language() { if (d->language.isEmpty()) { (void) d->readSycocaHeader(); } return d->language; } QStringList KSycoca::allResourceDirs() { if (!d->timeStamp) { (void) d->readSycocaHeader(); } return d->allResourceDirs.keys(); } void KSycoca::flagError() { qCWarning(SYCOCA) << "ERROR: KSycoca database corruption!"; KSycoca *sycoca = self(); if (sycoca->d->readError) { return; } sycoca->d->readError = true; if (qAppName() != KBUILDSYCOCA_EXENAME && !sycoca->isBuilding()) { // Rebuild the damned thing. KBuildSycoca builder; (void)builder.recreate(); } } bool KSycoca::isBuilding() { return false; } void KSycoca::disableAutoRebuild() { qCWarning(SYCOCA) << "KSycoca::disableAutoRebuild() is internal, do not call it."; } QDataStream *&KSycoca::stream() { return d->stream(); } void KSycoca::connectNotify(const QMetaMethod &signal) { if (signal.name() == "databaseChanged" && !d->m_haveListeners) { d->m_haveListeners = true; if (d->m_databasePath.isEmpty()) { d->m_databasePath = d->findDatabase(); } else { d->m_fileWatcher.addFile(d->m_databasePath); } } } void KSycoca::clearCaches() { if (ksycocaInstance.exists() && ksycocaInstance()->hasSycoca()) ksycocaInstance()->sycoca()->d->closeDatabase(); } extern KSERVICE_EXPORT int ksycoca_ms_between_checks; KSERVICE_EXPORT int ksycoca_ms_between_checks = 1500; void KSycoca::ensureCacheValid() { if (qAppName() == KBUILDSYCOCA_EXENAME) { return; } if (d->databaseStatus != KSycocaPrivate::DatabaseOK) { if (!d->checkDatabase(KSycocaPrivate::IfNotFoundRecreate)) { return; } } if (d->m_lastCheck.isValid() && d->m_lastCheck.elapsed() < ksycoca_ms_between_checks) { return; } d->m_lastCheck.start(); // Check if the file on disk was modified since we last checked it. QFileInfo info(d->m_databasePath); if (info.lastModified() == d->m_dbLastModified) { // Check if the watched directories were modified, then the cache needs a rebuild. d->checkDirectories(); return; } // Close the database and forget all about what we knew. // The next call to any public method will recreate // everything that's needed. d->closeDatabase(); } diff --git a/src/sycoca/ksycocadevices_p.h b/src/sycoca/ksycocadevices_p.h index 2ce5a06..a52fe21 100644 --- a/src/sycoca/ksycocadevices_p.h +++ b/src/sycoca/ksycocadevices_p.h @@ -1,105 +1,105 @@ /* This file is part of the KDE project Copyright 2009 David Faure This library is free software; you can redistribute it and/or modify it under the terms of the GNU Library General Public License as published by the Free Software Foundation; either version 2 of the License or ( at your option ) version 3 or, at the discretion of KDE e.V. ( which shall act as a proxy as in section 14 of the GPLv3 ), any later version. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Library General Public License for more details. You should have received a copy of the GNU Library General Public License along with this library; see the file COPYING.LIB. If not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #ifndef KSYCOCADEVICES_P_H #define KSYCOCADEVICES_P_H #include #include #include // TODO: remove mmap() from kdewin32 and use QFile::mmap() when needed #ifdef Q_OS_WIN #undef HAVE_MMAP #endif class QString; class QDataStream; class QBuffer; class QFile; class QIODevice; class KMemFile; class KSycocaAbstractDevice { public: - KSycocaAbstractDevice() : m_stream(0) + KSycocaAbstractDevice() : m_stream(nullptr) { } virtual ~KSycocaAbstractDevice(); virtual QIODevice *device() = 0; QDataStream *&stream(); private: QDataStream *m_stream; }; #if HAVE_MMAP // Reading from a mmap'ed file class KSycocaMmapDevice : public KSycocaAbstractDevice { public: KSycocaMmapDevice(const char *sycoca_mmap, size_t sycoca_size); ~KSycocaMmapDevice(); QIODevice *device() Q_DECL_OVERRIDE; private: QBuffer *m_buffer; }; #endif // Reading from a QFile class KSycocaFileDevice : public KSycocaAbstractDevice { public: KSycocaFileDevice(const QString &path); ~KSycocaFileDevice(); QIODevice *device() Q_DECL_OVERRIDE; private: QFile *m_database; }; #ifndef QT_NO_SHAREDMEMORY // Reading from a KMemFile class KSycocaMemFileDevice : public KSycocaAbstractDevice { public: KSycocaMemFileDevice(const QString &path); ~KSycocaMemFileDevice(); QIODevice *device() Q_DECL_OVERRIDE; private: KMemFile *m_database; }; #endif // Reading from a dummy memory buffer class KSycocaBufferDevice : public KSycocaAbstractDevice { public: KSycocaBufferDevice(); ~KSycocaBufferDevice(); QIODevice *device() Q_DECL_OVERRIDE; private: QBuffer *m_buffer; }; #endif /* KSYCOCADEVICES_P_H */ diff --git a/src/sycoca/ksycocadict.cpp b/src/sycoca/ksycocadict.cpp index d01be82..fa6da68 100644 --- a/src/sycoca/ksycocadict.cpp +++ b/src/sycoca/ksycocadict.cpp @@ -1,569 +1,569 @@ /* This file is part of the KDE libraries * Copyright (C) 1999 Waldo Bastian * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Library General Public * License version 2 as published by the Free Software Foundation; * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Library General Public License for more details. * * You should have received a copy of the GNU Library General Public License * along with this library; see the file COPYING.LIB. If not, write to * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, * Boston, MA 02110-1301, USA. **/ #include "ksycocadict_p.h" #include "sycocadebug.h" #include #include "ksycocaentry.h" #include "ksycoca.h" #include #include #include namespace { struct string_entry { string_entry(const QString &_key, const KSycocaEntry::Ptr &_payload) : hash(0), length(_key.length()), keyStr(_key), key(keyStr.unicode()), payload(_payload) {} uint hash; const int length; const QString keyStr; const QChar *const key; // always points to keyStr.unicode(); just an optimization const KSycocaEntry::Ptr payload; }; } class KSycocaDictStringList : public QList { public: ~KSycocaDictStringList() { qDeleteAll(*this); } }; class KSycocaDictPrivate { public: KSycocaDictPrivate() - : stream(0) + : stream(nullptr) , offset(0) , hashTableSize(0) { } ~KSycocaDictPrivate() { } // Helper for find_string and findMultiString qint32 offsetForKey(const QString &key) const; // Calculate hash - can be used during loading and during saving. quint32 hashKey(const QString &key) const; KSycocaDictStringList stringlist; QDataStream *stream; qint64 offset; quint32 hashTableSize; QList hashList; }; KSycocaDict::KSycocaDict() : d(new KSycocaDictPrivate) { } KSycocaDict::KSycocaDict(QDataStream *str, int offset) : d(new KSycocaDictPrivate) { d->stream = str; d->offset = offset; quint32 test1, test2; str->device()->seek(offset); (*str) >> test1 >> test2; if ((test1 > 0x000fffff) || (test2 > 1024)) { KSycoca::flagError(); d->hashTableSize = 0; d->offset = 0; return; } str->device()->seek(offset); (*str) >> d->hashTableSize; (*str) >> d->hashList; d->offset = str->device()->pos(); // Start of hashtable } KSycocaDict::~KSycocaDict() { delete d; } void KSycocaDict::add(const QString &key, const KSycocaEntry::Ptr &payload) { if (key.isEmpty()) { return; // Not allowed (should never happen) } if (!payload) { return; // Not allowed! } d->stringlist.append(new string_entry(key, payload)); } void KSycocaDict::remove(const QString &key) { if (!d) { return; } bool found = false; for (KSycocaDictStringList::Iterator it = d->stringlist.begin(); it != d->stringlist.end(); ++it) { string_entry *entry = *it; if (entry->keyStr == key) { d->stringlist.erase(it); delete entry; found = true; break; } } if (!found) { qDebug() << "key not found:" << key; } } int KSycocaDict::find_string(const QString &key) const { Q_ASSERT(d); //qDebug() << QString("KSycocaDict::find_string(%1)").arg(key); qint32 offset = d->offsetForKey(key); //qDebug() << QString("offset is %1").arg(offset,8,16); if (offset == 0) { return 0; } if (offset > 0) { return offset; // Positive ID } // Lookup duplicate list. offset = -offset; d->stream->device()->seek(offset); //qDebug() << QString("Looking up duplicate list at %1").arg(offset,8,16); while (true) { (*d->stream) >> offset; if (offset == 0) { break; } QString dupkey; (*d->stream) >> dupkey; //qDebug() << QString(">> %1 %2").arg(offset,8,16).arg(dupkey); if (dupkey == key) { return offset; } } //qDebug() << "Not found!"; return 0; } QList KSycocaDict::findMultiString(const QString &key) const { qint32 offset = d->offsetForKey(key); QList offsetList; if (offset == 0) { return offsetList; } if (offset > 0) { // Positive ID: one entry found offsetList.append(offset); return offsetList; } // Lookup duplicate list. offset = -offset; d->stream->device()->seek(offset); //qDebug() << QString("Looking up duplicate list at %1").arg(offset,8,16); while (true) { (*d->stream) >> offset; if (offset == 0) { break; } QString dupkey; (*d->stream) >> dupkey; //qDebug() << QString(">> %1 %2").arg(offset,8,16).arg(dupkey); if (dupkey == key) { offsetList.append(offset); } } return offsetList; } uint KSycocaDict::count() const { if (!d) { return 0; } return d->stringlist.count(); } void KSycocaDict::clear() { delete d; - d = 0; + d = nullptr; } uint KSycocaDictPrivate::hashKey(const QString &key) const { int len = key.length(); uint h = 0; for (int i = 0; i < hashList.count(); i++) { int pos = hashList[i]; if (pos == 0) { continue; } else if (pos < 0) { pos = -pos; if (pos < len) { h = ((h * 13) + (key[len - pos].cell() % 29)) & 0x3ffffff; } } else { pos = pos - 1; if (pos < len) { h = ((h * 13) + (key[pos].cell() % 29)) & 0x3ffffff; } } } return h; } // If we have the strings // hello // world // kde // Then we end up with // ABCDE // where A = diversity of 'h' + 'w' + 'k' etc. // Also, diversity(-2) == 'l'+'l'+'d' (second character from the end) // The hasList is used for hashing: // hashList = (-2, 1, 3) means that the hash key comes from // the 2nd character from the right, then the 1st from the left, then the 3rd from the left. // Calculate the diversity of the strings at position 'pos' // NOTE: this code is slow, it takes 12% of the _overall_ `kbuildsycoca5 --noincremental` running time static int calcDiversity(KSycocaDictStringList* stringlistp, int inPos, uint sz) { if (inPos == 0) { return 0; } KSycocaDictStringList& stringlist = *stringlistp; QBitArray matrix(sz); int pos; //static const int s_maxItems = 50; //int numItem = 0; if (inPos < 0) { pos = -inPos; for (auto it = stringlist.constBegin(), end = stringlist.constEnd(); it != end; ++it) { string_entry *entry = *it; int rpos = entry->length - pos; if (rpos > 0) { uint hash = ((entry->hash * 13) + (entry->key[rpos].cell() % 29)) & 0x3ffffff; matrix.setBit(hash % sz, true); } //if (++numItem == s_maxItems) // break; } } else { pos = inPos - 1; for (auto it = stringlist.constBegin(), end = stringlist.constEnd(); it != end; ++it) { string_entry *entry = *it; if (pos < entry->length) { uint hash = ((entry->hash * 13) + (entry->key[pos].cell() % 29)) & 0x3ffffff; matrix.setBit(hash % sz, true); } //if (++numItem == s_maxItems) // break; } } return matrix.count(true); } // // Add the diversity of the strings at position 'pos' static void addDiversity(KSycocaDictStringList* stringlistp, int pos) { if (pos == 0) { return; } KSycocaDictStringList& stringlist = *stringlistp; if (pos < 0) { pos = -pos; for (auto it = stringlist.constBegin(), end = stringlist.constEnd(); it != end; ++it) { string_entry *entry = *it; int rpos = entry->length - pos; if (rpos > 0) { entry->hash = ((entry->hash * 13) + (entry->key[rpos].cell() % 29)) & 0x3fffffff; } } } else { pos = pos - 1; for (auto it = stringlist.constBegin(), end = stringlist.constEnd(); it != end; ++it) { string_entry *entry = *it; if (pos < entry->length) { entry->hash = ((entry->hash * 13) + (entry->key[pos].cell() % 29)) & 0x3fffffff; } } } } void KSycocaDict::save(QDataStream &str) { if (count() == 0) { d->hashTableSize = 0; d->hashList.clear(); str << d->hashTableSize; str << d->hashList; return; } d->offset = str.device()->pos(); //qDebug() << "KSycocaDict:" << count() << "entries."; //qDebug() << "Calculating hash keys.."; int maxLength = 0; //qDebug() << "Finding maximum string length"; for (KSycocaDictStringList::const_iterator it = d->stringlist.constBegin(); it != d->stringlist.constEnd(); ++it) { string_entry *entry = *it; entry->hash = 0; if (entry->length > maxLength) { maxLength = entry->length; } } //qDebug() << "Max string length=" << maxLength << "existing hashList=" << d->hashList; // use "almost prime" number for sz (to calculate diversity) and later // for the table size of big tables // int sz = d->stringlist.count()*5-1; unsigned int sz = count() * 4 + 1; while (!(((sz % 3) && (sz % 5) && (sz % 7) && (sz % 11) && (sz % 13)))) { sz += 2; } d->hashList.clear(); // Times (with warm caches, i.e. after multiple runs) // kbuildsycoca5 --noincremental 2.83s user 0.20s system 95% cpu 3.187 total // kbuildsycoca5 --noincremental 2.74s user 0.25s system 93% cpu 3.205 total // unittest: 0.50-60 msec per iteration / 0.40-50 msec per iteration // Now that MimeTypes are not parsed anymore: // kbuildsycoca5 --noincremental 2.18s user 0.30s system 91% cpu 2.719 total // kbuildsycoca5 --noincremental 2.07s user 0.34s system 89% cpu 2.681 total // If I enabled s_maxItems = 50, it goes down to // but I don't know if that's a good idea. // kbuildsycoca5 --noincremental 1.73s user 0.31s system 85% cpu 2.397 total // kbuildsycoca5 --noincremental 1.84s user 0.29s system 95% cpu 2.230 total // try to limit diversity scan by "predicting" positions // with high diversity QVector oldvec(maxLength * 2 + 1); oldvec.fill(0); int mindiv = 0; int lastDiv = 0; while (true) { int divsum = 0, divnum = 0; int maxDiv = 0; int maxPos = 0; for (int pos = -maxLength; pos <= maxLength; ++pos) { // cut off if (oldvec[pos + maxLength] < mindiv) { oldvec[pos + maxLength] = 0; continue; } const int diversity = calcDiversity(&(d->stringlist), pos, sz); if (diversity > maxDiv) { maxDiv = diversity; maxPos = pos; } oldvec[pos + maxLength] = diversity; divsum += diversity; ++divnum; } // arbitrary cut-off value 3/4 of average seems to work if (divnum) { mindiv = (3 * divsum) / (4 * divnum); } if (maxDiv <= lastDiv) { break; } //qDebug() << "Max Div=" << maxDiv << "at pos" << maxPos; lastDiv = maxDiv; addDiversity(&(d->stringlist), maxPos); d->hashList.append(maxPos); } for (auto it = d->stringlist.constBegin(); it != d->stringlist.constEnd(); ++it) { (*it)->hash = d->hashKey((*it)->keyStr); } // fprintf(stderr, "Calculating minimum table size..\n"); d->hashTableSize = sz; //qDebug() << "hashTableSize=" << sz << "hashList=" << d->hashList << "oldvec=" << oldvec; struct hashtable_entry { string_entry *entry; QList *duplicates; qint64 duplicate_offset; }; hashtable_entry *hashTable = new hashtable_entry[ sz ]; //qDebug() << "Clearing hashtable..."; for (unsigned int i = 0; i < sz; i++) { - hashTable[i].entry = 0; - hashTable[i].duplicates = 0; + hashTable[i].entry = nullptr; + hashTable[i].duplicates = nullptr; } //qDebug() << "Filling hashtable..."; for (auto it = d->stringlist.constBegin(); it != d->stringlist.constEnd(); ++it) { string_entry *entry = *it; //qDebug() << "entry keyStr=" << entry->keyStr << entry->payload.data() << entry->payload->entryPath(); int hash = entry->hash % sz; if (!hashTable[hash].entry) { // First entry hashTable[hash].entry = entry; } else { if (!hashTable[hash].duplicates) { // Second entry, build duplicate list. hashTable[hash].duplicates = new QList; hashTable[hash].duplicates->append(hashTable[hash].entry); hashTable[hash].duplicate_offset = 0; } hashTable[hash].duplicates->append(entry); } } str << d->hashTableSize; str << d->hashList; d->offset = str.device()->pos(); // d->offset points to start of hashTable //qDebug() << QString("Start of Hash Table, offset = %1").arg(d->offset,8,16); // Write the hashtable + the duplicates twice. // The duplicates are after the normal hashtable, but the offset of each // duplicate entry is written into the normal hashtable. for (int pass = 1; pass <= 2; pass++) { str.device()->seek(d->offset); //qDebug() << QString("Writing hash table (pass #%1)").arg(pass); for (uint i = 0; i < d->hashTableSize; i++) { qint32 tmpid; if (!hashTable[i].entry) { tmpid = 0; } else if (!hashTable[i].duplicates) { tmpid = hashTable[i].entry->payload->offset(); // Positive ID } else { tmpid = - hashTable[i].duplicate_offset; // Negative ID } str << tmpid; //qDebug() << QString("Hash table : %1").arg(tmpid,8,16); } //qDebug() << QString("End of Hash Table, offset = %1").arg(str.device()->at(),8,16); //qDebug() << QString("Writing duplicate lists (pass #%1)").arg(pass); for (uint i = 0; i < d->hashTableSize; i++) { const QList *dups = hashTable[i].duplicates; if (dups) { hashTable[i].duplicate_offset = str.device()->pos(); /*qDebug() << QString("Duplicate lists: Offset = %1 list_size = %2") .arg(hashTable[i].duplicate_offset,8,16).arg(dups->count()); */ Q_FOREACH (string_entry* dup, *dups) { const qint32 offset = dup->payload->offset(); if (!offset) { const QString storageId = dup->payload->storageId(); qDebug() << "about to assert! dict=" << this << "storageId=" << storageId << dup->payload.data(); if (dup->payload->isType(KST_KService)) { KService::Ptr service(static_cast(dup->payload.data())); qDebug() << service->storageId() << service->entryPath(); } // save() must have been called on the entry Q_ASSERT_X(offset, "KSycocaDict::save", QByteArray("entry offset is 0, save() was not called on " + dup->payload->storageId().toLatin1() + " entryPath=" + dup->payload->entryPath().toLatin1()).constData() ); } str << offset; // Positive ID str << dup->keyStr; // Key (QString) } str << qint32(0); // End of list marker (0) } } //qDebug() << QString("End of Dict, offset = %1").arg(str.device()->at(),8,16); } //qDebug() << "Cleaning up hash table."; for (uint i = 0; i < d->hashTableSize; i++) { delete hashTable[i].duplicates; } delete [] hashTable; } qint32 KSycocaDictPrivate::offsetForKey(const QString &key) const { if (!stream || !offset) { qCWarning(SYCOCA) << "No ksycoca database available! Tried running" << KBUILDSYCOCA_EXENAME << "?"; return 0; } if (hashTableSize == 0) { return 0; // Unlikely to find anything :-] } // Read hash-table data const uint hash = hashKey(key) % hashTableSize; //qDebug() << "hash is" << hash; const qint32 off = offset + sizeof(qint32) * hash; //qDebug() << QString("off is %1").arg(off,8,16); stream->device()->seek(off); qint32 retOffset; (*stream) >> retOffset; return retOffset; } diff --git a/src/sycoca/ksycocaentry.cpp b/src/sycoca/ksycocaentry.cpp index 310d11b..5a4c627 100644 --- a/src/sycoca/ksycocaentry.cpp +++ b/src/sycoca/ksycocaentry.cpp @@ -1,119 +1,119 @@ /* This file is part of the KDE libraries * Copyright (C) 1999 Waldo Bastian * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Library General Public * License version 2 as published by the Free Software Foundation; * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Library General Public License for more details. * * You should have received a copy of the GNU Library General Public License * along with this library; see the file COPYING.LIB. If not, write to * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, * Boston, MA 02110-1301, USA. **/ #include "ksycocaentry.h" #include "ksycocaentry_p.h" #include "ksycocautils_p.h" #include KSycocaEntryPrivate::KSycocaEntryPrivate(QDataStream &_str, int iOffset) : offset(iOffset), deleted(false) { _str >> path; } KSycocaEntry::KSycocaEntry() - : d_ptr(0) + : d_ptr(nullptr) { } KSycocaEntry::KSycocaEntry(KSycocaEntryPrivate &d) : d_ptr(&d) { } KSycocaEntry::~KSycocaEntry() { delete d_ptr; } bool KSycocaEntry::isType(KSycocaType t) const { return d_ptr->isType(t); } KSycocaType KSycocaEntry::sycocaType() const { return d_ptr->sycocaType(); } QString KSycocaEntry::entryPath() const { Q_D(const KSycocaEntry); return d->path; } QString KSycocaEntry::storageId() const { Q_D(const KSycocaEntry); return d->storageId(); } bool KSycocaEntry::isDeleted() const { Q_D(const KSycocaEntry); return d->deleted; } void KSycocaEntry::setDeleted(bool deleted) { Q_D(KSycocaEntry); d->deleted = deleted; } bool KSycocaEntry::isSeparator() const { - return d_ptr == 0 || isType(KST_KServiceSeparator); + return d_ptr == nullptr || isType(KST_KServiceSeparator); } int KSycocaEntry::offset() const { Q_D(const KSycocaEntry); return d->offset; } void KSycocaEntryPrivate::save(QDataStream &s) { offset = s.device()->pos(); // store position in member variable s << qint32(sycocaType()) << path; } bool KSycocaEntry::isValid() const { Q_D(const KSycocaEntry); return d && d->isValid(); } QString KSycocaEntry::name() const { Q_D(const KSycocaEntry); return d->name(); } QStringList KSycocaEntry::propertyNames() const { Q_D(const KSycocaEntry); return d->propertyNames(); } QVariant KSycocaEntry::property(const QString &name) const { Q_D(const KSycocaEntry); return d->property(name); } diff --git a/src/sycoca/ksycocafactory.cpp b/src/sycoca/ksycocafactory.cpp index d9f53de..c6acfef 100644 --- a/src/sycoca/ksycocafactory.cpp +++ b/src/sycoca/ksycocafactory.cpp @@ -1,268 +1,268 @@ /* This file is part of the KDE libraries * Copyright (C) 1999 David Faure * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Library General Public * License version 2 as published by the Free Software Foundation; * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Library General Public License for more details. * * You should have received a copy of the GNU Library General Public License * along with this library; see the file COPYING.LIB. If not, write to * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, * Boston, MA 02110-1301, USA. */ #include "ksycocafactory_p.h" #include "ksycoca.h" #include "ksycocatype.h" #include "ksycocaentry.h" #include "ksycocaentry_p.h" #include "ksycocadict_p.h" #include "sycocadebug.h" #include #include #include class KSycocaFactoryPrivate { public: KSycocaFactoryPrivate() : mOffset(0), m_sycocaDictOffset(0), m_beginEntryOffset(0), m_endEntryOffset(0) {} ~KSycocaFactoryPrivate() { delete m_sycocaDict; } int mOffset; int m_sycocaDictOffset; int m_beginEntryOffset; int m_endEntryOffset; KSycocaDict *m_sycocaDict; }; KSycocaFactory::KSycocaFactory(KSycocaFactoryId factory_id, KSycoca *sycoca) - : m_resourceList(0), m_entryDict(0), m_str(0), m_sycoca(sycoca), d(new KSycocaFactoryPrivate) + : m_resourceList(nullptr), m_entryDict(nullptr), m_str(nullptr), m_sycoca(sycoca), d(new KSycocaFactoryPrivate) { if (!m_sycoca->isBuilding() && (m_str = m_sycoca->findFactory(factory_id))) { // Read position of index tables.... qint32 i; (*m_str) >> i; d->m_sycocaDictOffset = i; (*m_str) >> i; d->m_beginEntryOffset = i; (*m_str) >> i; d->m_endEntryOffset = i; QDataStream *str = stream(); int saveOffset = str->device()->pos(); // Init index tables d->m_sycocaDict = new KSycocaDict(str, d->m_sycocaDictOffset); saveOffset = str->device()->seek(saveOffset); } else { // We are in kbuildsycoca -- build new database! m_entryDict = new KSycocaEntryDict; d->m_sycocaDict = new KSycocaDict; d->m_beginEntryOffset = 0; d->m_endEntryOffset = 0; // m_resourceList will be filled in by inherited constructors } m_sycoca->addFactory(this); } KSycocaFactory::~KSycocaFactory() { delete m_entryDict; delete d; } void KSycocaFactory::saveHeader(QDataStream &str) { // Write header str.device()->seek(d->mOffset); str << qint32(d->m_sycocaDictOffset); str << qint32(d->m_beginEntryOffset); str << qint32(d->m_endEntryOffset); } void KSycocaFactory::save(QDataStream &str) { if (!m_entryDict) { return; // Error! Function should only be called when } // building database if (!d->m_sycocaDict) { return; // Error! } d->mOffset = str.device()->pos(); // store position in member variable d->m_sycocaDictOffset = 0; // Write header (pass #1) saveHeader(str); d->m_beginEntryOffset = str.device()->pos(); // Write all entries. int entryCount = 0; Q_FOREACH(KSycocaEntry::Ptr entry, *m_entryDict) { entry->d_ptr->save(str); entryCount++; } d->m_endEntryOffset = str.device()->pos(); // Write indices... // Linear index str << qint32(entryCount); Q_FOREACH(KSycocaEntry::Ptr entry, *m_entryDict) { str << qint32(entry.data()->offset()); } // Dictionary index d->m_sycocaDictOffset = str.device()->pos(); d->m_sycocaDict->save(str); int endOfFactoryData = str.device()->pos(); // Update header (pass #2) saveHeader(str); // Seek to end. str.device()->seek(endOfFactoryData); } void KSycocaFactory::addEntry(const KSycocaEntry::Ptr &newEntry) { if (!m_entryDict) { return; // Error! Function should only be called when } // building database if (!d->m_sycocaDict) { return; // Error! } KSycocaEntry::Ptr oldEntry = m_entryDict->value(newEntry->storageId()); if (oldEntry) { // Already exists -> replace // We found a more-local override, e.g. ~/.local/share/applications/kde5/foo.desktop // So forget about the more global file. // // This can also happen with two .protocol files using the same protocol= entry. // If we didn't remove one here, we would end up asserting because save() // wasn't called on one of the entries. //qDebug() << "removing" << oldEntry.data() << oldEntry->entryPath() << "because of" << newEntry->entryPath() << "they have the same storageId" << newEntry->storageId(); removeEntry(newEntry->storageId()); } const QString name = newEntry->storageId(); m_entryDict->insert(name, newEntry); d->m_sycocaDict->add(name, newEntry); } void KSycocaFactory::removeEntry(const QString &entryName) { if (!m_entryDict) { return; // Error! Function should only be called when } // building database if (!d->m_sycocaDict) { return; // Error! } m_entryDict->remove(entryName); d->m_sycocaDict->remove(entryName); // O(N) } KSycocaEntry::List KSycocaFactory::allEntries() const { KSycocaEntry::List list; // Assume we're NOT building a database QDataStream *str = stream(); if (!str) { return list; } str->device()->seek(d->m_endEntryOffset); qint32 entryCount; (*str) >> entryCount; if (entryCount > 8192) { qCWarning(SYCOCA) << QThread::currentThread() << "error detected in factory" << this; KSycoca::flagError(); return list; } // offsetList is needed because createEntry() modifies the stream position qint32 *offsetList = new qint32[entryCount]; for (int i = 0; i < entryCount; i++) { (*str) >> offsetList[i]; } for (int i = 0; i < entryCount; i++) { KSycocaEntry *newEntry = createEntry(offsetList[i]); if (newEntry) { list.append(KSycocaEntry::Ptr(newEntry)); } } delete [] offsetList; return list; } int KSycocaFactory::offset() const { return d->mOffset; } const KSycocaResourceList *KSycocaFactory::resourceList() const { return m_resourceList; } const KSycocaDict *KSycocaFactory::sycocaDict() const { return d->m_sycocaDict; } bool KSycocaFactory::isEmpty() const { return d->m_beginEntryOffset == d->m_endEntryOffset; } QDataStream *KSycocaFactory::stream() const { return m_str; } QStringList KSycocaFactory::allDirectories(const QString &subdir) { // We don't use QStandardPaths::locateAll() because we want all paths, even those that don't exist yet const QStringList topDirs = QStandardPaths::standardLocations(QStandardPaths::GenericDataLocation); QStringList dirs; dirs.reserve(topDirs.size()); for (QStringList::const_iterator dir = topDirs.constBegin(); dir != topDirs.constEnd(); ++dir) { dirs.append(*dir + QLatin1Char('/') + subdir); } return dirs; } void KSycocaFactory::virtual_hook(int /*id*/, void * /*data*/) { /*BASE::virtual_hook( id, data );*/ } diff --git a/src/sycoca/vfolder_menu.cpp b/src/sycoca/vfolder_menu.cpp index 90248bb..2d14501 100644 --- a/src/sycoca/vfolder_menu.cpp +++ b/src/sycoca/vfolder_menu.cpp @@ -1,1437 +1,1437 @@ /* This file is part of the KDE libraries * Copyright (C) 2003 Waldo Bastian * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Library General Public * License version 2 as published by the Free Software Foundation; * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Library General Public License for more details. * * You should have received a copy of the GNU Library General Public License * along with this library; see the file COPYING.LIB. If not, write to * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, * Boston, MA 02110-1301, USA. **/ #include "vfolder_menu_p.h" #include "kbuildservicefactory_p.h" #include "kbuildsycocainterface_p.h" #include "sycocadebug.h" #include #include #include #include #include #include #include #include static void foldNode(QDomElement &docElem, QDomElement &e, QMap &dupeList, QString s = QString()) //krazy:exclude=passbyvalue { if (s.isEmpty()) { s = e.text(); } QMap::iterator it = dupeList.find(s); if (it != dupeList.end()) { //qDebug() << e.tagName() << "and" << s << "requires combining!"; docElem.removeChild(*it); dupeList.erase(it); } dupeList.insert(s, e); } static void replaceNode(QDomElement &docElem, QDomNode &n, const QStringList &list, const QString &tag) { for (QStringList::ConstIterator it = list.begin(); it != list.end(); ++it) { QDomElement e = docElem.ownerDocument().createElement(tag); QDomText txt = docElem.ownerDocument().createTextNode(*it); e.appendChild(txt); docElem.insertAfter(e, n); } QDomNode next = n.nextSibling(); docElem.removeChild(n); n = next; // qDebug() << "Next tag = " << n.toElement().tagName(); } void VFolderMenu::registerFile(const QString &file) { int i = file.lastIndexOf('/'); if (i < 0) { return; } QString dir = file.left(i + 1); // Include trailing '/' registerDirectory(dir); } void VFolderMenu::registerDirectory(const QString &directory) { m_allDirectories.append(directory); } QStringList VFolderMenu::allDirectories() { if (m_allDirectories.isEmpty()) { return m_allDirectories; } m_allDirectories.sort(); QStringList::Iterator it = m_allDirectories.begin(); QString previous = *it++; for (; it != m_allDirectories.end();) { #ifndef Q_OS_WIN if ((*it).startsWith(previous)) #else if ((*it).startsWith(previous, Qt::CaseInsensitive)) #endif { it = m_allDirectories.erase(it); } else { previous = *it; ++it; } } return m_allDirectories; } static void track(const QString &menuId, const QString &menuName, const QHash &includeList, const QHash &excludeList, const QHash &itemList, const QString &comment) { if (itemList.contains(menuId)) { printf("%s: %s INCL %d EXCL %d\n", qPrintable(menuName), qPrintable(comment), includeList.contains(menuId) ? 1 : 0, excludeList.contains(menuId) ? 1 : 0); } } void VFolderMenu::includeItems(QHash &items1, const QHash &items2) { foreach (const KService::Ptr &p, items2) { items1.insert(p->menuId(), p); } } void VFolderMenu::matchItems(QHash &items1, const QHash &items2) { foreach (const KService::Ptr &p, items1) { QString id = p->menuId(); if (!items2.contains(id)) { items1.remove(id); } } } void VFolderMenu::excludeItems(QHash &items1, const QHash &items2) { foreach (const KService::Ptr &p, items2) { items1.remove(p->menuId()); } } VFolderMenu::SubMenu * VFolderMenu::takeSubMenu(SubMenu *parentMenu, const QString &menuName) { const int i = menuName.indexOf('/'); const QString s1 = i > 0 ? menuName.left(i) : menuName; const QString s2 = menuName.mid(i + 1); // Look up menu for (QList::Iterator it = parentMenu->subMenus.begin(); it != parentMenu->subMenus.end(); ++it) { SubMenu *menu = *it; if (menu->name == s1) { if (i == -1) { // Take it out parentMenu->subMenus.erase(it); return menu; } else { return takeSubMenu(menu, s2); } } } - return 0; // Not found + return nullptr; // Not found } void VFolderMenu::mergeMenu(SubMenu *menu1, SubMenu *menu2, bool reversePriority) { if (m_track) { track(m_trackId, menu1->name, menu1->items, menu1->excludeItems, menu2->items, QStringLiteral("Before MenuMerge w. %1 (incl)").arg(menu2->name)); track(m_trackId, menu1->name, menu1->items, menu1->excludeItems, menu2->excludeItems, QStringLiteral("Before MenuMerge w. %1 (excl)").arg(menu2->name)); } if (reversePriority) { // Merge menu1 with menu2, menu1 takes precedent excludeItems(menu2->items, menu1->excludeItems); includeItems(menu1->items, menu2->items); excludeItems(menu2->excludeItems, menu1->items); includeItems(menu1->excludeItems, menu2->excludeItems); } else { // Merge menu1 with menu2, menu2 takes precedent excludeItems(menu1->items, menu2->excludeItems); includeItems(menu1->items, menu2->items); includeItems(menu1->excludeItems, menu2->excludeItems); menu1->isDeleted = menu2->isDeleted; } while (!menu2->subMenus.isEmpty()) { SubMenu *subMenu = menu2->subMenus.takeFirst(); insertSubMenu(menu1, subMenu->name, subMenu, reversePriority); } if (reversePriority) { // Merge menu1 with menu2, menu1 takes precedent if (menu1->directoryFile.isEmpty()) { menu1->directoryFile = menu2->directoryFile; } if (menu1->defaultLayoutNode.isNull()) { menu1->defaultLayoutNode = menu2->defaultLayoutNode; } if (menu1->layoutNode.isNull()) { menu1->layoutNode = menu2->layoutNode; } } else { // Merge menu1 with menu2, menu2 takes precedent if (!menu2->directoryFile.isEmpty()) { menu1->directoryFile = menu2->directoryFile; } if (!menu2->defaultLayoutNode.isNull()) { menu1->defaultLayoutNode = menu2->defaultLayoutNode; } if (!menu2->layoutNode.isNull()) { menu1->layoutNode = menu2->layoutNode; } } if (m_track) { track(m_trackId, menu1->name, menu1->items, menu1->excludeItems, menu2->items, QStringLiteral("After MenuMerge w. %1 (incl)").arg(menu2->name)); track(m_trackId, menu1->name, menu1->items, menu1->excludeItems, menu2->excludeItems, QStringLiteral("After MenuMerge w. %1 (excl)").arg(menu2->name)); } delete menu2; } void VFolderMenu::insertSubMenu(SubMenu *parentMenu, const QString &menuName, SubMenu *newMenu, bool reversePriority) { const int i = menuName.indexOf('/'); const QString s1 = menuName.left(i); const QString s2 = menuName.mid(i + 1); // Look up menu foreach (SubMenu *menu, parentMenu->subMenus) { if (menu->name == s1) { if (i == -1) { mergeMenu(menu, newMenu, reversePriority); return; } else { insertSubMenu(menu, s2, newMenu, reversePriority); return; } } } if (i == -1) { // Add it here newMenu->name = menuName; parentMenu->subMenus.append(newMenu); } else { SubMenu *menu = new SubMenu; menu->name = s1; parentMenu->subMenus.append(menu); insertSubMenu(menu, s2, newMenu); } } void VFolderMenu::insertService(SubMenu *parentMenu, const QString &name, KService::Ptr newService) { const int i = name.indexOf('/'); if (i == -1) { // Add it here parentMenu->items.insert(newService->menuId(), newService); return; } QString s1 = name.left(i); QString s2 = name.mid(i + 1); // Look up menu foreach (SubMenu *menu, parentMenu->subMenus) { if (menu->name == s1) { insertService(menu, s2, newService); return; } } SubMenu *menu = new SubMenu; menu->name = s1; parentMenu->subMenus.append(menu); insertService(menu, s2, newService); } VFolderMenu::VFolderMenu(KServiceFactory *serviceFactory, KBuildSycocaInterface *kbuildsycocaInterface) - : m_appsInfo(0) - , m_rootMenu(0) - , m_currentMenu(0) + : m_appsInfo(nullptr) + , m_rootMenu(nullptr) + , m_currentMenu(nullptr) , m_track(false) , m_serviceFactory(serviceFactory) , m_kbuildsycocaInterface(kbuildsycocaInterface) { m_usedAppsDict.reserve(797); initDirs(); } VFolderMenu::~VFolderMenu() { qDeleteAll(m_appsInfoList); delete m_rootMenu; } #define FOR_ALL_APPLICATIONS(it) \ foreach (AppsInfo *info, m_appsInfoStack) \ { \ QHashIterator it = info->applications; \ while (it.hasNext()) \ { \ it.next(); #define FOR_ALL_APPLICATIONS_END } } #define FOR_CATEGORY(category, it) \ foreach (AppsInfo *info, m_appsInfoStack) \ { \ const KService::List list = info->dictCategories.value(category); \ for(KService::List::ConstIterator it = list.constBegin(); \ it != list.constEnd(); ++it) \ { #define FOR_CATEGORY_END } } KService::Ptr VFolderMenu::findApplication(const QString &relPath) { foreach (AppsInfo *info, m_appsInfoStack) { if (info->applications.contains(relPath)) { KService::Ptr s = info->applications[relPath]; if (s) { return s; } } } return KService::Ptr(); } void VFolderMenu::addApplication(const QString &id, KService::Ptr service) { service->setMenuId(id); m_appsInfo->applications.insert(id, service); // replaces, if already there m_serviceFactory->addEntry(KSycocaEntry::Ptr(service)); } void VFolderMenu::buildApplicationIndex(bool unusedOnly) { foreach (AppsInfo *info, m_appsInfoList) { info->dictCategories.clear(); QMutableHashIterator it = info->applications; while (it.hasNext()) { KService::Ptr s = it.next().value(); if (unusedOnly && m_usedAppsDict.contains(s->menuId())) { // Remove and skip this one it.remove(); continue; } Q_FOREACH (const QString &cat, s->categories()) { info->dictCategories[cat].append(s); // find or insert entry in hash } } } } void VFolderMenu::createAppsInfo() { if (m_appsInfo) { return; } m_appsInfo = new AppsInfo; m_appsInfoStack.prepend(m_appsInfo); m_appsInfoList.append(m_appsInfo); m_currentMenu->apps_info = m_appsInfo; } void VFolderMenu::loadAppsInfo() { m_appsInfo = m_currentMenu->apps_info; if (!m_appsInfo) { return; // No appsInfo for this menu } if (!m_appsInfoStack.isEmpty() && m_appsInfoStack.first() == m_appsInfo) { return; // Already added (By createAppsInfo?) } m_appsInfoStack.prepend(m_appsInfo); // Add } void VFolderMenu::unloadAppsInfo() { m_appsInfo = m_currentMenu->apps_info; if (!m_appsInfo) { return; // No appsInfo for this menu } if (m_appsInfoStack.first() != m_appsInfo) { return; // Already removed (huh?) } m_appsInfoStack.removeAll(m_appsInfo); // Remove - m_appsInfo = 0; + m_appsInfo = nullptr; } QString VFolderMenu::absoluteDir(const QString &_dir, const QString &baseDir, bool keepRelativeToCfg) { QString dir = _dir; if (QDir::isRelativePath(dir)) { dir = baseDir + dir; } bool relative = QDir::isRelativePath(dir); if (relative && !keepRelativeToCfg) { relative = false; dir = QStandardPaths::locate(QStandardPaths::GenericConfigLocation, QStringLiteral("menus/") + dir, QStandardPaths::LocateDirectory); } if (!relative) { QString resolved = QDir(dir).canonicalPath(); if (!resolved.isEmpty()) { dir = resolved; } } if (!dir.endsWith('/')) { dir += '/'; } return dir; } static void tagBaseDir(QDomDocument &doc, const QString &tag, const QString &dir) { QDomNodeList mergeFileList = doc.elementsByTagName(tag); for (int i = 0; i < mergeFileList.count(); i++) { QDomAttr attr = doc.createAttribute(QStringLiteral("__BaseDir")); attr.setValue(dir); mergeFileList.item(i).toElement().setAttributeNode(attr); } } static void tagBasePath(QDomDocument &doc, const QString &tag, const QString &path) { QDomNodeList mergeFileList = doc.elementsByTagName(tag); for (int i = 0; i < mergeFileList.count(); i++) { QDomAttr attr = doc.createAttribute(QStringLiteral("__BasePath")); attr.setValue(path); mergeFileList.item(i).toElement().setAttributeNode(attr); } } QDomDocument VFolderMenu::loadDoc() { QDomDocument doc; if (m_docInfo.path.isEmpty()) { return doc; } QFile file(m_docInfo.path); if (!file.open(QIODevice::ReadOnly)) { qCWarning(SYCOCA) << "Could not open " << m_docInfo.path; return doc; } QString errorMsg; int errorRow; int errorCol; if (!doc.setContent(&file, &errorMsg, &errorRow, &errorCol)) { qCWarning(SYCOCA) << "Parse error in " << m_docInfo.path << ", line " << errorRow << ", col " << errorCol << ": " << errorMsg; file.close(); return doc; } file.close(); tagBaseDir(doc, QStringLiteral("MergeFile"), m_docInfo.baseDir); tagBasePath(doc, QStringLiteral("MergeFile"), m_docInfo.path); tagBaseDir(doc, QStringLiteral("MergeDir"), m_docInfo.baseDir); tagBaseDir(doc, QStringLiteral("DirectoryDir"), m_docInfo.baseDir); tagBaseDir(doc, QStringLiteral("AppDir"), m_docInfo.baseDir); tagBaseDir(doc, QStringLiteral("LegacyDir"), m_docInfo.baseDir); return doc; } void VFolderMenu::mergeFile(QDomElement &parent, const QDomNode &mergeHere) { //qDebug() << m_docInfo.path; QDomDocument doc = loadDoc(); QDomElement docElem = doc.documentElement(); QDomNode n = docElem.firstChild(); QDomNode last = mergeHere; while (!n.isNull()) { QDomElement e = n.toElement(); // try to convert the node to an element. QDomNode next = n.nextSibling(); if (e.isNull()) { // Skip } // The spec says we must ignore any Name nodes else if (e.tagName() != "Name") { parent.insertAfter(n, last); last = n; } docElem.removeChild(n); n = next; } } void VFolderMenu::mergeMenus(QDomElement &docElem, QString &name) { QMap menuNodes; QMap directoryNodes; QMap appDirNodes; QMap directoryDirNodes; QMap legacyDirNodes; QDomElement defaultLayoutNode; QDomElement layoutNode; QDomNode n = docElem.firstChild(); while (!n.isNull()) { QDomElement e = n.toElement(); // try to convert the node to an element. if (e.isNull()) { // qDebug() << "Empty node"; } else if (e.tagName() == QLatin1String("DefaultAppDirs")) { // Replace with m_defaultAppDirs replaceNode(docElem, n, m_defaultAppDirs, QStringLiteral("AppDir")); continue; } else if (e.tagName() == QLatin1String("DefaultDirectoryDirs")) { // Replace with m_defaultDirectoryDirs replaceNode(docElem, n, m_defaultDirectoryDirs, QStringLiteral("DirectoryDir")); continue; } else if (e.tagName() == QLatin1String("DefaultMergeDirs")) { // Replace with m_defaultMergeDirs replaceNode(docElem, n, m_defaultMergeDirs, QStringLiteral("MergeDir")); continue; } else if (e.tagName() == QLatin1String("AppDir")) { // Filter out dupes foldNode(docElem, e, appDirNodes); } else if (e.tagName() == QLatin1String("DirectoryDir")) { // Filter out dupes foldNode(docElem, e, directoryDirNodes); } else if (e.tagName() == QLatin1String("LegacyDir")) { // Filter out dupes foldNode(docElem, e, legacyDirNodes); } else if (e.tagName() == QLatin1String("Directory")) { // Filter out dupes foldNode(docElem, e, directoryNodes); } else if (e.tagName() == QLatin1String("Move")) { // Filter out dupes QString orig; QDomNode n2 = e.firstChild(); while (!n2.isNull()) { QDomElement e2 = n2.toElement(); // try to convert the node to an element. if (e2.tagName() == QLatin1String("Old")) { orig = e2.text(); break; } n2 = n2.nextSibling(); } foldNode(docElem, e, appDirNodes, orig); } else if (e.tagName() == QLatin1String("Menu")) { QString name; mergeMenus(e, name); QMap::iterator it = menuNodes.find(name); if (it != menuNodes.end()) { QDomElement docElem2 = *it; QDomNode n2 = docElem2.firstChild(); QDomNode first = e.firstChild(); while (!n2.isNull()) { QDomElement e2 = n2.toElement(); // try to convert the node to an element. QDomNode n3 = n2.nextSibling(); e.insertBefore(n2, first); docElem2.removeChild(n2); n2 = n3; } // We still have duplicated Name entries // but we don't care about that docElem.removeChild(docElem2); menuNodes.erase(it); } menuNodes.insert(name, e); } else if (e.tagName() == QLatin1String("MergeFile")) { if ((e.attribute(QStringLiteral("type")) == QLatin1String("parent"))) { // Ignore e.text(), as per the standard. We'll just look up the parent (more global) xml file. pushDocInfoParent(e.attribute(QStringLiteral("__BasePath")), e.attribute(QStringLiteral("__BaseDir"))); } else { pushDocInfo(e.text(), e.attribute(QStringLiteral("__BaseDir"))); } if (!m_docInfo.path.isEmpty()) { mergeFile(docElem, n); } popDocInfo(); QDomNode last = n; n = n.nextSibling(); docElem.removeChild(last); // Remove the MergeFile node continue; } else if (e.tagName() == QLatin1String("MergeDir")) { QString dir = absoluteDir(e.text(), e.attribute(QStringLiteral("__BaseDir")), true); Q_ASSERT(dir.endsWith('/')); const bool relative = QDir::isRelativePath(dir); const QStringList dirs = QStandardPaths::locateAll(QStandardPaths::GenericConfigLocation, QStringLiteral("menus/") + dir, QStandardPaths::LocateDirectory); Q_FOREACH (const QString &menuDir, dirs) { registerDirectory(menuDir); } QStringList fileList; Q_FOREACH (const QString &menuDir, dirs) { const QStringList fileNames = QDir(menuDir).entryList(QStringList() << QStringLiteral("*.menu")); Q_FOREACH (const QString &file, fileNames) { const QString fileToAdd = relative ? dir + file : menuDir + file; if (!fileList.contains(fileToAdd)) { fileList.append(fileToAdd); } } } Q_FOREACH (const QString &file, fileList) { pushDocInfo(file); mergeFile(docElem, n); popDocInfo(); } QDomNode last = n; n = n.nextSibling(); docElem.removeChild(last); // Remove the MergeDir node continue; } else if (e.tagName() == QLatin1String("Name")) { name = e.text(); } else if (e.tagName() == QLatin1String("DefaultLayout")) { if (!defaultLayoutNode.isNull()) { docElem.removeChild(defaultLayoutNode); } defaultLayoutNode = e; } else if (e.tagName() == QLatin1String("Layout")) { if (!layoutNode.isNull()) { docElem.removeChild(layoutNode); } layoutNode = e; } n = n.nextSibling(); } } static QString makeRelative(const QString &dir) { const QString canonical = QDir(dir).canonicalPath(); Q_FOREACH (const QString &base, QStandardPaths::locateAll(QStandardPaths::GenericConfigLocation, QLatin1String("menus"), QStandardPaths::LocateDirectory)) { if (canonical.startsWith(base)) { return canonical.mid(base.length() + 1); } } return dir; } void VFolderMenu::pushDocInfo(const QString &fileName, const QString &baseDir) { m_docInfoStack.push(m_docInfo); if (!baseDir.isEmpty()) { if (!QDir::isRelativePath(baseDir)) { m_docInfo.baseDir = makeRelative(baseDir); } else { m_docInfo.baseDir = baseDir; } } QString baseName = fileName; if (!QDir::isRelativePath(baseName)) { registerFile(baseName); } else { baseName = m_docInfo.baseDir + baseName; } m_docInfo.path = locateMenuFile(fileName); if (m_docInfo.path.isEmpty()) { m_docInfo.baseDir.clear(); m_docInfo.baseName.clear(); qDebug() << "Menu" << fileName << "not found."; return; } int i; i = baseName.lastIndexOf('/'); if (i > 0) { m_docInfo.baseDir = baseName.left(i + 1); m_docInfo.baseName = baseName.mid(i + 1, baseName.length() - i - 6); } else { m_docInfo.baseDir.clear(); m_docInfo.baseName = baseName.left(baseName.length() - 5); } } void VFolderMenu::pushDocInfoParent(const QString &basePath, const QString &baseDir) { m_docInfoStack.push(m_docInfo); m_docInfo.baseDir = baseDir; QString fileName = basePath.mid(basePath.lastIndexOf('/') + 1); m_docInfo.baseName = fileName.left(fileName.length() - 5); // without ".menu" QString baseName = QDir::cleanPath(m_docInfo.baseDir + fileName); QStringList result = QStandardPaths::locateAll(QStandardPaths::GenericConfigLocation, QStringLiteral("menus/") + baseName); // Remove anything "more local" than basePath. while (!result.isEmpty() && (result.at(0) != basePath)) { result.removeFirst(); } if (result.count() <= 1) { m_docInfo.path.clear(); // No parent found return; } // Now result.at(0) == basePath, take the next one, i.e. the one in the parent dir. m_docInfo.path = result.at(1); } void VFolderMenu::popDocInfo() { m_docInfo = m_docInfoStack.pop(); } QString VFolderMenu::locateMenuFile(const QString &fileName) { if (!QDir::isRelativePath(fileName)) { if (QFile::exists(fileName)) { return fileName; } return QString(); } QString result; QString xdgMenuPrefix = QString::fromLocal8Bit(qgetenv("XDG_MENU_PREFIX")); if (!xdgMenuPrefix.isEmpty()) { QFileInfo fileInfo(fileName); QString fileNameOnly = fileInfo.fileName(); if (!fileNameOnly.startsWith(xdgMenuPrefix)) { fileNameOnly = xdgMenuPrefix + fileNameOnly; } QString baseName = QDir::cleanPath(m_docInfo.baseDir + fileInfo.path() + '/' + fileNameOnly); result = QStandardPaths::locate(QStandardPaths::GenericConfigLocation, QStringLiteral("menus/") + baseName); } if (result.isEmpty()) { QString baseName = QDir::cleanPath(m_docInfo.baseDir + fileName); result = QStandardPaths::locate(QStandardPaths::GenericConfigLocation, QStringLiteral("menus/") + baseName); } return result; } QString VFolderMenu::locateDirectoryFile(const QString &fileName) { if (fileName.isEmpty()) { return QString(); } if (!QDir::isRelativePath(fileName)) { if (QFile::exists(fileName)) { return fileName; } return QString(); } // First location in the list wins for (QStringList::ConstIterator it = m_directoryDirs.constBegin(); it != m_directoryDirs.constEnd(); ++it) { QString tmp = (*it) + fileName; if (QFile::exists(tmp)) { return tmp; } } return QString(); } void VFolderMenu::initDirs() { m_defaultAppDirs = QStandardPaths::standardLocations(QStandardPaths::ApplicationsLocation); m_defaultDirectoryDirs = QStandardPaths::locateAll(QStandardPaths::GenericDataLocation, QStringLiteral("desktop-directories"), QStandardPaths::LocateDirectory); } void VFolderMenu::loadMenu(const QString &fileName) { m_defaultMergeDirs.clear(); if (!fileName.endsWith(QLatin1String(".menu"))) { return; } pushDocInfo(fileName); m_defaultMergeDirs << QStringLiteral("applications-merged/"); m_doc = loadDoc(); popDocInfo(); if (m_doc.isNull()) { if (m_docInfo.path.isEmpty()) { qCritical() << fileName << " not found in " << m_allDirectories << endl; } else { qCWarning(SYCOCA) << "Load error (" << m_docInfo.path << ")"; } return; } QDomElement e = m_doc.documentElement(); QString name; mergeMenus(e, name); } void VFolderMenu::processCondition(QDomElement &domElem, QHash &items) { if (domElem.tagName() == QLatin1String("And")) { QDomNode n = domElem.firstChild(); // Look for the first child element while (!n.isNull()) { // loop in case of comments QDomElement e = n.toElement(); n = n.nextSibling(); if (!e.isNull()) { processCondition(e, items); break; // we only want the first one } } QHash andItems; while (!n.isNull()) { QDomElement e = n.toElement(); if (e.tagName() == QLatin1String("Not")) { // Special handling for "and not" QDomNode n2 = e.firstChild(); while (!n2.isNull()) { QDomElement e2 = n2.toElement(); andItems.clear(); processCondition(e2, andItems); excludeItems(items, andItems); n2 = n2.nextSibling(); } } else { andItems.clear(); processCondition(e, andItems); matchItems(items, andItems); } n = n.nextSibling(); } } else if (domElem.tagName() == QLatin1String("Or")) { QDomNode n = domElem.firstChild(); // Look for the first child element while (!n.isNull()) { // loop in case of comments QDomElement e = n.toElement(); n = n.nextSibling(); if (!e.isNull()) { processCondition(e, items); break; // we only want the first one } } QHash orItems; while (!n.isNull()) { QDomElement e = n.toElement(); if (!e.isNull()) { orItems.clear(); processCondition(e, orItems); includeItems(items, orItems); } n = n.nextSibling(); } } else if (domElem.tagName() == QLatin1String("Not")) { FOR_ALL_APPLICATIONS(it) { KService::Ptr s = it.value(); items.insert(s->menuId(), s); } FOR_ALL_APPLICATIONS_END QHash notItems; QDomNode n = domElem.firstChild(); while (!n.isNull()) { QDomElement e = n.toElement(); if (!e.isNull()) { notItems.clear(); processCondition(e, notItems); excludeItems(items, notItems); } n = n.nextSibling(); } } else if (domElem.tagName() == QLatin1String("Category")) { FOR_CATEGORY(domElem.text(), it) { KService::Ptr s = *it; items.insert(s->menuId(), s); } FOR_CATEGORY_END } else if (domElem.tagName() == QLatin1String("All")) { FOR_ALL_APPLICATIONS(it) { KService::Ptr s = it.value(); items.insert(s->menuId(), s); } FOR_ALL_APPLICATIONS_END } else if (domElem.tagName() == QLatin1String("Filename")) { const QString filename = domElem.text(); //qDebug() << "Adding file" << filename; KService::Ptr s = findApplication(filename); if (s) { items.insert(filename, s); } } } void VFolderMenu::loadApplications(const QString &dir, const QString &prefix) { //qDebug() << "Looking up applications under" << dir; QDirIterator it(dir); while (it.hasNext()) { it.next(); const QFileInfo fi = it.fileInfo(); const QString fn = fi.fileName(); if (fi.isDir() && !fi.isSymLink() && !fi.isBundle()) { // same check as in ksycocautils_p.h if (fn == QLatin1String(".") || fn == QLatin1String("..")) { continue; } loadApplications(fi.filePath(), prefix + fn + '-'); continue; } if (fi.isFile()) { if (!fn.endsWith(QLatin1String(".desktop"))) { continue; } KService::Ptr service = m_kbuildsycocaInterface->createService(fi.absoluteFilePath()); if (service) { addApplication(prefix + fn, service); } } } } void VFolderMenu::processLegacyDir(const QString &dir, const QString &relDir, const QString &prefix) { //qDebug().nospace() << "processLegacyDir(" << dir << ", " << relDir << ", " << prefix << ")"; QHash items; QDirIterator it(dir); while (it.hasNext()) { it.next(); const QFileInfo fi = it.fileInfo(); const QString fn = fi.fileName(); if (fi.isDir()) { if (fn == QLatin1String(".") || fn == QLatin1String("..")) { continue; } SubMenu *parentMenu = m_currentMenu; m_currentMenu = new SubMenu; m_currentMenu->name = fn; m_currentMenu->directoryFile = fi.absoluteFilePath() + QStringLiteral("/.directory"); parentMenu->subMenus.append(m_currentMenu); processLegacyDir(fi.filePath(), relDir + fn + '/', prefix); m_currentMenu = parentMenu; continue; } if (fi.isFile() /*&& !fi.isSymLink() ?? */) { if (!fn.endsWith(QLatin1String(".desktop"))) { continue; } KService::Ptr service = m_kbuildsycocaInterface->createService(fi.absoluteFilePath()); if (service) { const QString id = prefix + fn; // TODO: Add legacy category addApplication(id, service); items.insert(service->menuId(), service); if (service->categories().isEmpty()) { m_currentMenu->items.insert(id, service); } } } } markUsedApplications(items); } void VFolderMenu::processMenu(QDomElement &docElem, int pass) { SubMenu *parentMenu = m_currentMenu; int oldDirectoryDirsCount = m_directoryDirs.count(); QString name; QString directoryFile; bool onlyUnallocated = false; bool isDeleted = false; QDomElement defaultLayoutNode; QDomElement layoutNode; QDomElement query; QDomNode n = docElem.firstChild(); while (!n.isNull()) { QDomElement e = n.toElement(); // try to convert the node to an element. if (e.tagName() == QLatin1String("Name")) { name = e.text(); } else if (e.tagName() == QLatin1String("Directory")) { directoryFile = e.text(); } else if (e.tagName() == QLatin1String("DirectoryDir")) { QString dir = absoluteDir(e.text(), e.attribute(QStringLiteral("__BaseDir"))); m_directoryDirs.prepend(dir); } else if (e.tagName() == QLatin1String("OnlyUnallocated")) { onlyUnallocated = true; } else if (e.tagName() == QLatin1String("NotOnlyUnallocated")) { onlyUnallocated = false; } else if (e.tagName() == QLatin1String("Deleted")) { isDeleted = true; } else if (e.tagName() == QLatin1String("NotDeleted")) { isDeleted = false; } else if (e.tagName() == QLatin1String("DefaultLayout")) { defaultLayoutNode = e; } else if (e.tagName() == QLatin1String("Layout")) { layoutNode = e; } n = n.nextSibling(); } // Setup current menu entry if (pass == 0) { - m_currentMenu = 0; + m_currentMenu = nullptr; // Look up menu if (parentMenu) { foreach (SubMenu *menu, parentMenu->subMenus) { if (menu->name == name) { m_currentMenu = menu; break; } } } if (!m_currentMenu) { // Not found? // Create menu m_currentMenu = new SubMenu; m_currentMenu->name = name; if (parentMenu) { parentMenu->subMenus.append(m_currentMenu); } else { m_rootMenu = m_currentMenu; } } if (directoryFile.isEmpty()) { //qDebug() << "Menu" << name << "does not specify a directory file."; } // Override previous directoryFile iff available QString tmp = locateDirectoryFile(directoryFile); if (! tmp.isEmpty()) { m_currentMenu->directoryFile = tmp; } m_currentMenu->isDeleted = isDeleted; m_currentMenu->defaultLayoutNode = defaultLayoutNode; m_currentMenu->layoutNode = layoutNode; } else { // Look up menu if (parentMenu) { foreach (SubMenu *menu, parentMenu->subMenus) { if (menu->name == name) { m_currentMenu = menu; break; } } } else { m_currentMenu = m_rootMenu; } } // Process AppDir and LegacyDir if (pass == 0) { QDomElement query; QDomNode n = docElem.firstChild(); while (!n.isNull()) { QDomElement e = n.toElement(); // try to convert the node to an element. if (e.tagName() == QLatin1String("AppDir")) { createAppsInfo(); QString dir = absoluteDir(e.text(), e.attribute(QStringLiteral("__BaseDir"))); registerDirectory(dir); loadApplications(dir, QString()); } else if (e.tagName() == QLatin1String("LegacyDir")) { createAppsInfo(); QString dir = absoluteDir(e.text(), e.attribute(QStringLiteral("__BaseDir"))); QString prefix = e.attributes().namedItem(QStringLiteral("prefix")).toAttr().value(); SubMenu *oldMenu = m_currentMenu; m_currentMenu = new SubMenu; registerDirectory(dir); processLegacyDir(dir, QString(), prefix); m_legacyNodes.insert(dir, m_currentMenu); m_currentMenu = oldMenu; } n = n.nextSibling(); } } loadAppsInfo(); // Update the scope wrt the list of applications if (((pass == 1) && !onlyUnallocated) || ((pass == 2) && onlyUnallocated)) { n = docElem.firstChild(); while (!n.isNull()) { QDomElement e = n.toElement(); // try to convert the node to an element. if (e.tagName() == QLatin1String("Include")) { QHash items; QDomNode n2 = e.firstChild(); while (!n2.isNull()) { QDomElement e2 = n2.toElement(); items.clear(); processCondition(e2, items); if (m_track) { track(m_trackId, m_currentMenu->name, m_currentMenu->items, m_currentMenu->excludeItems, items, QStringLiteral("Before ")); } includeItems(m_currentMenu->items, items); excludeItems(m_currentMenu->excludeItems, items); markUsedApplications(items); if (m_track) { track(m_trackId, m_currentMenu->name, m_currentMenu->items, m_currentMenu->excludeItems, items, QStringLiteral("After ")); } n2 = n2.nextSibling(); } } else if (e.tagName() == QLatin1String("Exclude")) { QHash items; QDomNode n2 = e.firstChild(); while (!n2.isNull()) { QDomElement e2 = n2.toElement(); items.clear(); processCondition(e2, items); if (m_track) { track(m_trackId, m_currentMenu->name, m_currentMenu->items, m_currentMenu->excludeItems, items, QStringLiteral("Before ")); } excludeItems(m_currentMenu->items, items); includeItems(m_currentMenu->excludeItems, items); if (m_track) { track(m_trackId, m_currentMenu->name, m_currentMenu->items, m_currentMenu->excludeItems, items, QStringLiteral("After ")); } n2 = n2.nextSibling(); } } n = n.nextSibling(); } } n = docElem.firstChild(); while (!n.isNull()) { QDomElement e = n.toElement(); // try to convert the node to an element. if (e.tagName() == QLatin1String("Menu")) { processMenu(e, pass); } // We insert legacy dir in pass 0, this way the order in the .menu-file determines // which .directory file gets used, but the menu-entries of legacy-menus will always // have the lowest priority. // else if (((pass == 1) && !onlyUnallocated) || ((pass == 2) && onlyUnallocated)) else if (pass == 0) { if (e.tagName() == QLatin1String("LegacyDir")) { // Add legacy nodes to Menu structure QString dir = absoluteDir(e.text(), e.attribute(QStringLiteral("__BaseDir"))); SubMenu *legacyMenu = m_legacyNodes[dir]; if (legacyMenu) { mergeMenu(m_currentMenu, legacyMenu); } } } n = n.nextSibling(); } if (pass == 2) { n = docElem.firstChild(); while (!n.isNull()) { QDomElement e = n.toElement(); // try to convert the node to an element. if (e.tagName() == QLatin1String("Move")) { QString orig; QString dest; QDomNode n2 = e.firstChild(); while (!n2.isNull()) { QDomElement e2 = n2.toElement(); // try to convert the node to an element. if (e2.tagName() == QLatin1String("Old")) { orig = e2.text(); } if (e2.tagName() == QLatin1String("New")) { dest = e2.text(); } n2 = n2.nextSibling(); } //qDebug() << "Moving" << orig << "to" << dest; if (!orig.isEmpty() && !dest.isEmpty()) { SubMenu *menu = takeSubMenu(m_currentMenu, orig); if (menu) { insertSubMenu(m_currentMenu, dest, menu, true); // Destination has priority } } } n = n.nextSibling(); } } unloadAppsInfo(); // Update the scope wrt the list of applications while (m_directoryDirs.count() > oldDirectoryDirsCount) { m_directoryDirs.pop_front(); } m_currentMenu = parentMenu; } static QString parseAttribute(const QDomElement &e) { QString option; const QString SHOW_EMPTY=QStringLiteral("show_empty"); if (e.hasAttribute(SHOW_EMPTY)) { QString str = e.attribute(SHOW_EMPTY); if (str == QLatin1String("true")) { option = "ME "; } else if (str == QLatin1String("false")) { option = "NME "; } else { //qDebug()<<" Error in parsing show_empty attribute :"<defaultLayoutNode.isNull()) { defaultLayout = parseLayoutNode(menu->defaultLayoutNode); } if (menu->layoutNode.isNull()) { menu->layoutList = defaultLayout; } else { menu->layoutList = parseLayoutNode(menu->layoutNode); if (menu->layoutList.isEmpty()) { menu->layoutList = defaultLayout; } } foreach (VFolderMenu::SubMenu *subMenu, menu->subMenus) { layoutMenu(subMenu, defaultLayout); } } void VFolderMenu::markUsedApplications(const QHash &items) { foreach (const KService::Ptr &p, items) { m_usedAppsDict.insert(p->menuId()); } } VFolderMenu::SubMenu *VFolderMenu::parseMenu(const QString &file) { - m_appsInfo = 0; + m_appsInfo = nullptr; const QStringList dirs = QStandardPaths::locateAll(QStandardPaths::GenericConfigLocation, QStringLiteral("menus"), QStandardPaths::LocateDirectory); for (QStringList::ConstIterator it = dirs.begin(); it != dirs.end(); ++it) { registerDirectory(*it); } loadMenu(file); delete m_rootMenu; - m_rootMenu = m_currentMenu = 0; + m_rootMenu = m_currentMenu = nullptr; QDomElement docElem = m_doc.documentElement(); for (int pass = 0; pass <= 2; pass++) { // pass 0: load application desktop files // pass 1: the normal processing // pass 2: process to put unused files into "Lost & Found". processMenu(docElem, pass); switch (pass) { case 0: // Fill the dictCategories for each AppsInfo in m_appsInfoList, // in preparation for processMenu pass 1. buildApplicationIndex(false); break; case 1: // Fill the dictCategories for each AppsInfo in m_appsInfoList, // with only the unused apps, in preparation for processMenu pass 2. buildApplicationIndex(true /* unusedOnly */); break; case 2: { QStringList defaultLayout; defaultLayout << QStringLiteral(":M"); // Sub-Menus defaultLayout << QStringLiteral(":F"); // Individual entries layoutMenu(m_rootMenu, defaultLayout); break; } default: break; } } return m_rootMenu; } void VFolderMenu::setTrackId(const QString &id) { m_track = !id.isEmpty(); m_trackId = id; } diff --git a/src/sycoca/vfolder_menu_p.h b/src/sycoca/vfolder_menu_p.h index f42e06f..8a83ca5 100644 --- a/src/sycoca/vfolder_menu_p.h +++ b/src/sycoca/vfolder_menu_p.h @@ -1,279 +1,279 @@ /* This file is part of the KDE libraries Copyright (c) 2003 Waldo Bastian This library is free software; you can redistribute it and/or modify it under the terms of the GNU Library General Public License version 2 as published by the Free Software Foundation. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Library General Public License for more details. You should have received a copy of the GNU Library General Public License along with this library; see the file COPYING.LIB. If not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #ifndef VFOLDER_MENU_H #define VFOLDER_MENU_H #include #include #include #include #include #include #include class KBuildSycocaInterface; class KServiceFactory; class VFolderMenu : public QObject { Q_OBJECT public: class AppsInfo; class SubMenu { public: - SubMenu() : isDeleted(false), apps_info(0) + SubMenu() : isDeleted(false), apps_info(nullptr) { items.reserve(43); } ~SubMenu() { qDeleteAll(subMenus); } public: QString name; QString directoryFile; QList subMenus; QHash items; QHash excludeItems; // Needed when merging due to Move. QDomElement defaultLayoutNode; QDomElement layoutNode; bool isDeleted; QStringList layoutList; AppsInfo *apps_info; }; VFolderMenu(KServiceFactory *serviceFactory, KBuildSycocaInterface *kbuildsycocaInterface); ~VFolderMenu(); /** * Parses VFolder menu definition and generates a menu layout. * The newService signals is used as callback to load * a specific service description. * * @param file Menu file to load */ SubMenu *parseMenu(const QString &file); /** * Returns a list of all directories involved in the last call to * parseMenu(). * * A change in any of these directories or in any of their child- * directories can result in changes to the menu. */ QStringList allDirectories(); /** * Debug function to enable tracking of what happens with a specific * menu item id */ void setTrackId(const QString &id); public: struct MenuItem { enum Type { MI_Service, MI_SubMenu, MI_Separator }; Type type; KService::Ptr service; SubMenu *submenu; }; public: QStringList m_allDirectories; // A list of all the directories that we touch QStringList m_defaultAppDirs; QStringList m_defaultDirectoryDirs; QStringList m_defaultMergeDirs; QStringList m_directoryDirs; // Current set of applicable dirs QHash m_legacyNodes; // Dictionary that stores Menu nodes // associated with legacy tree. class DocInfo { public: QString baseDir; // Relative base dir of current menu file QString baseName; // Filename of current menu file without ".menu" QString path; // Full path of current menu file including ".menu" }; DocInfo m_docInfo; // DocInfo for current doc QStack m_docInfoStack; class AppsInfo { public: AppsInfo() { dictCategories.reserve(53); applications.reserve(997); appRelPaths.reserve(997); } ~AppsInfo() { } QHash dictCategories; // category -> apps QHash applications; // rel path -> service QHash appRelPaths; // service -> rel path }; AppsInfo *m_appsInfo; // AppsInfo for current menu QList m_appsInfoStack; // All applicable AppsInfo for current menu QList m_appsInfoList; // List of all AppsInfo objects. QSet m_usedAppsDict; // all applications that have been allocated QDomDocument m_doc; SubMenu *m_rootMenu; SubMenu *m_currentMenu; bool m_track; QString m_trackId; private: /** * Lookup application by relative path */ KService::Ptr findApplication(const QString &relPath); /** * Lookup applications by category */ QList findCategory(const QString &category); /** * Add new application */ void addApplication(const QString &id, KService::Ptr service); /** * Build application indices */ void buildApplicationIndex(bool unusedOnly); /** * Create a AppsInfo frame for current menu */ void createAppsInfo(); /** * Load additional AppsInfo frame for current menu */ void loadAppsInfo(); /** * Unload additional AppsInfo frame for current menu */ void unloadAppsInfo(); QDomDocument loadDoc(); void mergeMenus(QDomElement &docElem, QString &name); void mergeFile(QDomElement &docElem, const QDomNode &mergeHere); void loadMenu(const QString &filename); /** * Merge the items2 set into the items1 set */ void includeItems(QHash &items1, const QHash &items2); /** * Remove all items from the items1 set that aren't also in the items2 set */ void matchItems(QHash &items1, const QHash &items2); /** * Remove all items in the items2 set from the items1 set */ void excludeItems(QHash &items1, const QHash &items2); /** * Search the parentMenu tree for the menu menuName and takes it * out. * * This function returns a pointer to the menu if it was found * or 0 if it was not found. */ SubMenu *takeSubMenu(SubMenu *parentMenu, const QString &menuName); /** * Insert the menu newMenu with name menuName into the parentMenu. * If such menu already exist the result is merged, if any additional * submenus are required they are created. * If reversePriority is false, newMenu has priority over the existing * menu during merging. * If reversePriority is true, the existing menu has priority over newMenu * during merging. */ void insertSubMenu(VFolderMenu::SubMenu *parentMenu, const QString &menuName, VFolderMenu::SubMenu *newMenu, bool reversePriority = false); /** * Merge menu2 and its submenus into menu1 and its submenus * If reversePriority is false, menu2 has priority over menu1 * If reversePriority is true, menu1 has priority over menu2 */ void mergeMenu(SubMenu *menu1, SubMenu *menu2, bool reversePriority = false); /** * Inserts service into the menu using name relative to parentMenu * Any missing sub-menus are created. */ void insertService(SubMenu *parentMenu, const QString &name, KService::Ptr newService); /** * Register the directory that @p file is in. * @see allDirectories() */ void registerFile(const QString &file); /** * Fill m_usedAppsDict with all applications from @p items */ void markUsedApplications(const QHash &items); /** * Register @p directory * @see allDirectories() */ void registerDirectory(const QString &directory); void processLegacyDir(const QString &dir, const QString &relDir, const QString &prefix); void processMenu(QDomElement &docElem, int pass); void layoutMenu(VFolderMenu::SubMenu *menu, QStringList defaultLayout); void processCondition(QDomElement &docElem, QHash &items); void initDirs(); void pushDocInfo(const QString &fileName, const QString &baseDir = QString()); void pushDocInfoParent(const QString &basePath, const QString &baseDir); void popDocInfo(); QString absoluteDir(const QString &_dir, const QString &baseDir, bool keepRelativeToCfg = false); QString locateMenuFile(const QString &fileName); QString locateDirectoryFile(const QString &fileName); void loadApplications(const QString &, const QString &); private: KServiceFactory *m_serviceFactory; KBuildSycocaInterface *m_kbuildsycocaInterface; }; #endif diff --git a/tests/pluginlocator/plugintest.cpp b/tests/pluginlocator/plugintest.cpp index 72bacde..8229dd7 100644 --- a/tests/pluginlocator/plugintest.cpp +++ b/tests/pluginlocator/plugintest.cpp @@ -1,201 +1,201 @@ /****************************************************************************** * Copyright 2013 Sebastian Kügler * * * * This library is free software; you can redistribute it and/or * * modify it under the terms of the GNU Library General Public * * License as published by the Free Software Foundation; either * * version 2 of the License, or (at your option) any later version. * * * * This library is distributed in the hope that it will be useful, * * but WITHOUT ANY WARRANTY; without even the implied warranty of * * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * * Library General Public License for more details. * * * * You should have received a copy of the GNU Library General Public License * * along with this library; see the file COPYING.LIB. If not, write to * * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, * * Boston, MA 02110-1301, USA. * *******************************************************************************/ #include "plugintest.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include static QTextStream cout(stdout); class PluginTestPrivate { public: QString pluginName; }; PluginTest::PluginTest() : - QObject(0) + QObject(nullptr) { d = new PluginTestPrivate; } PluginTest::~PluginTest() { delete d; } int PluginTest::runMain() { // measure performance QElapsedTimer timer; int runs = 1; QList timings; cout << "-- PluginLocator Test --" << endl; bool ok = true; // KSycoca querying timer.start(); for (int _i = 0; _i < runs; _i++) { timer.restart(); if (!loadFromKService(QStringLiteral("time"))) { ok = false; } timings << timer.nsecsElapsed(); } report(timings, QStringLiteral("KServiceTypeTrader")); timings.clear(); // -- Metadata querying for (int _i = 0; _i < runs; _i++) { timer.restart(); if (!loadFromMetaData()) { ok = false; } //if (!loadFromMetaData2("Plasma/ContainmentActions")) ok = false; timings << timer.nsecsElapsed(); } report(timings, QStringLiteral("Metadata")); timings.clear(); findPlugins(); if (ok) { qDebug() << "All tests finished successfully"; return 0; } return 0; // We return successfully in any case, since plugins aren't installed for most people } void PluginTest::report(const QList timings, const QString &msg) { qulonglong totalTime = 0; int unitDiv = 1000; QString unit = QStringLiteral("microsec"); int i = 0; foreach (qint64 t, timings) { int msec = t / 1000000; qDebug() << " Run " << i << ": " << msec << " msec"; totalTime += t; i++; } QString av = QString::number((totalTime / timings.count() / unitDiv), 'f', 1); qDebug() << " Average: " << av << " " << unit << " (" << msg << ")" << endl; } bool PluginTest::loadFromKService(const QString &name) { bool ok = false; QString constraint = QStringLiteral("[X-KDE-PluginInfo-Name] == '%1'").arg(name); KService::List offers = KServiceTypeTrader::self()->query(QStringLiteral("Plasma/DataEngine"), constraint); QString error; if (offers.isEmpty()) { qDebug() << "offers are empty for " << name << " with constraint " << constraint; } else { QVariantList allArgs; allArgs << offers.first()->storageId(); const QString _n = offers.first()->property(QStringLiteral("Name")).toString(); if (!_n.isEmpty()) { qDebug() << "Found Dataengine: " << _n << endl; ok = true; } else { qDebug() << "Nothing found. " << endl; } } return ok; } bool PluginTest::loadFromMetaData(const QString &serviceType) { bool ok = false; QString pluginName("time"); QString constraint = QStringLiteral("[X-KDE-PluginInfo-Name] == '%1'").arg(pluginName); KPluginInfo::List res = KPluginTrader::self()->query(QStringLiteral("kf5"), serviceType, QString()); qDebug() << "----------- Found " << res.count() << " Plugins" << constraint; ok = res.count() > 0; foreach (const KPluginInfo &info, res) { qDebug() << " file: " << info.libraryPath(); } return ok; } bool PluginTest::findPlugins() { QElapsedTimer timer; QList timings; const QString pluginDir("/media/storage/testdata/"); const QStringList sizes = QStringList() << QStringLiteral("50") << QStringLiteral("100") << QStringLiteral("150") << QStringLiteral("200") << QStringLiteral("250") << QStringLiteral("300") << QStringLiteral("400") << QStringLiteral("500") << QStringLiteral("600") << QStringLiteral("700") << QStringLiteral("800") << QStringLiteral("1000") << QStringLiteral("1500") << QStringLiteral("2000") << QStringLiteral("5000"); QStringList datadirs; foreach (const QString &_s, sizes) { datadirs << pluginDir + _s; } foreach (const QString &subdir, datadirs) { const QString pluginName; const QString constraint; const QString serviceType; timer.restart(); KPluginInfo::List res = KPluginTrader::self()->query(subdir, serviceType, constraint); timings << timer.nsecsElapsed(); qDebug() << "Found " << res.count() << " Plugins in " << subdir; } report(timings, QStringLiteral("reading monsterdirs")); return true; } #include "moc_plugintest.cpp"