SVN commit 1184941 by dfaure: Add support for defining "helper protocols" (e.g. "mailto URLs should launch the executable kmailservice") using .desktop files (with a custom mimetype, x-scheme-handler/mailto, as added to shared-mime-info recently) rather than using .protocol files (which are KDE-specific). Converted the two in KIO, but the code can still handle old-style .protocol files. Unit-tested. M +1 -0 kdecore/services/kmimetype.cpp M +1 -0 kdecore/services/kmimetypetrader.cpp M +17 -9 kdecore/sycoca/kprotocolinfo.cpp M +18 -0 kdecore/tests/kmimetypetest.cpp M +1 -0 kdecore/tests/kmimetypetest.h M +13 -10 kded/kbuildmimetypefactory.cpp M +2 -0 kded/kbuildmimetypefactory.h M +10 -3 kded/kbuildservicefactory.cpp M +3 -3 kio/misc/CMakeLists.txt A kio/misc/kmailservice.desktop kio/misc/kmailservice.protocol#1184154 D kio/misc/kmailservice.protocol A kio/misc/ktelnetservice.desktop kio/misc/telnet.protocol#1184154 D kio/misc/rlogin.protocol D kio/misc/ssh.protocol D kio/misc/telnet.protocol --- trunk/KDE/kdelibs/kdecore/services/kmimetype.cpp #1184940:1184941 @@ -66,6 +66,7 @@ // This could be done faster... KMimeType::List lst; Q_FOREACH(const QString& mimeType, KMimeTypeFactory::self()->allMimeTypes()) { + if (!mimeType.startsWith("x-scheme-handler")) lst.append(KMimeType::mimeType(mimeType)); } return lst; --- trunk/KDE/kdelibs/kdecore/services/kmimetypetrader.cpp #1184940:1184941 @@ -59,6 +59,7 @@ KMimeTypeFactory *factory = KMimeTypeFactory::self(); const int offset = factory->entryOffset(mime); if ( !offset ) { + if (!mimeType.startsWith("x-scheme-handler/")) // don't warn for unknown scheme handler mimetypes kWarning(7014) << "KMimeTypeTrader: mimeType" << mimeType << "not found"; return lst; // empty } --- trunk/KDE/kdelibs/kdecore/sycoca/kprotocolinfo.cpp #1184940:1184941 @@ -21,6 +21,7 @@ #include "kprotocolinfo_p.h" #include "kprotocolinfofactory.h" +#include #include #include #include @@ -301,15 +302,21 @@ return prot->m_determineMimetypeFromExtension; } -QString KProtocolInfo::exec( const QString& _protocol ) +QString KProtocolInfo::exec(const QString& protocol) { - KProtocolInfo::Ptr prot = KProtocolInfoFactory::self()->findProtocol(_protocol); - if ( !prot ) - return QString(); - + KProtocolInfo::Ptr prot = KProtocolInfoFactory::self()->findProtocol(protocol); + if ( prot ) { return prot->m_exec; } + // Maybe it's "helper protocol", i.e. launches an app? + const KService::Ptr service = KMimeTypeTrader::self()->preferredService("x-scheme-handler/" + protocol); + if (service) + return service->exec(); + + return QString(); +} + KProtocolInfo::ExtraFieldList KProtocolInfo::extraFields( const KUrl &url ) { KProtocolInfo::Ptr prot = KProtocolInfoFactory::self()->findProtocol(url.protocol()); @@ -419,10 +426,11 @@ { // We call the findProtocol directly (not via KProtocolManager) to bypass any proxy settings. KProtocolInfo::Ptr prot = KProtocolInfoFactory::self()->findProtocol(protocol); - if ( !prot ) - return false; + if ( prot ) + return prot->m_isHelperProtocol; - return prot->m_isHelperProtocol; + const KService::Ptr service = KMimeTypeTrader::self()->preferredService("x-scheme-handler/" + protocol); + return !service.isNull(); } bool KProtocolInfo::isKnownProtocol( const KUrl &url ) @@ -434,7 +442,7 @@ { // We call the findProtocol (const QString&) to bypass any proxy settings. KProtocolInfo::Ptr prot = KProtocolInfoFactory::self()->findProtocol(protocol); - return prot; + return prot || isHelperProtocol(protocol); } QDataStream& operator>>( QDataStream& s, KProtocolInfo::ExtraField& field ) { --- trunk/KDE/kdelibs/kdecore/tests/kmimetypetest.cpp #1184940:1184941 @@ -892,4 +892,22 @@ f.waitForFinished(); } +void KMimeTypeTest::testHelperProtocols() +{ + QVERIFY(KProtocolInfo::isKnownProtocol("mailto")); + QVERIFY(KProtocolInfo::isHelperProtocol("mailto")); + QVERIFY(KProtocolInfo::isHelperProtocol(KUrl("mailto:faure@kde.org"))); + QCOMPARE(KProtocolInfo::exec("mailto"), QString::fromLatin1("kmailservice %u")); + QVERIFY(!KProtocolInfo::isHelperProtocol("http")); + QVERIFY(!KProtocolInfo::isHelperProtocol("ftp")); + QVERIFY(!KProtocolInfo::isHelperProtocol("file")); + QVERIFY(!KProtocolInfo::isHelperProtocol("unknown")); + QVERIFY(KProtocolInfo::isHelperProtocol("telnet")); + + // To test that compat still works + if (KProtocolInfo::isKnownProtocol("tel")) { + QVERIFY(KProtocolInfo::isHelperProtocol("tel")); + } +} + #include "kmimetypetest.moc" --- trunk/KDE/kdelibs/kdecore/tests/kmimetypetest.h #1184940:1184941 @@ -58,6 +58,7 @@ void testExtractKnownExtension(); void testParseMagicFile_data(); void testParseMagicFile(); + void testHelperProtocols(); void testThreads(); private: --- trunk/KDE/kdelibs/kded/kbuildmimetypefactory.cpp #1184940:1184941 @@ -101,16 +101,6 @@ void KBuildMimeTypeFactory::saveHeader(QDataStream &str) { KSycocaFactory::saveHeader(str); -#if 0 - // This header is read by old KMimeTypeFactory's constructor - // KDE5: remove - str << (qint32) 0; // m_fastPatternOffset - str << (qint32) 0; // old "other pattern offset" - str << (qint32) 0; - str << (qint32) 0; // m_highWeightPatternOffset - str << (qint32) 0; // m_lowWeightPatternOffset - str << (qint32) 0; -#endif } void KBuildMimeTypeFactory::save(QDataStream &str) @@ -127,3 +117,16 @@ // Seek to end. str.device()->seek(endOfFactoryData); } + +void 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); +} --- trunk/KDE/kdelibs/kded/kbuildmimetypefactory.h #1184940:1184941 @@ -54,6 +54,8 @@ virtual MimeTypeEntry * createEntry( int ) const { assert(0); return 0L; } + void createFakeMimeType(const QString& name); + /** * Write out mime type specific index files. */ --- trunk/KDE/kdelibs/kded/kbuildservicefactory.cpp #1184940:1184941 @@ -253,18 +253,22 @@ m_offerHash.addServiceOffer(stName, KServiceOffer(service, preference, 0, service->allowAsDefault()) ); } else { KMimeType::Ptr mime = KMimeType::mimeType(stName, KMimeType::ResolveAliases); - if (mime) { - m_offerHash.addServiceOffer(stName, KServiceOffer(service, serviceTypeList[i].preference, 0, service->allowAsDefault()) ); + if (!mime) { + if (stName.startsWith("x-scheme-handler/")) { + // Create those on demand + m_mimeTypeFactory->createFakeMimeType(stName); } else { kDebug(7021) << service->entryPath() << "specifies undefined mimetype/servicetype" << stName; - continue; // technically we could call addServiceOffer here, 'mime' isn't used. But it // would be useless, since the loops for writing out the offers iterate // over all known servicetypes and mimetypes. Unknown -> never written out. + continue; } } + m_offerHash.addServiceOffer(stName, KServiceOffer(service, serviceTypeList[i].preference, 0, service->allowAsDefault()) ); } } + } // Read user preferences (added/removed associations) and add/remove serviceoffers to m_offerHash KMimeAssociations mimeAssociations(m_offerHash); @@ -278,6 +282,9 @@ int offersOffset = 0; const int offerEntrySize = sizeof( qint32 ) * 4; // four qint32s, see saveOfferList. + // TODO: idea: we could iterate over m_offerHash, and look up the servicetype or mimetype. + // Would that be faster than iterating over all servicetypes and mimetypes? + KSycocaEntryDict::const_iterator itstf = m_serviceTypeFactory->entryDict()->constBegin(); const KSycocaEntryDict::const_iterator endstf = m_serviceTypeFactory->entryDict()->constEnd(); for( ; itstf != endstf; ++itstf ) { --- trunk/KDE/kdelibs/kio/misc/CMakeLists.txt #1184940:1184941 @@ -19,8 +19,8 @@ target_link_libraries(kmailservice ${KDE4_KDEUI_LIBS} ) install(TARGETS kmailservice DESTINATION ${LIBEXEC_INSTALL_DIR} ) +install(FILES kmailservice.desktop DESTINATION ${XDG_APPS_INSTALL_DIR}) - ########### next target ############### set(ktelnetservice_SRCS ktelnetservice.cpp ) @@ -31,11 +31,11 @@ target_link_libraries(ktelnetservice ${KDE4_KDEUI_LIBS} ) install(TARGETS ktelnetservice DESTINATION ${LIBEXEC_INSTALL_DIR} ) +install(FILES ktelnetservice.desktop DESTINATION ${XDG_APPS_INSTALL_DIR}) - ########### install files ############### -install( FILES kmailservice.protocol telnet.protocol rlogin.protocol rtsp.protocol ssh.protocol mms.protocol mmst.protocol mmsu.protocol pnm.protocol rtspt.protocol rtspu.protocol DESTINATION ${SERVICES_INSTALL_DIR} ) +install( FILES rtsp.protocol mms.protocol mmst.protocol mmsu.protocol pnm.protocol rtspt.protocol rtspu.protocol DESTINATION ${SERVICES_INSTALL_DIR} ) # install the fileshareset and filesharelist scripts, they are not required on windows if (UNIX)