SPDX-FileCopyrightText: 2002 Joseph Wenninger <jowenn@kde.org>
SPDX-FileCopyrightText: 2020 Méven Car <meven.car@kdemail.net>
SPDX-FileCopyrightText: 2020 Tobias Fella <fella@posteo.de>
SPDX-FileCopyrightText: 2022 Méven Car <meven@kde.org>
SPDX-License-Identifier: GPL-2.0-or-later
*/
#include "componentchooser.h"
#include <QApplication>
#include <QDBusConnection>
#include <QDBusMessage>
#include <QMimeDatabase>
#include <KApplicationTrader>
#include <KBuildSycocaProgressDialog>
#include <KConfigGroup>
#include <KOpenWithDialog>
#include <KQuickConfigModule>
#include <KService>
#include <KSharedConfig>
extern KSERVICE_EXPORT int ksycoca_ms_between_checks;
ComponentChooser::ComponentChooser(QObject *parent,
const QString &mimeType,
const QString &applicationCategory,
const QString &defaultApplication,
const QString &dialogText)
: QObject(parent)
, m_mimeType(mimeType)
, m_applicationCategory(applicationCategory)
, m_defaultApplication(defaultApplication)
, m_dialogText(dialogText)
{
qRegisterMetaType<QList<PairQml>>("QList<PairQml>");
m_model = new ApplicationModel(this);
connect(m_model, &QAbstractItemModel::modelReset, this, &ComponentChooser::modelChanged);
}
void ComponentChooser::defaults()
{
const auto defaultIndex = m_model->defaultIndex();
if (defaultIndex) {
select(*defaultIndex);
}
}
void ComponentChooser::load()
{
m_model->load(m_mimeType, m_applicationCategory, m_defaultApplication, KApplicationTrader::preferredService(m_mimeType));
m_index = m_model->currentIndex();
m_currentApplication = currentStorageId();
Q_EMIT indexChanged();
Q_EMIT isDefaultsChanged();
}
void ComponentChooser::select(int index)
{
if (index == m_model->rowCount() - 1) {
KOpenWithDialog *dialog = new KOpenWithDialog(QList<QUrl>(), m_mimeType, m_dialogText, QString(), QApplication::activeWindow());
dialog->setSaveNewApplications(true);
dialog->setAttribute(Qt::WA_DeleteOnClose);
connect(dialog, &KOpenWithDialog::finished, this, [this, dialog](int result) {
if (result == QDialog::Rejected) {
Q_EMIT indexChanged();
Q_EMIT isDefaultsChanged();
return;
}
const auto serviceStorageId = dialog->service()->storageId();
QModelIndex modelIndex = m_model->findByStorageId(serviceStorageId);
if (modelIndex.isValid()) {
select(modelIndex.row());
return;
}
auto newIndex = m_model->addApplicationBeforeLast(dialog->service());
select(newIndex);
});
dialog->open();
} else {
m_index = index;
const auto selectedIndex = m_model->index(index, 0);
m_model->setData(selectedIndex, true, ApplicationModel::Selected);
Q_EMIT indexChanged();
Q_EMIT isDefaultsChanged();
}
}
void ComponentChooser::saveMimeTypeAssociations(const QString &storageId, const QStringList &mimeTypes, bool forceUnsupportedMimeType)
{
if (storageId.isEmpty()) {
return;
}
KSharedConfig::Ptr mimeAppsList = KSharedConfig::openConfig(QStringLiteral("mimeapps.list"), KConfig::NoGlobals, QStandardPaths::GenericConfigLocation);
if (mimeAppsList->isConfigWritable(true)) {
const auto appService = KService::serviceByStorageId(storageId);
for (const QString &mimeType : mimeTypes) {
if (!forceUnsupportedMimeType && appService && !serviceSupportsMimeType(appService, mimeType)) {
continue;
}
KApplicationTrader::setPreferredService(mimeType, appService);
}
m_currentApplication = storageId;
}
}
void ComponentChooser::onSaved()
{
Q_EMIT indexChanged();
Q_EMIT isDefaultsChanged();
}
QStringList ComponentChooser::unsupportedMimeTypes() const
{
const auto preferredApp = currentStorageId();
if (preferredApp.isEmpty()) {
return QStringList{};
}
const auto db = QMimeDatabase();
QStringList unsupportedMimeTypes;
const auto appService = KService::serviceByStorageId(preferredApp);
const auto componentMimeTypes = mimeTypes();
for (const QString &mimeType : componentMimeTypes) {
if (!serviceSupportsMimeType(appService, mimeType)) {
const auto preferredService = KApplicationTrader::preferredService(mimeType);
if (!preferredService || (preferredService->storageId() != appService->storageId())) {
unsupportedMimeTypes << mimeType;
}
}
}
return unsupportedMimeTypes;
}
void ComponentChooser::saveAssociationUnsuportedMimeTypes()
{
const auto storageId = currentStorageId();
saveMimeTypeAssociations(storageId, unsupportedMimeTypes(), true);
forceReloadServiceCache();
load();
}
QList<PairQml> ComponentChooser::mimeTypesNotAssociated() const
{
auto ret = QList<PairQml>();
const auto db = QMimeDatabase();
const auto storageId = m_currentApplication;
const auto appService = KService::serviceByStorageId(storageId);
if (!appService) {
return ret;
}
const auto mimes = mimeTypes();
for (const QString &mimeType : mimes) {
const auto service = KApplicationTrader::preferredService(mimeType);
if (service && service->storageId() != storageId &&
serviceSupportsMimeType(appService, mimeType)) {
ret.append(PairQml(service->name(), mimeType));
}
}
return ret;
}
void ComponentChooser::saveMimeTypesNotAssociated()
{
const auto mimeTypeNotAssociated = mimeTypesNotAssociated();
auto mimeTypeList = QList<QString>();
for (const auto &pair : mimeTypeNotAssociated) {
mimeTypeList.append(pair.second.toString());
}
const auto storageId = currentStorageId();
saveMimeTypeAssociations(storageId, mimeTypeList);
forceReloadServiceCache();
load();
}
QString ComponentChooser::currentStorageId() const
{
return m_model->data(m_index, ApplicationModel::StorageId).toString();
}
QString ComponentChooser::applicationName() const
{
return m_model->data(m_index, Qt::DisplayRole).toString();
}
QString ComponentChooser::applicationIcon() const
{
return m_model->data(m_index, ApplicationModel::Icon).toString();
}
QStringList ComponentChooser::mimeTypes() const
{
return QStringList{};
}
void ComponentChooser::forceReloadServiceCache()
{
KBuildSycocaProgressDialog::rebuildKSycoca(QApplication::activeWindow());
int previous_delay = ksycoca_ms_between_checks;
ksycoca_ms_between_checks = 0;
KService::allServices();
ksycoca_ms_between_checks = previous_delay;
}
void ComponentChooser::save()
{
const auto storageId = currentStorageId();
saveMimeTypeAssociations(storageId, mimeTypes());
}
bool ComponentChooser::isDefaults() const
{
const auto defaultIndex = m_model->defaultIndex();
return !defaultIndex.has_value() || *defaultIndex == m_index;
}
bool ComponentChooser::isSaveNeeded() const
{
const auto storageId = currentStorageId();
return m_model->rowCount() > 1 && (m_currentApplication != storageId) && storageId != "";
}
bool ComponentChooser::serviceSupportsMimeType(KService::Ptr service, const QString &targetMimeType)
{
static QMimeDatabase db;
const QStringList mimeTypes = service->mimeTypes();
for (const QString &supportedMimeType : mimeTypes) {
if (supportedMimeType == targetMimeType) {
return true;
}
if (db.mimeTypeForName(targetMimeType).inherits(supportedMimeType)) {
return true;
}
}
if (targetMimeType.startsWith(QLatin1String("x-scheme-handler/")) && service->schemeHandlers().contains(targetMimeType.mid(17))) {
return true;
}
return false;
}