Commit 0709592d authored by David Faure's avatar David Faure
Browse files

Backport 779366: Fix mime-application association support by porting the code...

Backport 779366: Fix mime-application association support by porting the code to the new XDG standard mimeapps.list
(see kdelibs commit r779354)

svn path=/branches/KDE/4.0/kdebase/apps/; revision=780190
parent b49ef015
......@@ -187,7 +187,6 @@ void FileTypesView::readFileTypes()
typesLV->clear();
m_majorMap.clear();
m_itemList.clear();
MimeTypeData::reset();
TypesListItem *groupItem;
KMimeType::List mimetypes = KMimeType::allMimeTypes();
......
......@@ -355,29 +355,10 @@ void KServiceListWidget::removeService()
if ( selected >= 0 ) {
// Check if service is associated with this mimetype or with one of its parents
KServiceListItem *serviceItem = static_cast<KServiceListItem *>(servicesLB->item(selected));
KMimeType::Ptr mimetype = m_mimeTypeData->findImplicitAssociation(serviceItem->desktopPath);
if (serviceItem->isImmutable())
{
KMessageBox::sorry(this, i18n("You are not authorized to remove this service."));
}
else if (mimetype)
{
KMessageBox::sorry(this, "<qt>"+
i18n("The service <b>%1</b> can not be removed.", serviceItem->text())+
"<p>"+
i18n("The service is listed here because it has been associated "
"with the <b>%1</b> (%2) file type and files of type "
"<b>%3</b> (%4) are per definition also of type "
"<b>%5</b>.", mimetype->name(), mimetype->comment(),
m_mimeTypeData->name(), m_mimeTypeData->comment(), mimetype->name())+
"<p>"+
i18n("Either select the <b>%1</b> file type to remove the "
"service from there or move the service down "
"to deprecate it.", mimetype->name()));
// i18n("Do you want to remove the service from the <b>%1</b> "
// "file type or from the <b>%2</b> file type?", ???, ???);
}
else
{
delete servicesLB->takeItem( selected );
......
......@@ -27,9 +27,6 @@
#include <kmimetypetrader.h>
#include <kdesktopfile.h>
typedef QMap< QString, QStringList > ChangedServices;
K_GLOBAL_STATIC(ChangedServices, s_changedServices)
MimeTypeData::MimeTypeData(const QString& major)
: m_askSave(AskSaveDefault),
m_bNewItem(false),
......@@ -96,14 +93,12 @@ void MimeTypeData::writeAutoEmbed()
bool MimeTypeData::isEssential() const
{
// Keep in sync with KMimeTYpe::checkEssentialMimeTypes
// Keep in sync with KMimeType::checkEssentialMimeTypes
const QString n = name();
if ( n == "application/octet-stream" )
return true;
if ( n == "inode/directory" )
return true;
if ( n == "inode/directory-locked" ) // doesn't exist anymore, but the check doesn't hurt :)
return true;
if ( n == "inode/blockdevice" )
return true;
if ( n == "inode/chardevice" )
......@@ -267,158 +262,24 @@ bool MimeTypeData::sync()
void MimeTypeData::syncServices()
{
KConfig profile("profilerc", KConfig::NoGlobals);
// Deleting current contents in profilerc relating to
// this service type
//
const QStringList groups = profile.groupList();
QStringList::const_iterator it;
for (it = groups.begin(); it != groups.end(); it++ )
{
KConfigGroup group = profile.group( *it );
// Entries with Preference <= 0 or AllowAsDefault == false
// are not in m_services
if ( group.readEntry( "ServiceType" ) == name()
&& group.readEntry( "Preference" ) > 0
&& group.readEntry( "AllowAsDefault",false ) )
{
group.deleteGroup();
}
}
KSharedConfig::Ptr profile = KSharedConfig::openConfig("mimeapps.list", KConfig::NoGlobals, "xdgdata-apps");
// Save preferred services
//
groupCount = 1;
saveServices( profile, m_appServices, "Application" );
saveServices( profile, m_embedServices, "KParts/ReadOnlyPart" );
KConfigGroup addedApps(profile, "Added Associations");
saveServices(addedApps, m_appServices);
KConfigGroup addedParts(profile, "Added KDE Service Associations");
saveServices(addedParts, m_embedServices);
// Handle removed services
KService::List offerList =
KMimeTypeTrader::self()->query(m_mimetype->name(), "Application");
offerList += KMimeTypeTrader::self()->query(m_mimetype->name(), "KParts/ReadOnlyPart");
KService::List::const_iterator it_srv(offerList.begin());
for (; it_srv != offerList.end(); ++it_srv) {
KService::Ptr pService = (*it_srv);
bool isApplication = pService->isApplication();
if (isApplication && !pService->allowAsDefault())
continue; // Only those which were added in init()
// Look in the correct list...
if ( (isApplication && ! m_appServices.contains( pService->entryPath() ))
|| (!isApplication && !m_embedServices.contains( pService->entryPath() ))
) {
// The service was in m_appServices but has been removed
// create a new .desktop file without this mimetype
QStringList mimeTypeList = s_changedServices->contains( pService->entryPath())
? (*s_changedServices)[ pService->entryPath() ] : pService->serviceTypes();
if ( mimeTypeList.contains( name() ) ) {
// The mimetype is listed explicitly in the .desktop files, so
// just remove it and we're done
KDesktopFile *desktop;
if ( !isApplication )
{
desktop = new KDesktopFile("services", pService->entryPath());
}
else
{
QString path = pService->locateLocal();
KDesktopFile orig("apps", pService->entryPath());
desktop = orig.copyTo(path);
}
KConfigGroup group = desktop->desktopGroup();
mimeTypeList = s_changedServices->contains( pService->entryPath())
? (*s_changedServices)[ pService->entryPath() ] : group.readXdgListEntry("MimeType");
// Remove entry and the number that might follow.
for(int i=0;(i = mimeTypeList.indexOf(name())) != -1;)
{
QStringList::iterator it = mimeTypeList.begin()+i;
it = mimeTypeList.erase(it);
if (it != mimeTypeList.end())
{
// Check next item
bool numeric;
(*it).toInt(&numeric);
if (numeric)
mimeTypeList.erase(it);
}
}
group.writeXdgListEntry("MimeType", mimeTypeList);
// if two or more types have been modified, and they use the same service,
// accumulate the changes
(*s_changedServices)[ pService->entryPath() ] = mimeTypeList;
desktop->sync();
delete desktop;
}
else {
// The mimetype is not listed explicitly so it can't
// be removed. Preference = 0 handles this.
// Find a group header. The headers are just dummy names as far as
// KUserProfile is concerned, but using the mimetype makes it a
// bit more structured for "manual" reading
while ( profile.hasGroup(
name() + " - " + QString::number(groupCount) ) )
groupCount++;
KConfigGroup cg = profile.group( name() + " - " + QString::number(groupCount) );
cg.writeEntry("Application", pService->storageId());
cg.writeEntry("ServiceType", name());
cg.writeEntry("AllowAsDefault", true);
cg.writeEntry("Preference", 0);
}
}
}
}
static bool inheritsMimetype(KMimeType::Ptr m, const QStringList &mimeTypeList)
{
for(QStringList::const_iterator it = mimeTypeList.begin();
it != mimeTypeList.end(); ++it) {
if (m->is(*it))
return true;
}
return false;
KConfigGroup removedApps(profile, "Removed Associations");
saveRemovedServices(removedApps, m_appServices, "Application");
KConfigGroup removedParts(profile, "Removed KDE Service Associations");
saveRemovedServices(removedParts, m_embedServices, "KParts/ReadOnlyPart");
}
KMimeType::Ptr MimeTypeData::findImplicitAssociation(const QString &desktop)
{
QStringList mimeTypeList;
if (s_changedServices->contains(desktop)) {
mimeTypeList = s_changedServices->value( desktop );
} else {
KService::Ptr s = KService::serviceByDesktopPath(desktop);
if (!s) return KMimeType::Ptr(); // Hey, where did that one go?
mimeTypeList = s->serviceTypes();
}
for (QStringList::const_iterator it = mimeTypeList.begin();
it != mimeTypeList.end(); ++it) {
if ((m_mimetype->name() != *it) && m_mimetype->is(*it)) {
return KMimeType::mimeType(*it);
}
}
return KMimeType::Ptr();
}
void MimeTypeData::saveServices( KConfig & profile, const QStringList& services, const QString & genericServiceType )
static QStringList collectStorageIds(const QStringList& services)
{
QStringList serviceList;
QStringList::const_iterator it(services.begin());
for (int i = services.count(); it != services.end(); ++it, i--) {
......@@ -428,63 +289,43 @@ void MimeTypeData::saveServices( KConfig & profile, const QStringList& services,
continue; // Where did that one go?
}
// Find a group header. The headers are just dummy names as far as
// KUserProfile is concerned, but using the mimetype makes it a
// bit more structured for "manual" reading
while ( profile.hasGroup( name() + " - " + QString::number(groupCount) ) )
groupCount++;
KConfigGroup group = profile.group( name() + " - " + QString::number(groupCount) );
group.writeEntry("ServiceType", name());
group.writeEntry("GenericServiceType", genericServiceType);
group.writeEntry("Application", pService->storageId());
group.writeEntry("AllowAsDefault", true);
group.writeEntry("Preference", i);
// merge new mimetype
QStringList mimeTypeList = s_changedServices->contains( pService->entryPath())
? (*s_changedServices)[ pService->entryPath() ] : pService->serviceTypes();
if (!mimeTypeList.contains(name()) && !inheritsMimetype(m_mimetype, mimeTypeList))
{
KDesktopFile *desktop;
if ( !pService->isApplication() )
{
desktop = new KDesktopFile("services", pService->entryPath());
}
else
{
QString path = pService->locateLocal();
KDesktopFile orig("apps", pService->entryPath());
desktop = orig.copyTo(path);
}
KConfigGroup group = desktop->desktopGroup();
mimeTypeList = s_changedServices->contains( pService->entryPath())
? (*s_changedServices)[ pService->entryPath() ] : group.readXdgListEntry("MimeType");
mimeTypeList.append(name());
group.writeXdgListEntry("MimeType", mimeTypeList);
desktop->sync();
delete desktop;
// if two or more types have been modified, and they use the same service,
// accumulate the changes
(*s_changedServices)[ pService->entryPath() ] = mimeTypeList;
serviceList.append(pService->storageId());
}
return serviceList;
}
void MimeTypeData::saveRemovedServices(KConfigGroup & config, const QStringList& services, const QString& genericServiceType)
{
QStringList removedServiceList;
const KService::List offerList =
KMimeTypeTrader::self()->query(m_mimetype->name(), genericServiceType);
KService::List::const_iterator it_srv(offerList.begin());
for (; it_srv != offerList.end(); ++it_srv) {
KService::Ptr pService = (*it_srv);
if (!services.contains(pService->entryPath())) {
// The service was in m_appServices (or m_embedServices) but has been removed
removedServiceList.append(pService->storageId());
}
}
if (removedServiceList.isEmpty())
config.deleteEntry(name());
else
config.writeXdgListEntry(name(), removedServiceList);
}
void MimeTypeData::refresh()
void MimeTypeData::saveServices(KConfigGroup & config, const QStringList& services)
{
kDebug() << "MimeTypeData refresh" << name();
m_mimetype = KMimeType::mimeType( name() );
if (services.isEmpty())
config.deleteEntry(name());
else
config.writeXdgListEntry(name(), collectStorageIds(services));
}
void MimeTypeData::reset()
void MimeTypeData::refresh()
{
s_changedServices->clear();
kDebug() << "MimeTypeData refresh" << name();
m_mimetype = KMimeType::mimeType( name() );
}
void MimeTypeData::getAskSave(bool &_askSave)
......
......@@ -71,9 +71,6 @@ public:
void getAskSave(bool &);
void setAskSave(bool);
// Whether the given service lists this mimetype explicitly
KMimeType::Ptr findImplicitAssociation(const QString &desktop);
/**
* Returns true if the mimetype data has any unsaved changes.
*/
......@@ -89,8 +86,6 @@ public:
*/
void refresh();
static void reset();
private:
AutoEmbed readAutoEmbed() const;
void writeAutoEmbed();
......@@ -99,10 +94,10 @@ private:
void getServiceOffers(QStringList& appServices, QStringList& embedServices) const;
void getMyServiceOffers() const;
void syncServices();
void saveServices( KConfig & profile, const QStringList& services, const QString & servicetype2 );
void saveServices(KConfigGroup & config, const QStringList& services);
void saveRemovedServices(KConfigGroup & config, const QStringList& services, const QString& genericServiceType);
KMimeType::Ptr m_mimetype; // 0 if this is data for a mimetype group (m_isGroup==true)
unsigned int groupCount:16; // shared between saveServices and sync
enum AskSave { AskSaveYes = 0, AskSaveNo = 1, AskSaveDefault = 2 };
AskSave m_askSave:3;
AutoEmbed m_autoEmbed:3;
......
/* This file is part of the KDE project
Copyright (C) 2007 David Faure <faure@kde.org>
Copyright 2007 David Faure <faure@kde.org>
This program is free software: you can redistribute it and/or modify
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 2 of the License, or
(at your option) any later version.
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 program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
......@@ -36,9 +37,11 @@ class FileTypesTest : public QObject
private Q_SLOTS:
void initTestCase()
{
const QString profilerc = KStandardDirs::locateLocal( "config", "profilerc" );
if ( !profilerc.isEmpty() )
QFile::remove( profilerc );
m_kdehome = QDir::home().canonicalPath() + "/.kde-unit-test";
// We need a place where we can hack a mimeapps.list without harm, so not ~/.local
// This test relies on shared-mime-info being installed in /usr/share [or kdedir/share]
::setenv("XDG_DATA_DIRS", QFile::encodeName(m_kdehome + "/share:/usr/share"), 1 );
QFile::remove(m_kdehome + "/share/applications/mimeapps.list");
// Create fake application for some tests below.
bool mustUpdateKSycoca = false;
......@@ -61,7 +64,6 @@ private Q_SLOTS:
mustUpdateKSycoca = true;
}
if ( mustUpdateKSycoca ) {
// Update ksycoca in ~/.kde-unit-test after creating the above
QProcess::execute( KGlobal::dirs()->findExe(KBUILDSYCOCA_EXENAME) );
......@@ -181,7 +183,7 @@ private Q_SLOTS:
QVERIFY(!data.isDirty());
// Now test removing (in the same test, since it's inter-dependent)
appServices.remove(fakeApplication);
appServices.removeAll(fakeApplication);
data.setAppServices(appServices);
QVERIFY(data.isDirty());
data.sync();
......@@ -190,6 +192,9 @@ private Q_SLOTS:
QVERIFY(!data.appServices().contains(fakeApplication));
}
// TODO test removing an "implicit association"
// TODO see TODO in filetypesview
//void testDeleteMimeType()
//{
......@@ -214,6 +219,7 @@ private: // helper methods
}
QString fakeApplication;
QString m_kdehome;
};
QTEST_KDEMAIN( FileTypesTest, NoGUI )
......
Markdown is supported
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment