diff --git a/applets/kicker/plugin/actionlist.cpp b/applets/kicker/plugin/actionlist.cpp index 339eecbc6..7daa7f796 100644 --- a/applets/kicker/plugin/actionlist.cpp +++ b/applets/kicker/plugin/actionlist.cpp @@ -1,414 +1,414 @@ /*************************************************************************** * Copyright (C) 2013 by Aurélien Gâteau * * Copyright (C) 2014 by Eike Hein * * * * 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. * * * * This program 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 General Public License for more details. * * * * You should have received a copy of the GNU General Public License * * along with this program; if not, write to the * * Free Software Foundation, Inc., * * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA . * ***************************************************************************/ #include "actionlist.h" #include "menuentryeditor.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include "containmentinterface.h" #ifdef HAVE_APPSTREAMQT #include #endif namespace KAStats = KActivities::Stats; using namespace KAStats; using namespace KAStats::Terms; namespace Kicker { QVariantMap createActionItem(const QString &label, const QString &actionId, const QVariant &argument) { QVariantMap map; map[QStringLiteral("text")] = label; map[QStringLiteral("actionId")] = actionId; if (argument.isValid()) { map[QStringLiteral("actionArgument")] = argument; } return map; } QVariantMap createTitleActionItem(const QString &label) { QVariantMap map; map[QStringLiteral("text")] = label; map[QStringLiteral("type")] = QStringLiteral("title"); return map; } QVariantMap createSeparatorActionItem() { QVariantMap map; map[QStringLiteral("type")] = QStringLiteral("separator"); return map; } QVariantList createActionListForFileItem(const KFileItem &fileItem) { QVariantList list; KService::List services = KMimeTypeTrader::self()->query(fileItem.mimetype(), QStringLiteral("Application")); if (!services.isEmpty()) { list << createTitleActionItem(i18n("Open with:")); foreach (const KService::Ptr service, services) { const QString text = service->name().replace(QLatin1Char('&'), QStringLiteral("&&")); QVariantMap item = createActionItem(text, QStringLiteral("_kicker_fileItem_openWith"), service->entryPath()); item[QStringLiteral("icon")] = service->icon(); list << item; } list << createSeparatorActionItem(); } QVariantMap propertiesItem = createActionItem(i18n("Properties"), QStringLiteral("_kicker_fileItem_properties")); propertiesItem[QStringLiteral("icon")] = QStringLiteral("document-properties"); list << propertiesItem; return list; } bool handleFileItemAction(const KFileItem &fileItem, const QString &actionId, const QVariant &argument, bool *close) { if (actionId == QLatin1String("_kicker_fileItem_properties")) { KPropertiesDialog *dlg = new KPropertiesDialog(fileItem, QApplication::activeWindow()); dlg->setAttribute(Qt::WA_DeleteOnClose); dlg->show(); *close = false; return true; } if (actionId == QLatin1String("_kicker_fileItem_openWith")) { const QString path = argument.toString(); const KService::Ptr service = KService::serviceByDesktopPath(path); if (!service) { return false; } KRun::runService(*service, QList() << fileItem.url(), QApplication::activeWindow()); *close = true; return true; } return false; } QVariantList createAddLauncherActionList(QObject *appletInterface, const KService::Ptr &service) { QVariantList actionList; if (!service) { return actionList; } if (ContainmentInterface::mayAddLauncher(appletInterface, ContainmentInterface::Desktop)) { actionList << Kicker::createActionItem(i18n("Add to Desktop"), QStringLiteral("addToDesktop")); } if (ContainmentInterface::mayAddLauncher(appletInterface, ContainmentInterface::Panel)) { actionList << Kicker::createActionItem(i18n("Add to Panel (Widget)"), QStringLiteral("addToPanel")); } if (service && ContainmentInterface::mayAddLauncher(appletInterface, ContainmentInterface::TaskManager, Kicker::resolvedServiceEntryPath(service))) { actionList << Kicker::createActionItem(i18n("Pin to Task Manager"), QStringLiteral("addToTaskManager")); } return actionList; } bool handleAddLauncherAction(const QString &actionId, QObject *appletInterface, const KService::Ptr &service) { if (!service) { return false; } if (actionId == QLatin1String("addToDesktop")) { if (ContainmentInterface::mayAddLauncher(appletInterface, ContainmentInterface::Desktop)) { ContainmentInterface::addLauncher(appletInterface, ContainmentInterface::Desktop, Kicker::resolvedServiceEntryPath(service)); } return true; } else if (actionId == QLatin1String("addToPanel")) { if (ContainmentInterface::mayAddLauncher(appletInterface, ContainmentInterface::Panel)) { ContainmentInterface::addLauncher(appletInterface, ContainmentInterface::Panel, Kicker::resolvedServiceEntryPath(service)); } return true; } else if (actionId == QLatin1String("addToTaskManager")) { if (ContainmentInterface::mayAddLauncher(appletInterface, ContainmentInterface::TaskManager, Kicker::resolvedServiceEntryPath(service))) { ContainmentInterface::addLauncher(appletInterface, ContainmentInterface::TaskManager, Kicker::resolvedServiceEntryPath(service)); } return true; } return false; } QString storageIdFromService(KService::Ptr service) { QString storageId = service->storageId(); if (storageId.endsWith(QLatin1String(".desktop"))) { storageId = storageId.left(storageId.length() - 8); } return storageId; } QVariantList jumpListActions(KService::Ptr service) { QVariantList list; if (!service) { return list; } const auto &actions = service->actions(); foreach (const KServiceAction &action, actions) { if (action.text().isEmpty() || action.exec().isEmpty()) { continue; } QVariantMap item = createActionItem(action.text(), QStringLiteral("_kicker_jumpListAction"), action.exec()); item[QStringLiteral("icon")] = action.icon(); list << item; } return list; } QVariantList recentDocumentActions(KService::Ptr service) { QVariantList list; if (!service) { return list; } const QString storageId = storageIdFromService(service); if (storageId.isEmpty()) { return list; } auto query = UsedResources | RecentlyUsedFirst | Agent(storageId) | Type::any() | Activity::current() | Url::file(); ResultSet results(query); ResultSet::const_iterator resultIt; resultIt = results.begin(); while (list.count() < 6 && resultIt != results.end()) { const QString resource = (*resultIt).resource(); ++resultIt; const QUrl url(resource); if (!url.isValid()) { continue; } const KFileItem fileItem(url); if (!fileItem.isFile()) { continue; } if (list.isEmpty()) { list << createTitleActionItem(i18n("Recent Documents")); } QVariantMap item = createActionItem(url.fileName(), QStringLiteral("_kicker_recentDocument"), resource); item[QStringLiteral("icon")] = fileItem.iconName(); list << item; } if (!list.isEmpty()) { QVariantMap forgetAction = createActionItem(i18n("Forget Recent Documents"), QStringLiteral("_kicker_forgetRecentDocuments")); forgetAction[QStringLiteral("icon")] = QStringLiteral("edit-clear-history"); list << forgetAction; } return list; } bool handleRecentDocumentAction(KService::Ptr service, const QString &actionId, const QVariant &_argument) { if (!service) { return false; } if (actionId == QLatin1String("_kicker_forgetRecentDocuments")) { const QString storageId = storageIdFromService(service); if (storageId.isEmpty()) { return false; } auto query = UsedResources | Agent(storageId) | Type::any() | Activity::current() | Url::file(); KAStats::forgetResources(query); return false; } QString argument = _argument.toString(); if (argument.isEmpty()) { return false; } return (KRun::runService(*service, QList() << QUrl(argument), QApplication::activeWindow()) != 0); } Q_GLOBAL_STATIC(MenuEntryEditor, menuEntryEditor) bool canEditApplication(const KService::Ptr &service) { return (service->isApplication() && menuEntryEditor->canEdit(service->entryPath())); } void editApplication(const QString &entryPath, const QString &menuId) { menuEntryEditor->edit(entryPath, menuId); } QVariantList editApplicationAction(const KService::Ptr &service) { QVariantList actionList; if (canEditApplication(service)) { QVariantMap editAction = Kicker::createActionItem(i18n("Edit Application..."), QStringLiteral("editApplication")); editAction[QStringLiteral("icon")] = QStringLiteral("kmenuedit"); // TODO: Using the KMenuEdit icon might be misleading. actionList << editAction; } return actionList; } bool handleEditApplicationAction(const QString &actionId, const KService::Ptr &service) { if (service && actionId ==QLatin1String("editApplication") && canEditApplication(service)) { Kicker::editApplication(service->entryPath(), service->menuId()); return true; } return false; } #ifdef HAVE_APPSTREAMQT Q_GLOBAL_STATIC(AppStream::Pool, appstreamPool) #endif QVariantList appstreamActions(const KService::Ptr &service) { QVariantList ret; #ifdef HAVE_APPSTREAMQT const KService::Ptr appStreamHandler = KMimeTypeTrader::self()->preferredService(QStringLiteral("x-scheme-handler/appstream")); // Don't show action if we can't find any app to handle appstream:// URLs. if (!appStreamHandler) { if (!KProtocolInfo::isHelperProtocol(QStringLiteral("appstream")) || KProtocolInfo::exec(QStringLiteral("appstream")).isEmpty()) { return ret; } } if (!appstreamPool.exists()) { appstreamPool->load(); } const auto components = appstreamPool->componentsById(service->desktopEntryName()+QLatin1String(".desktop")); for(const auto &component: components) { const QString componentId = component.id(); - QVariantMap appstreamAction = Kicker::createActionItem(i18nc("@action opens a software center with the application", "Uninstall or Manage Add-Ons..."), "manageApplication", QVariant(QStringLiteral("appstream://") + componentId)); + QVariantMap appstreamAction = Kicker::createActionItem(i18nc("@action opens a software center with the application", "Uninstall or Manage Add-Ons..."), "manageApplication", QVariant(QLatin1String("appstream://") + componentId)); appstreamAction[QStringLiteral("icon")] = QStringLiteral("plasmadiscover"); ret << appstreamAction; } #else Q_UNUSED(service) #endif return ret; } bool handleAppstreamActions(const QString &actionId, const QVariant &argument) { if (actionId == QLatin1String("manageApplication")) { return QDesktopServices::openUrl(QUrl(argument.toString())); } return false; } QString resolvedServiceEntryPath(const KService::Ptr &service) { QString path = service->entryPath(); if (!QDir::isAbsolutePath(path)) { - path = QStandardPaths::locate(QStandardPaths::GenericDataLocation, QStringLiteral("kservices5/") + path); + path = QStandardPaths::locate(QStandardPaths::GenericDataLocation, QLatin1String("kservices5/") + path); } return path; } } diff --git a/applets/kicker/plugin/appentry.cpp b/applets/kicker/plugin/appentry.cpp index 317182f5e..820a94c12 100644 --- a/applets/kicker/plugin/appentry.cpp +++ b/applets/kicker/plugin/appentry.cpp @@ -1,303 +1,303 @@ /*************************************************************************** * Copyright (C) 201 by Eike Hein * * * * 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. * * * * This program 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 General Public License for more details. * * * * You should have received a copy of the GNU General Public License * * along with this program; if not, write to the * * Free Software Foundation, Inc., * * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA . * ***************************************************************************/ #include #include "appentry.h" #include "actionlist.h" #include "appsmodel.h" #include "containmentinterface.h" #include #include #include #include #if HAVE_X11 #include #endif #include #include #include #include #include #include #include #include #include #include #include AppEntry::AppEntry(AbstractModel *owner, KService::Ptr service, NameFormat nameFormat) : AbstractEntry(owner) , m_service(service) { if (m_service) { init(nameFormat); } } AppEntry::AppEntry(AbstractModel *owner, const QString &id) : AbstractEntry(owner) { const QUrl url(id); if (url.scheme() == QLatin1String("preferred")) { m_service = defaultAppByName(url.host()); m_id = id; } else { m_service = KService::serviceByStorageId(id); } if (m_service) { init((NameFormat)owner->rootModel()->property("appNameFormat").toInt()); } } void AppEntry::init(NameFormat nameFormat) { m_name = nameFromService(m_service, nameFormat); if (nameFormat == GenericNameOnly) { m_description = nameFromService(m_service, NameOnly); } else { m_description = nameFromService(m_service, GenericNameOnly); } } bool AppEntry::isValid() const { return m_service; } QIcon AppEntry::icon() const { if (m_icon.isNull()) { m_icon = QIcon::fromTheme(m_service->icon(), QIcon::fromTheme(QStringLiteral("unknown"))); } return m_icon; } QString AppEntry::name() const { return m_name; } QString AppEntry::description() const { return m_description; } KService::Ptr AppEntry::service() const { return m_service; } QString AppEntry::id() const { if (!m_id.isEmpty()) { return m_id; } return m_service->storageId(); } QString AppEntry::menuId() const { return m_service->menuId(); } QUrl AppEntry::url() const { return QUrl::fromLocalFile(Kicker::resolvedServiceEntryPath(m_service)); } bool AppEntry::hasActions() const { return true; } QVariantList AppEntry::actions() const { QVariantList actionList; actionList << Kicker::jumpListActions(m_service); if (!actionList.isEmpty()) { actionList << Kicker::createSeparatorActionItem(); } QObject *appletInterface = m_owner->rootModel()->property("appletInterface").value(); const bool systemImmutable = appletInterface->property("immutability").toInt() == Plasma::Types::SystemImmutable; const QVariantList &addLauncherActions = Kicker::createAddLauncherActionList(appletInterface, m_service); if (!systemImmutable && !addLauncherActions.isEmpty()) { actionList << addLauncherActions << Kicker::createSeparatorActionItem(); } const QVariantList &recentDocuments = Kicker::recentDocumentActions(m_service); if (!recentDocuments.isEmpty()) { actionList << recentDocuments << Kicker::createSeparatorActionItem(); } // Don't allow adding launchers, editing, hiding, or uninstalling applications // when system is immutable. if (systemImmutable) { return actionList; } if (m_service->isApplication()) { actionList << Kicker::createSeparatorActionItem(); actionList << Kicker::editApplicationAction(m_service); actionList << Kicker::appstreamActions(m_service); } QQmlPropertyMap *appletConfig = qobject_cast(appletInterface->property("configuration").value()); if (appletConfig && appletConfig->contains(QLatin1String("hiddenApplications")) && qobject_cast(m_owner)) { const QStringList &hiddenApps = appletConfig->value(QLatin1String("hiddenApplications")).toStringList(); if (!hiddenApps.contains(m_service->menuId())) { QVariantMap hideAction = Kicker::createActionItem(i18n("Hide Application"), QStringLiteral("hideApplication")); hideAction[QStringLiteral("icon")] = QStringLiteral("hint"); actionList << hideAction; } } return actionList; } bool AppEntry::run(const QString& actionId, const QVariant &argument) { if (!m_service->isValid()) { return false; } if (actionId.isEmpty()) { quint32 timeStamp = 0; #if HAVE_X11 if (QX11Info::isPlatformX11()) { timeStamp = QX11Info::appUserTime(); } #endif KRun::runApplication(*m_service, {}, nullptr, KRun::DeleteTemporaryFiles, {}, KStartupInfo::createNewStartupIdForTimestamp(timeStamp)); KActivities::ResourceInstance::notifyAccessed(QUrl(QStringLiteral("applications:") + m_service->storageId()), QStringLiteral("org.kde.plasma.kicker")); return true; } QObject *appletInterface = m_owner->rootModel()->property("appletInterface").value(); if (Kicker::handleAddLauncherAction(actionId, appletInterface, m_service)) { return true; } else if (Kicker::handleEditApplicationAction(actionId, m_service)) { return true; } else if (Kicker::handleAppstreamActions(actionId, argument)) { return true; } else if (actionId == QLatin1String("_kicker_jumpListAction")) { return KRun::run(argument.toString(), {}, nullptr, m_service->name(), m_service->icon()); } return Kicker::handleRecentDocumentAction(m_service, actionId, argument); } QString AppEntry::nameFromService(const KService::Ptr service, NameFormat nameFormat) { const QString &name = service->name(); QString genericName = service->genericName(); if (genericName.isEmpty()) { genericName = service->comment(); } if (nameFormat == NameOnly || genericName.isEmpty() || name == genericName) { return name; } else if (nameFormat == GenericNameOnly) { return genericName; } else if (nameFormat == NameAndGenericName) { return i18nc("App name (Generic name)", "%1 (%2)", name, genericName); } else { return i18nc("Generic name (App name)", "%1 (%2)", genericName, name); } } KService::Ptr AppEntry::defaultAppByName(const QString& name) { if (name == QLatin1String("browser")) { KConfigGroup config(KSharedConfig::openConfig(), "General"); QString browser = config.readPathEntry("BrowserApplication", QString()); if (browser.isEmpty()) { return KMimeTypeTrader::self()->preferredService(QLatin1String("text/html")); } else if (browser.startsWith(QLatin1Char('!'))) { - browser = browser.mid(1); + browser.remove(0, 1); } return KService::serviceByStorageId(browser); } return KService::Ptr(); } AppGroupEntry::AppGroupEntry(AppsModel *parentModel, KServiceGroup::Ptr group, bool paginate, int pageSize, bool flat, bool sorted, bool separators, int appNameFormat) : AbstractGroupEntry(parentModel), m_group(group) { AppsModel* model = new AppsModel(group->entryPath(), paginate, pageSize, flat, sorted, separators, parentModel); model->setAppNameFormat(appNameFormat); m_childModel = model; QObject::connect(parentModel, &AppsModel::cleared, model, &AppsModel::deleteLater); QObject::connect(model, &AppsModel::countChanged, [parentModel, this] { if (parentModel) { parentModel->entryChanged(this); } } ); QObject::connect(model, &AppsModel::hiddenEntriesChanged, [parentModel, this] { if (parentModel) { parentModel->entryChanged(this); } } ); } QIcon AppGroupEntry::icon() const { if (m_icon.isNull()) { m_icon = QIcon::fromTheme(m_group->icon(), QIcon::fromTheme(QStringLiteral("unknown"))); } return m_icon; } QString AppGroupEntry::name() const { return m_group->caption(); } bool AppGroupEntry::hasChildren() const { return m_childModel && m_childModel->count() > 0; } AbstractModel *AppGroupEntry::childModel() const { return m_childModel; } diff --git a/applets/kicker/plugin/kastatsfavoritesmodel.cpp b/applets/kicker/plugin/kastatsfavoritesmodel.cpp index fe49c54a1..3474faefa 100644 --- a/applets/kicker/plugin/kastatsfavoritesmodel.cpp +++ b/applets/kicker/plugin/kastatsfavoritesmodel.cpp @@ -1,719 +1,719 @@ /*************************************************************************** * Copyright (C) 2014-2015 by Eike Hein * * Copyright (C) 2016-2017 by Ivan Cukic * * * * 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. * * * * This program 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 General Public License for more details. * * * * You should have received a copy of the GNU General Public License * * along with this program; if not, write to the * * Free Software Foundation, Inc., * * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA . * ***************************************************************************/ #include "kastatsfavoritesmodel.h" #include "appentry.h" #include "contactentry.h" #include "fileentry.h" #include "actionlist.h" #include "debug.h" #include #include #include #include #include #include #include #include #include #include #include namespace KAStats = KActivities::Stats; using namespace KAStats; using namespace KAStats::Terms; #define AGENT_APPLICATIONS QStringLiteral("org.kde.plasma.favorites.applications") #define AGENT_CONTACTS QStringLiteral("org.kde.plasma.favorites.contacts") #define AGENT_DOCUMENTS QStringLiteral("org.kde.plasma.favorites.documents") QString agentForUrl(const QString &url) { return url.startsWith(QLatin1String("ktp:")) ? AGENT_CONTACTS : url.startsWith(QLatin1String("preferred:")) ? AGENT_APPLICATIONS : url.startsWith(QLatin1String("applications:")) ? AGENT_APPLICATIONS : (url.startsWith(QLatin1Char('/')) && !url.endsWith(QLatin1String(".desktop"))) ? AGENT_DOCUMENTS : (url.startsWith(QLatin1String("file:/")) && !url.endsWith(QLatin1String(".desktop"))) ? AGENT_DOCUMENTS // use applications as the default : AGENT_APPLICATIONS; } class KAStatsFavoritesModel::Private: public QAbstractListModel { public: class NormalizedId { public: NormalizedId() { } NormalizedId(const Private *parent, const QString &id) { if (id.isEmpty()) return; QSharedPointer entry = nullptr; if (parent->m_itemEntries.contains(id)) { entry = parent->m_itemEntries[id]; } else { // This entry is not cached - it is temporary, // so let's clean up when we exit this function entry = parent->entryForResource(id); } if (!entry || !entry->isValid()) { qWarning() << "Entry is not valid" << id << entry; m_id = id; return; } const auto url = entry->url(); qCDebug(KICKER_DEBUG) << "Original id is: " << id << ", and the url is" << url; // Preferred applications need special handling if (entry->id().startsWith(QLatin1String("preferred:"))) { m_id = entry->id(); return; } // If this is an application, use the applications:-format url auto appEntry = dynamic_cast(entry.data()); if (appEntry && !appEntry->menuId().isEmpty()) { - m_id = QStringLiteral("applications:") + appEntry->menuId(); + m_id = QLatin1String("applications:") + appEntry->menuId(); return; } // We want to resolve symbolic links not to have two paths // refer to the same .desktop file if (url.isLocalFile()) { QFileInfo file(url.toLocalFile()); if (file.exists()) { m_id = QUrl::fromLocalFile(file.canonicalFilePath()).toString(); return; } } // If this is a file, we should have already covered it if (url.scheme() == QLatin1String("file")) { return; } m_id = url.toString(); } const QString& value() const { return m_id; } bool operator==(const NormalizedId &other) const { return m_id == other.m_id; } private: QString m_id; }; NormalizedId normalizedId(const QString &id) const { return NormalizedId(this, id); } QSharedPointer entryForResource(const QString &resource) const { using SP = QSharedPointer; const auto agent = agentForUrl(resource); if (agent == AGENT_CONTACTS) { return SP(new ContactEntry(q, resource)); } else if (agent == AGENT_DOCUMENTS) { if (resource.startsWith(QLatin1String("/"))) { return SP(new FileEntry(q, QUrl::fromLocalFile(resource))); } else { return SP(new FileEntry(q, QUrl(resource))); } } else if (agent == AGENT_APPLICATIONS) { if (resource.startsWith(QLatin1String("applications:"))) { return SP(new AppEntry(q, resource.mid(13))); } else { return SP(new AppEntry(q, resource)); } } else { return {}; } } Private(KAStatsFavoritesModel *parent, QString clientId) : q(parent) , m_query( LinkedResources | Agent { AGENT_APPLICATIONS, AGENT_CONTACTS, AGENT_DOCUMENTS } | Type::any() | Activity::current() | Activity::global() | Limit::all() ) , m_watcher(m_query) , m_clientId(clientId) { // Connecting the watcher connect(&m_watcher, &ResultWatcher::resultLinked, [this] (const QString &resource) { addResult(resource, -1); }); connect(&m_watcher, &ResultWatcher::resultUnlinked, [this] (const QString &resource) { removeResult(resource); }); // Loading the items order const auto cfg = KSharedConfig::openConfig(QStringLiteral("kactivitymanagerd-statsrc")); // We want first to check whether we have an ordering for this activity. // If not, we will try to get a global one for this applet const QString thisGroupName = QStringLiteral("Favorites-") + clientId + QStringLiteral("-") + m_activities.currentActivity(); const QString globalGroupName = QStringLiteral("Favorites-") + clientId + QStringLiteral("-global"); KConfigGroup thisCfgGroup(cfg, thisGroupName); KConfigGroup globalCfgGroup(cfg, globalGroupName); QStringList ordering = thisCfgGroup.readEntry("ordering", QStringList()) + globalCfgGroup.readEntry("ordering", QStringList()); qCDebug(KICKER_DEBUG) << "Loading the ordering " << ordering; // Loading the results without emitting any model signals qCDebug(KICKER_DEBUG) << "Query is" << m_query; ResultSet results(m_query); for (const auto& result: results) { qCDebug(KICKER_DEBUG) << "Got " << result.resource() << " -->"; addResult(result.resource(), -1, false); } // Normalizing all the ids std::transform(ordering.begin(), ordering.end(), ordering.begin(), [&] (const QString &item) { return normalizedId(item).value(); }); // Sorting the items in the cache std::sort(m_items.begin(), m_items.end(), [&] (const NormalizedId &left, const NormalizedId &right) { auto leftIndex = ordering.indexOf(left.value()); auto rightIndex = ordering.indexOf(right.value()); return (leftIndex == -1 && rightIndex == -1) ? left.value() < right.value() : (leftIndex == -1) ? false : (rightIndex == -1) ? true : // otherwise leftIndex < rightIndex; }); // Debugging: QVector itemStrings(m_items.size()); std::transform(m_items.cbegin(), m_items.cend(), itemStrings.begin(), [] (const NormalizedId &item) { return item.value(); }); qCDebug(KICKER_DEBUG) << "After ordering: " << itemStrings; } void addResult(const QString &_resource, int index, bool notifyModel = true) { // We want even files to have a proper URL const auto resource = _resource.startsWith(QLatin1Char('/')) ? QUrl::fromLocalFile(_resource).toString() : _resource; qCDebug(KICKER_DEBUG) << "Adding result" << resource << "already present?" << m_itemEntries.contains(resource); if (m_itemEntries.contains(resource)) return; auto entry = entryForResource(resource); if (!entry || !entry->isValid()) { qCDebug(KICKER_DEBUG) << "Entry is not valid!"; return; } if (index == -1) { index = m_items.count(); } if (notifyModel) { beginInsertRows(QModelIndex(), index, index); } auto url = entry->url(); m_itemEntries[resource] = m_itemEntries[entry->id()] = m_itemEntries[url.toString()] = m_itemEntries[url.toLocalFile()] = entry; auto normalized = normalizedId(resource); m_items.insert(index, normalized); m_itemEntries[normalized.value()] = entry; if (notifyModel) { endInsertRows(); saveOrdering(); } } void removeResult(const QString &resource) { auto normalized = normalizedId(resource); // If we know this item will not really be removed, // but only that activities it is on have changed, // lets leave it if (m_ignoredItems.contains(normalized.value())) { m_ignoredItems.removeAll(normalized.value()); return; } qCDebug(KICKER_DEBUG) << "Removing result" << resource; auto index = m_items.indexOf(normalizedId(resource)); if (index == -1) return; beginRemoveRows(QModelIndex(), index, index); auto entry = m_itemEntries[resource]; m_items.removeAt(index); // Removing the entry from the cache QMutableHashIterator> i(m_itemEntries); while (i.hasNext()) { i.next(); if (i.value() == entry) { i.remove(); } } endRemoveRows(); } int rowCount(const QModelIndex &parent = QModelIndex()) const override { if (parent.isValid()) return 0; return m_items.count(); } QVariant data(const QModelIndex &item, int role = Qt::DisplayRole) const override { if (item.parent().isValid()) return QVariant(); const auto index = item.row(); const auto entry = m_itemEntries[m_items[index].value()]; return entry == nullptr ? QVariant() : role == Qt::DisplayRole ? entry->name() : role == Qt::DecorationRole ? entry->icon() : role == Kicker::DescriptionRole ? entry->description() : role == Kicker::FavoriteIdRole ? entry->id() : role == Kicker::UrlRole ? entry->url() : role == Kicker::HasActionListRole ? entry->hasActions() : role == Kicker::ActionListRole ? entry->actions() : QVariant(); } bool trigger(int row, const QString &actionId, const QVariant &argument) { if (row < 0 || row >= rowCount()) { return false; } const QString id = data(index(row, 0), Kicker::UrlRole).toString(); return m_itemEntries.contains(id) ? m_itemEntries[id]->run(actionId, argument) : false; } void move(int from, int to) { if (from < 0) return; if (from >= m_items.count()) return; if (to < 0) return; if (to >= m_items.count()) return; if (from == to) return; const int modelTo = to + (to > from ? 1 : 0); if (q->beginMoveRows(QModelIndex(), from, from, QModelIndex(), modelTo)) { m_items.move(from, to); q->endMoveRows(); qCDebug(KICKER_DEBUG) << "Save ordering (from Private::move) -->"; saveOrdering(); } } void saveOrdering() { QStringList ids; for (const auto& item: m_items) { ids << item.value(); } qCDebug(KICKER_DEBUG) << "Save ordering (from Private::saveOrdering) -->"; saveOrdering(ids, m_clientId, m_activities.currentActivity()); } static void saveOrdering(const QStringList &ids, const QString &clientId, const QString ¤tActivity) { const auto cfg = KSharedConfig::openConfig(QStringLiteral("kactivitymanagerd-statsrc")); QStringList activities { currentActivity, QStringLiteral("global") }; qCDebug(KICKER_DEBUG) << "Saving ordering for" << currentActivity << "and global" << ids; for (const auto& activity: activities) { const QString groupName = QStringLiteral("Favorites-") + clientId + QStringLiteral("-") + activity; KConfigGroup cfgGroup(cfg, groupName); cfgGroup.writeEntry("ordering", ids); } cfg->sync(); } KAStatsFavoritesModel *const q; KActivities::Consumer m_activities; Query m_query; ResultWatcher m_watcher; QString m_clientId; QVector m_items; QHash> m_itemEntries; QStringList m_ignoredItems; }; KAStatsFavoritesModel::KAStatsFavoritesModel(QObject *parent) : PlaceholderModel(parent) , d(nullptr) // we have no client id yet , m_enabled(true) , m_maxFavorites(-1) , m_activities(new KActivities::Consumer(this)) { connect(m_activities, &KActivities::Consumer::currentActivityChanged, this, [&] (const QString ¤tActivity) { qCDebug(KICKER_DEBUG) << "Activity just got changed to" << currentActivity; Q_UNUSED(currentActivity); if (d) { auto clientId = d->m_clientId; initForClient(clientId); } }); } KAStatsFavoritesModel::~KAStatsFavoritesModel() { delete d; } void KAStatsFavoritesModel::initForClient(const QString &clientId) { qCDebug(KICKER_DEBUG) << "initForClient" << clientId; setSourceModel(nullptr); delete d; d = new Private( this, clientId ); setSourceModel(d); } QString KAStatsFavoritesModel::description() const { return i18n("Favorites"); } bool KAStatsFavoritesModel::trigger(int row, const QString &actionId, const QVariant &argument) { return d && d->trigger(row, actionId, argument); } bool KAStatsFavoritesModel::enabled() const { return m_enabled; } int KAStatsFavoritesModel::maxFavorites() const { return m_maxFavorites; } void KAStatsFavoritesModel::setMaxFavorites(int max) { Q_UNUSED(max); } void KAStatsFavoritesModel::setEnabled(bool enable) { if (m_enabled != enable) { m_enabled = enable; emit enabledChanged(); } } QStringList KAStatsFavoritesModel::favorites() const { qWarning() << "KAStatsFavoritesModel::favorites returns nothing, it is here just to keep the API backwards-compatible"; return QStringList(); } void KAStatsFavoritesModel::setFavorites(const QStringList& favorites) { Q_UNUSED(favorites); qWarning() << "KAStatsFavoritesModel::setFavorites is ignored"; } bool KAStatsFavoritesModel::isFavorite(const QString &id) const { return d && d->m_itemEntries.contains(id); } void KAStatsFavoritesModel::portOldFavorites(const QStringList &ids) { if (!d) return; qCDebug(KICKER_DEBUG) << "portOldFavorites" << ids; const QString activityId = QStringLiteral(":global"); std::for_each(ids.begin(), ids.end(), [&] (const QString &id) { addFavoriteTo(id, activityId); }); // Resetting the model auto clientId = d->m_clientId; setSourceModel(nullptr); delete d; d = nullptr; qCDebug(KICKER_DEBUG) << "Save ordering (from portOldFavorites) -->"; Private::saveOrdering(ids, clientId, m_activities->currentActivity()); QTimer::singleShot(500, std::bind(&KAStatsFavoritesModel::initForClient, this, clientId)); } void KAStatsFavoritesModel::addFavorite(const QString &id, int index) { qCDebug(KICKER_DEBUG) << "addFavorite" << id << index << " -->"; addFavoriteTo(id, QStringLiteral(":global"), index); } void KAStatsFavoritesModel::removeFavorite(const QString &id) { qCDebug(KICKER_DEBUG) << "removeFavorite" << id << " -->"; removeFavoriteFrom(id, QStringLiteral(":any")); } void KAStatsFavoritesModel::addFavoriteTo(const QString &id, const QString &activityId, int index) { qCDebug(KICKER_DEBUG) << "addFavoriteTo" << id << activityId << index << " -->"; addFavoriteTo(id, Activity(activityId), index); } void KAStatsFavoritesModel::removeFavoriteFrom(const QString &id, const QString &activityId) { qCDebug(KICKER_DEBUG) << "removeFavoriteFrom" << id << activityId << " -->"; removeFavoriteFrom(id, Activity(activityId)); } void KAStatsFavoritesModel::addFavoriteTo(const QString &id, const Activity &activity, int index) { if (!d || id.isEmpty()) return; Q_ASSERT(!activity.values.isEmpty()); setDropPlaceholderIndex(-1); QStringList matchers { d->m_activities.currentActivity(), QStringLiteral(":global"), QStringLiteral(":current") }; if (std::find_first_of(activity.values.cbegin(), activity.values.cend(), matchers.cbegin(), matchers.cend()) != activity.values.cend()) { d->addResult(id, index); } const auto url = d->normalizedId(id).value(); qCDebug(KICKER_DEBUG) << "addFavoriteTo" << id << activity << index << url << " (actual)"; if (url.isEmpty()) return; d->m_watcher.linkToActivity(QUrl(url), activity, Agent(agentForUrl(url))); } void KAStatsFavoritesModel::removeFavoriteFrom(const QString &id, const Activity &activity) { if (!d || id.isEmpty()) return; const auto url = d->normalizedId(id).value(); Q_ASSERT(!activity.values.isEmpty()); qCDebug(KICKER_DEBUG) << "addFavoriteTo" << id << activity << url << " (actual)"; if (url.isEmpty()) return; d->m_watcher.unlinkFromActivity(QUrl(url), activity, Agent(agentForUrl(url))); } void KAStatsFavoritesModel::setFavoriteOn(const QString &id, const QString &activityId) { if (!d || id.isEmpty()) return; const auto url = d->normalizedId(id).value(); qCDebug(KICKER_DEBUG) << "setFavoriteOn" << id << activityId << url << " (actual)"; qCDebug(KICKER_DEBUG) << "%%%%%%%%%%% Activity is" << activityId; if (activityId.isEmpty() || activityId == QLatin1String(":any") || activityId == QLatin1String(":global") || activityId == m_activities->currentActivity()) { d->m_ignoredItems << url; } d->m_watcher.unlinkFromActivity(QUrl(url), Activity::any(), Agent(agentForUrl(url))); d->m_watcher.linkToActivity(QUrl(url), activityId, Agent(agentForUrl(url))); } void KAStatsFavoritesModel::moveRow(int from, int to) { if (!d) return; d->move(from, to); } AbstractModel *KAStatsFavoritesModel::favoritesModel() { return this; } void KAStatsFavoritesModel::refresh() { } QObject *KAStatsFavoritesModel::activities() const { return m_activities; } QString KAStatsFavoritesModel::activityNameForId(const QString &activityId) const { // It is safe to use a short-lived object here, // we are always synced with KAMD in plasma KActivities::Info info(activityId); return info.name(); } QStringList KAStatsFavoritesModel::linkedActivitiesFor(const QString &id) const { if (!d) { qCDebug(KICKER_DEBUG) << "Linked for" << id << "is empty, no Private instance"; return {}; } auto url = d->normalizedId(id).value(); if (url.startsWith(QLatin1String("file:"))) { url = QUrl(url).toLocalFile(); } if (url.isEmpty()) { qCDebug(KICKER_DEBUG) << "The url for" << id << "is empty"; return {}; } auto query = LinkedResources | Agent { AGENT_APPLICATIONS, AGENT_CONTACTS, AGENT_DOCUMENTS } | Type::any() | Activity::any() | Url(url) | Limit::all(); ResultSet results(query); for (const auto &result: results) { qCDebug(KICKER_DEBUG) << "Returning" << result.linkedActivities() << "for" << id << url; return result.linkedActivities(); } qCDebug(KICKER_DEBUG) << "Returning empty list of activities for" << id << url; return {}; } diff --git a/containments/desktop/plugins/folder/foldermodel.cpp b/containments/desktop/plugins/folder/foldermodel.cpp index 9acc59357..df0f85f71 100644 --- a/containments/desktop/plugins/folder/foldermodel.cpp +++ b/containments/desktop/plugins/folder/foldermodel.cpp @@ -1,2107 +1,2107 @@ /*************************************************************************** * Copyright (C) 2006 David Faure * * Copyright (C) 2008 Fredrik Höglund * * Copyright (C) 2008 Rafael Fernández López * * Copyright (C) 2011 Marco Martin * * Copyright (C) 2014 by Eike Hein * * * * 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. * * * * This program 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 General Public License for more details. * * * * You should have received a copy of the GNU General Public License * * along with this program; if not, write to the * * Free Software Foundation, Inc., * * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA . * ***************************************************************************/ #include "foldermodel.h" #include "itemviewadapter.h" #include "positioner.h" #include "screenmapper.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include Q_LOGGING_CATEGORY(FOLDERMODEL, "plasma.containments.desktop.folder.foldermodel") DirLister::DirLister(QObject *parent) : KDirLister(parent) { } DirLister:: ~DirLister() { } void DirLister::handleError(KIO::Job *job) { if (!autoErrorHandlingEnabled()) { emit error(job->errorString()); return; } KDirLister::handleError(job); } FolderModel::FolderModel(QObject *parent) : QSortFilterProxyModel(parent), m_dirWatch(nullptr), m_dragInProgress(false), m_urlChangedWhileDragging(false), m_dropTargetPositionsCleanup(new QTimer(this)), m_previewGenerator(nullptr), m_viewAdapter(nullptr), m_actionCollection(this), m_newMenu(nullptr), m_fileItemActions(nullptr), m_usedByContainment(false), m_locked(true), m_sortMode(0), m_sortDesc(false), m_sortDirsFirst(true), m_parseDesktopFiles(false), m_previews(false), m_filterMode(NoFilter), m_filterPatternMatchAll(true), m_screenMapper(ScreenMapper::instance()), m_complete(false), m_screenUsed(false) { //needed to pass the job around with qml qmlRegisterType(); DirLister *dirLister = new DirLister(this); dirLister->setDelayedMimeTypes(true); dirLister->setAutoErrorHandlingEnabled(false, nullptr); connect(dirLister, &DirLister::error, this, &FolderModel::dirListFailed); connect(dirLister, &KCoreDirLister::itemsDeleted, this, &FolderModel::evictFromIsDirCache); connect(dirLister, &KCoreDirLister::started, this, std::bind(&FolderModel::setStatus, this, Status::Listing)); void (KCoreDirLister::*myCompletedSignal)() = &KCoreDirLister::completed; QObject::connect(dirLister, myCompletedSignal, this, [this] { setStatus(Status::Ready); emit listingCompleted(); }); void (KCoreDirLister::*myCanceledSignal)() = &KCoreDirLister::canceled; QObject::connect(dirLister, myCanceledSignal, this, [this] { setStatus(Status::Canceled); emit listingCanceled(); }); m_dirModel = new KDirModel(this); m_dirModel->setDirLister(dirLister); m_dirModel->setDropsAllowed(KDirModel::DropOnDirectory | KDirModel::DropOnLocalExecutable); // If we have dropped items queued for moving, go unsorted now. connect(this, &QAbstractItemModel::rowsAboutToBeInserted, this, [this]() { if (!m_dropTargetPositions.isEmpty()) { setSortMode(-1); } }); // Position dropped items at the desired target position. connect(this, &QAbstractItemModel::rowsInserted, this, [this](const QModelIndex &parent, int first, int last) { for (int i = first; i <= last; ++i) { const auto idx = index(i, 0, parent); const auto url = itemForIndex(idx).url(); auto it = m_dropTargetPositions.find(url.fileName()); if (it != m_dropTargetPositions.end()) { const auto pos = it.value(); m_dropTargetPositions.erase(it); emit move(pos.x(), pos.y(), {url}); } } }); /* * Dropped files may not actually show up as new files, e.g. when we overwrite * an existing file. Or files that fail to be listed by the dirLister, or... * To ensure we don't grow the map indefinitely, clean it up periodically. * The cleanup timer is (re)started whenever we modify the map. We use a quite * high interval of 10s. This should ensure, that we don't accidentally wipe * the mapping when we actually still want to use it. Since the time between * adding an entry in the map and it showing up in the model should be * small, this should rarely, if ever happen. */ m_dropTargetPositionsCleanup->setInterval(10000); m_dropTargetPositionsCleanup->setSingleShot(true); connect(m_dropTargetPositionsCleanup, &QTimer::timeout, this, [this]() { if (!m_dropTargetPositions.isEmpty()) { qCDebug(FOLDERMODEL) << "clearing drop target positions after timeout:" << m_dropTargetPositions; m_dropTargetPositions.clear(); } }); m_selectionModel = new QItemSelectionModel(this, this); connect(m_selectionModel, &QItemSelectionModel::selectionChanged, this, &FolderModel::selectionChanged); setSourceModel(m_dirModel); setSortLocaleAware(true); setFilterCaseSensitivity(Qt::CaseInsensitive); setDynamicSortFilter(true); sort(m_sortMode, m_sortDesc ? Qt::DescendingOrder : Qt::AscendingOrder); createActions(); } FolderModel::~FolderModel() { if (m_usedByContainment) { // disconnect so we don't handle signals from the screen mapper when // removeScreen is called m_screenMapper->disconnect(this); m_screenMapper->removeScreen(m_screen, resolvedUrl()); } } QHash< int, QByteArray > FolderModel::roleNames() const { return staticRoleNames(); } QHash< int, QByteArray > FolderModel::staticRoleNames() { QHash roleNames; roleNames[Qt::DisplayRole] = "display"; roleNames[Qt::DecorationRole] = "decoration"; roleNames[BlankRole] = "blank"; roleNames[OverlaysRole] = "overlays"; roleNames[SelectedRole] = "selected"; roleNames[IsDirRole] = "isDir"; roleNames[IsLinkRole] = "isLink"; roleNames[IsHiddenRole] = "isHidden"; roleNames[UrlRole] = "url"; roleNames[LinkDestinationUrl] = "linkDestinationUrl"; roleNames[SizeRole] = "size"; roleNames[TypeRole] = "type"; return roleNames; } void FolderModel::classBegin() { } void FolderModel::componentComplete() { m_complete = true; invalidate(); } void FolderModel::invalidateIfComplete() { if (!m_complete) { return; } invalidate(); } void FolderModel::invalidateFilterIfComplete() { if (!m_complete) { return; } invalidateFilter(); } void FolderModel::newFileMenuItemCreated(const QUrl &url) { if (m_usedByContainment && !m_screenMapper->sharedDesktops()) { m_screenMapper->addMapping(url, m_screen, ScreenMapper::DelayedSignal); m_dropTargetPositions.insert(url.fileName(), m_menuPosition); m_menuPosition = {}; m_dropTargetPositionsCleanup->start(); } } QString FolderModel::url() const { return m_url; } void FolderModel::setUrl(const QString& url) { const QUrl &resolvedNewUrl = resolve(url); if (url == m_url) { m_dirModel->dirLister()->updateDirectory(resolvedNewUrl); return; } const auto oldUrl = resolvedUrl(); beginResetModel(); m_url = url; m_isDirCache.clear(); m_dirModel->dirLister()->openUrl(resolvedNewUrl); clearDragImages(); m_dragIndexes.clear(); endResetModel(); emit urlChanged(); emit resolvedUrlChanged(); m_errorString.clear(); emit errorStringChanged(); if (m_dirWatch) { delete m_dirWatch; m_dirWatch = nullptr; } if (resolvedNewUrl.isValid()) { m_dirWatch = new KDirWatch(this); connect(m_dirWatch, &KDirWatch::created, this, &FolderModel::iconNameChanged); connect(m_dirWatch, &KDirWatch::dirty, this, &FolderModel::iconNameChanged); m_dirWatch->addFile(resolvedNewUrl.toLocalFile() + QLatin1String("/.directory")); } if (m_dragInProgress) { m_urlChangedWhileDragging = true; } emit iconNameChanged(); if (m_usedByContainment && !m_screenMapper->sharedDesktops()) { m_screenMapper->removeScreen(m_screen, oldUrl); m_screenMapper->addScreen(m_screen, resolvedUrl()); } } QUrl FolderModel::resolvedUrl() const { return m_dirModel->dirLister()->url(); } QUrl FolderModel::resolve(const QString& url) { QUrl resolvedUrl; if (url.startsWith(QLatin1Char('~'))) { resolvedUrl = QUrl::fromLocalFile(KShell::tildeExpand(url)); } else { resolvedUrl = QUrl::fromUserInput(url); } return resolvedUrl; } QString FolderModel::iconName() const { const KFileItem rootItem(m_dirModel->dirLister()->url()); if (!rootItem.isFinalIconKnown()) { rootItem.determineMimeType(); } return rootItem.iconName(); } FolderModel::Status FolderModel::status() const { return m_status; } void FolderModel::setStatus(Status status) { if (m_status != status) { m_status = status; emit statusChanged(); } } QString FolderModel::errorString() const { return m_errorString; } bool FolderModel::dragging() const { return m_dragInProgress; } bool FolderModel::usedByContainment() const { return m_usedByContainment; } void FolderModel::setUsedByContainment(bool used) { if (m_usedByContainment != used) { m_usedByContainment = used; QAction *action = m_actionCollection.action(QStringLiteral("refresh")); if (action) { action->setText(m_usedByContainment ? i18n("&Refresh Desktop") : i18n("&Refresh View")); action->setIcon(m_usedByContainment ? QIcon::fromTheme(QStringLiteral("user-desktop")) : QIcon::fromTheme(QStringLiteral("view-refresh"))); } m_screenMapper->disconnect(this); connect(m_screenMapper, &ScreenMapper::screensChanged, this, &FolderModel::invalidateFilterIfComplete); connect(m_screenMapper, &ScreenMapper::screenMappingChanged, this, &FolderModel::invalidateFilterIfComplete); emit usedByContainmentChanged(); } } bool FolderModel::locked() const { return m_locked; } void FolderModel::setLocked(bool locked) { if (m_locked != locked) { m_locked = locked; emit lockedChanged(); } } void FolderModel::dirListFailed(const QString& error) { m_errorString = error; emit errorStringChanged(); } int FolderModel::sortMode() const { return m_sortMode; } void FolderModel::setSortMode(int mode) { if (m_sortMode != mode) { m_sortMode = mode; if (mode == -1 /* Unsorted */) { setDynamicSortFilter(false); } else { invalidateIfComplete(); sort(m_sortMode, m_sortDesc ? Qt::DescendingOrder : Qt::AscendingOrder); setDynamicSortFilter(true); } emit sortModeChanged(); } } bool FolderModel::sortDesc() const { return m_sortDesc; } void FolderModel::setSortDesc(bool desc) { if (m_sortDesc != desc) { m_sortDesc = desc; if (m_sortMode != -1 /* Unsorted */) { invalidateIfComplete(); sort(m_sortMode, m_sortDesc ? Qt::DescendingOrder : Qt::AscendingOrder); } emit sortDescChanged(); } } bool FolderModel::sortDirsFirst() const { return m_sortDirsFirst; } void FolderModel::setSortDirsFirst(bool enable) { if (m_sortDirsFirst != enable) { m_sortDirsFirst = enable; if (m_sortMode != -1 /* Unsorted */) { invalidateIfComplete(); sort(m_sortMode, m_sortDesc ? Qt::DescendingOrder : Qt::AscendingOrder); } emit sortDirsFirstChanged(); } } bool FolderModel::parseDesktopFiles() const { return m_parseDesktopFiles; } void FolderModel::setParseDesktopFiles(bool enable) { if (m_parseDesktopFiles != enable) { m_parseDesktopFiles = enable; emit parseDesktopFilesChanged(); } } QObject* FolderModel::viewAdapter() const { return m_viewAdapter; } void FolderModel::setViewAdapter(QObject* adapter) { if (m_viewAdapter != adapter) { KAbstractViewAdapter *abstractViewAdapter = dynamic_cast(adapter); m_viewAdapter = abstractViewAdapter; if (m_viewAdapter && !m_previewGenerator) { m_previewGenerator = new KFilePreviewGenerator(abstractViewAdapter, this); m_previewGenerator->setPreviewShown(m_previews); m_previewGenerator->setEnabledPlugins(m_effectivePreviewPlugins); } emit viewAdapterChanged(); } } bool FolderModel::previews() const { return m_previews; } void FolderModel::setPreviews(bool previews) { if (m_previews != previews) { m_previews = previews; if (m_previewGenerator) { m_previewGenerator->setPreviewShown(m_previews); } emit previewsChanged(); } } QStringList FolderModel::previewPlugins() const { return m_previewPlugins; } void FolderModel::setPreviewPlugins(const QStringList& previewPlugins) { QStringList effectivePlugins = previewPlugins; if (effectivePlugins.isEmpty()) { effectivePlugins = KIO::PreviewJob::defaultPlugins(); } if (m_effectivePreviewPlugins != effectivePlugins) { m_effectivePreviewPlugins = effectivePlugins; if (m_previewGenerator) { m_previewGenerator->setPreviewShown(false); m_previewGenerator->setEnabledPlugins(m_effectivePreviewPlugins); m_previewGenerator->setPreviewShown(true); } } if (m_previewPlugins != previewPlugins) { m_previewPlugins = previewPlugins; emit previewPluginsChanged(); } } int FolderModel::filterMode() const { return m_filterMode; } void FolderModel::setFilterMode(int filterMode) { if (m_filterMode != (FilterMode)filterMode) { m_filterMode = (FilterMode)filterMode; invalidateFilterIfComplete(); emit filterModeChanged(); } } QString FolderModel::filterPattern() const { return m_filterPattern; } void FolderModel::setFilterPattern(const QString &pattern) { if (m_filterPattern == pattern) { return; } m_filterPattern = pattern; m_filterPatternMatchAll = (pattern == QLatin1String("*")); const QStringList patterns = pattern.split(QLatin1Char(' ')); m_regExps.clear(); m_regExps.reserve(patterns.count()); foreach (const QString &pattern, patterns) { QRegExp rx(pattern); rx.setPatternSyntax(QRegExp::Wildcard); rx.setCaseSensitivity(Qt::CaseInsensitive); m_regExps.append(rx); } invalidateFilterIfComplete(); emit filterPatternChanged(); } QStringList FolderModel::filterMimeTypes() const { return m_mimeSet.toList(); } void FolderModel::setFilterMimeTypes(const QStringList &mimeList) { const QSet &set = QSet::fromList(mimeList); if (m_mimeSet != set) { m_mimeSet = set; invalidateFilterIfComplete(); emit filterMimeTypesChanged(); } } void FolderModel::setScreen(int screen) { m_screenUsed = (screen != -1); if (!m_screenUsed || m_screen == screen) return; m_screen = screen; if (m_usedByContainment && !m_screenMapper->sharedDesktops()) { m_screenMapper->addScreen(screen, resolvedUrl()); } emit screenChanged(); } bool FolderModel::eventFilter(QObject *watched, QEvent *event) { Q_UNUSED(watched) // Catching Shift modifier usage on open context menus to swap the // Trash/Delete actions. if (event->type() == QEvent::KeyPress) { QKeyEvent *keyEvent = static_cast(event); if (keyEvent->key() == Qt::Key_Shift) { m_actionCollection.action(QStringLiteral("trash"))->setVisible(false); m_actionCollection.action(QStringLiteral("del"))->setVisible(true); } } else if (event->type() == QEvent::KeyRelease) { QKeyEvent *keyEvent = static_cast(event); if (keyEvent->key() == Qt::Key_Shift) { m_actionCollection.action(QStringLiteral("trash"))->setVisible(true); m_actionCollection.action(QStringLiteral("del"))->setVisible(false); } } return false; } KFileItem FolderModel::rootItem() const { return m_dirModel->dirLister()->rootItem(); } void FolderModel::up() { const QUrl &up = KIO::upUrl(resolvedUrl()); if (up.isValid()) { setUrl(up.toString()); } } void FolderModel::cd(int row) { if (row < 0) { return; } const QModelIndex idx = index(row, 0); bool isDir = data(idx, IsDirRole).toBool(); if (isDir) { const KFileItem item = itemForIndex(idx); if (m_parseDesktopFiles && item.isDesktopFile()) { const KDesktopFile file(item.targetUrl().path()); if (file.hasLinkType()) { setUrl(file.readUrl()); } } else { setUrl(item.targetUrl().toString()); } } } void FolderModel::run(int row) { if (row < 0) { return; } KFileItem item = itemForIndex(index(row, 0)); QUrl url(item.targetUrl()); // FIXME TODO: This can go once we depend on a KIO w/ fe1f50caaf2. if (url.scheme().isEmpty()) { url.setScheme(QStringLiteral("file")); } KRun *run = new KRun(url, nullptr); // On desktop:/ we want to be able to run .desktop files right away, // otherwise ask for security reasons. We also don't use the targetUrl() // from above since we don't want the resolved /home/foo/Desktop URL. run->setShowScriptExecutionPrompt(item.url().scheme() != QLatin1String("desktop") || item.url().adjusted(QUrl::RemoveFilename).path() != QLatin1String("/")); } void FolderModel::runSelected() { if (!m_selectionModel->hasSelection()) { return; } if (m_selectionModel->selectedIndexes().count() == 1) { run(m_selectionModel->selectedIndexes().constFirst().row()); return; } KFileItemActions fileItemActions(this); KFileItemList items; foreach (const QModelIndex &index, m_selectionModel->selectedIndexes()) { // Skip over directories. if (!index.data(IsDirRole).toBool()) { items << itemForIndex(index); } } fileItemActions.runPreferredApplications(items, QString()); } void FolderModel::rename(int row, const QString& name) { if (row < 0) { return; } QModelIndex idx = index(row, 0); m_dirModel->setData(mapToSource(idx), name, Qt::EditRole); } int FolderModel::fileExtensionBoundary(int row) { const QModelIndex idx = index(row, 0); const QString &name = data(idx, Qt::DisplayRole).toString(); int boundary = name.length(); if (data(idx, IsDirRole).toBool()) { return boundary; } QMimeDatabase db; const QString &ext = db.suffixForFileName(name); if (ext.isEmpty()) { boundary = name.lastIndexOf(QLatin1Char('.')); if (boundary < 1) { boundary = name.length(); } } else { boundary -= ext.length() + 1; } return boundary; } bool FolderModel::hasSelection() const { return m_selectionModel->hasSelection(); } bool FolderModel::isSelected(int row) { if (row < 0) { return false; } return m_selectionModel->isSelected(index(row, 0)); } void FolderModel::setSelected(int row) { if (row < 0) { return; } m_selectionModel->select(index(row, 0), QItemSelectionModel::Select); } void FolderModel::toggleSelected(int row) { if (row < 0) { return; } m_selectionModel->select(index(row, 0), QItemSelectionModel::Toggle); } void FolderModel::setRangeSelected(int anchor, int to) { if (anchor < 0 || to < 0) { return; } QItemSelection selection(index(anchor, 0), index(to, 0)); m_selectionModel->select(selection, QItemSelectionModel::ClearAndSelect); } void FolderModel::updateSelection(const QVariantList &rows, bool toggle) { QItemSelection newSelection; int iRow = -1; foreach (const QVariant &row, rows) { iRow = row.toInt(); if (iRow < 0) { return; } const QModelIndex &idx = index(iRow, 0); newSelection.select(idx, idx); } if (toggle) { QItemSelection pinnedSelection = m_pinnedSelection; pinnedSelection.merge(newSelection, QItemSelectionModel::Toggle); m_selectionModel->select(pinnedSelection, QItemSelectionModel::ClearAndSelect); } else { m_selectionModel->select(newSelection, QItemSelectionModel::ClearAndSelect); } } void FolderModel::clearSelection() { if (m_selectionModel->hasSelection()) { m_selectionModel->clear(); } } void FolderModel::pinSelection() { m_pinnedSelection = m_selectionModel->selection(); } void FolderModel::unpinSelection() { m_pinnedSelection = QItemSelection(); } void FolderModel::addItemDragImage(int row, int x, int y, int width, int height, const QVariant &image) { if (row < 0) { return; } delete m_dragImages.take(row); DragImage *dragImage = new DragImage(); dragImage->row = row; dragImage->rect = QRect(x, y, width, height); dragImage->image = image.value(); dragImage->blank = false; m_dragImages.insert(row, dragImage); } void FolderModel::clearDragImages() { qDeleteAll(m_dragImages); m_dragImages.clear(); } void FolderModel::setDragHotSpotScrollOffset(int x, int y) { m_dragHotSpotScrollOffset.setX(x); m_dragHotSpotScrollOffset.setY(y); } QPoint FolderModel::dragCursorOffset(int row) { DragImage *image = m_dragImages.value(row); if (!image) { return QPoint(0, 0); } return image->cursorOffset; } void FolderModel::addDragImage(QDrag *drag, int x, int y) { if (!drag || m_dragImages.isEmpty()) { return; } QRegion region; foreach (DragImage *image, m_dragImages) { image->blank = isBlank(image->row); image->rect.translate(-m_dragHotSpotScrollOffset.x(), -m_dragHotSpotScrollOffset.y()); if (!image->blank && !image->image.isNull()) { region = region.united(image->rect); } } QRect rect = region.boundingRect(); QPoint offset = rect.topLeft(); rect.translate(-offset.x(), -offset.y()); QImage dragImage(rect.size(), QImage::Format_RGBA8888); dragImage.fill(Qt::transparent); QPainter painter(&dragImage); QPoint pos; foreach (DragImage *image, m_dragImages) { if (!image->blank && !image->image.isNull()) { pos = image->rect.translated(-offset.x(), -offset.y()).topLeft(); image->cursorOffset.setX(pos.x() - (x - offset.x())); image->cursorOffset.setY(pos.y() - (y - offset.y())); painter.drawImage(pos, image->image); } // FIXME HACK: Operate on copy. image->rect.translate(m_dragHotSpotScrollOffset.x(), m_dragHotSpotScrollOffset.y()); } drag->setPixmap(QPixmap::fromImage(dragImage)); drag->setHotSpot(QPoint(x - offset.x(), y - offset.y())); } void FolderModel::dragSelected(int x, int y) { if (m_dragInProgress) { return; } m_dragInProgress = true; emit draggingChanged(); m_urlChangedWhileDragging = false; // Avoid starting a drag synchronously in a mouse handler or interferes with // child event filtering in parent items (and thus e.g. press-and-hold hand- // ling in a containment). QMetaObject::invokeMethod(this, "dragSelectedInternal", Qt::QueuedConnection, Q_ARG(int, x), Q_ARG(int, y)); } void FolderModel::dragSelectedInternal(int x, int y) { if (!m_viewAdapter || !m_selectionModel->hasSelection()) { m_dragInProgress = false; emit draggingChanged(); return; } ItemViewAdapter *adapter = qobject_cast(m_viewAdapter); QQuickItem *item = qobject_cast(adapter->adapterView()); QDrag *drag = new QDrag(item); addDragImage(drag, x, y); m_dragIndexes = m_selectionModel->selectedIndexes(); std::sort(m_dragIndexes.begin(), m_dragIndexes.end()); // TODO: Optimize to emit contiguous groups. emit dataChanged(m_dragIndexes.first(), m_dragIndexes.last(), QVector() << BlankRole); QModelIndexList sourceDragIndexes; sourceDragIndexes.reserve(m_dragIndexes.count()); foreach (const QModelIndex &index, m_dragIndexes) { sourceDragIndexes.append(mapToSource(index)); } drag->setMimeData(m_dirModel->mimeData(sourceDragIndexes)); // Due to spring-loading (aka auto-expand), the URL might change // while the drag is in-flight - in that case we don't want to // unnecessarily emit dataChanged() for (possibly invalid) indices // after it ends. const QUrl currentUrl(m_dirModel->dirLister()->url()); item->grabMouse(); drag->exec(supportedDragActions()); item->ungrabMouse(); m_dragInProgress = false; emit draggingChanged(); m_urlChangedWhileDragging = false; if (m_dirModel->dirLister()->url() == currentUrl) { const QModelIndex first(m_dragIndexes.first()); const QModelIndex last(m_dragIndexes.last()); m_dragIndexes.clear(); // TODO: Optimize to emit contiguous groups. emit dataChanged(first, last, QVector() << BlankRole); } } static bool isDropBetweenSharedViews(const QList &urls, const QUrl &folderUrl) { for (const auto &url : urls) { if (folderUrl.adjusted(QUrl::StripTrailingSlash) != url.adjusted(QUrl::RemoveFilename | QUrl::StripTrailingSlash)) { return false; } } return true; } void FolderModel::drop(QQuickItem *target, QObject* dropEvent, int row) { QMimeData *mimeData = qobject_cast(dropEvent->property("mimeData").value()); if (!mimeData) { return; } QModelIndex idx; KFileItem item; if (row > -1 && row < rowCount()) { idx = index(row, 0); item = itemForIndex(idx); } QUrl dropTargetUrl; // So we get to run mostLocalUrl() over the current URL. if (item.isNull()) { item = rootItem(); } if (item.isNull()) { dropTargetUrl = m_dirModel->dirLister()->url(); } else if (m_parseDesktopFiles && item.isDesktopFile()) { const KDesktopFile file(item.targetUrl().path()); if (file.hasLinkType()) { dropTargetUrl = QUrl(file.readUrl()); } else { dropTargetUrl = item.mostLocalUrl(); } } else { dropTargetUrl = item.mostLocalUrl(); } auto dropTargetFolderUrl = dropTargetUrl; - if (dropTargetFolderUrl.fileName() == QLatin1String(".")) { + if (dropTargetFolderUrl.fileName() == QLatin1Char('.')) { // the target URL for desktop:/ is e.g. 'file://home/user/Desktop/.' dropTargetFolderUrl = dropTargetFolderUrl.adjusted(QUrl::RemoveFilename); } // use dropTargetUrl to resolve desktop:/ to the actual file location which is also used by the mime data /* QMimeData operates on local URLs, but the dir lister and thus screen mapper and positioner may * use a fancy scheme like desktop:/ instead. Ensure we always use the latter to properly map URLs, * i.e. go from file:///home/user/Desktop/file to desktop:/file */ auto mappableUrl = [this, dropTargetFolderUrl](const QUrl &url) -> QUrl { if (dropTargetFolderUrl != m_dirModel->dirLister()->url()) { QString mappedUrl = url.toString(); const auto local = dropTargetFolderUrl.toString(); const auto internal = m_dirModel->dirLister()->url().toString(); if (mappedUrl.startsWith(local)) { mappedUrl.replace(0, local.size(), internal); } return ScreenMapper::stringToUrl(mappedUrl); } return url; }; const int x = dropEvent->property("x").toInt(); const int y = dropEvent->property("y").toInt(); const QPoint dropPos = {x, y}; if (m_dragInProgress && row == -1 && !m_urlChangedWhileDragging) { if (m_locked || mimeData->urls().isEmpty()) { return; } setSortMode(-1); for (const auto &url : mimeData->urls()) { m_dropTargetPositions.insert(url.fileName(), dropPos); m_screenMapper->addMapping(mappableUrl(url), m_screen, ScreenMapper::DelayedSignal); m_screenMapper->removeItemFromDisabledScreen(mappableUrl(url)); } emit move(x, y, mimeData->urls()); return; } if (mimeData->hasFormat(QStringLiteral("application/x-kde-ark-dndextract-service")) && mimeData->hasFormat(QStringLiteral("application/x-kde-ark-dndextract-path"))) { const QString remoteDBusClient = mimeData->data(QStringLiteral("application/x-kde-ark-dndextract-service")); const QString remoteDBusPath = mimeData->data(QStringLiteral("application/x-kde-ark-dndextract-path")); QDBusMessage message = QDBusMessage::createMethodCall(remoteDBusClient, remoteDBusPath, QStringLiteral("org.kde.ark.DndExtract"), QStringLiteral("extractSelectedFilesTo")); message.setArguments({dropTargetUrl.toDisplayString(QUrl::PreferLocalFile)}); QDBusConnection::sessionBus().call(message, QDBus::NoBlock); return; } if (idx.isValid() && !(flags(idx) & Qt::ItemIsDropEnabled)) { return; } // Catch drops from a Task Manager and convert to usable URL. if (!mimeData->hasUrls() && mimeData->hasFormat(QStringLiteral("text/x-orgkdeplasmataskmanager_taskurl"))) { QList urls = {QUrl(QString::fromUtf8(mimeData->data(QStringLiteral("text/x-orgkdeplasmataskmanager_taskurl"))))}; mimeData->setUrls(urls); } if (m_usedByContainment && !m_screenMapper->sharedDesktops()) { if (isDropBetweenSharedViews(mimeData->urls(), dropTargetFolderUrl)) { setSortMode(-1); for (const auto &url : mimeData->urls()) { m_dropTargetPositions.insert(url.fileName(), dropPos); m_screenMapper->addMapping(mappableUrl(url), m_screen, ScreenMapper::DelayedSignal); m_screenMapper->removeItemFromDisabledScreen(mappableUrl(url)); } m_dropTargetPositionsCleanup->start(); return; } } Qt::DropAction proposedAction((Qt::DropAction)dropEvent->property("proposedAction").toInt()); Qt::DropActions possibleActions(dropEvent->property("possibleActions").toInt()); Qt::MouseButtons buttons(dropEvent->property("buttons").toInt()); Qt::KeyboardModifiers modifiers(dropEvent->property("modifiers").toInt()); auto pos = target->mapToScene(dropPos).toPoint(); pos = target->window()->mapToGlobal(pos); QDropEvent ev(pos, possibleActions, mimeData, buttons, modifiers); ev.setDropAction(proposedAction); KIO::DropJob *dropJob = KIO::drop(&ev, dropTargetUrl); dropJob->uiDelegate()->setAutoErrorHandlingEnabled(true); // The QMimeData we extract from the DropArea's drop event is deleted as soon as this method // ends but we need to keep a copy for when popupMenuAboutToShow fires. QMimeData *mimeCopy = new QMimeData(); for (const QString &format : mimeData->formats()) { mimeCopy->setData(format, mimeData->data(format)); } connect(dropJob, &KIO::DropJob::popupMenuAboutToShow, this, [this, mimeCopy, x, y, dropJob](const KFileItemListProperties &) { emit popupMenuAboutToShow(dropJob, mimeCopy, x, y); mimeCopy->deleteLater(); }); /* * Position files that come from a drag'n'drop event at the drop event * target position. To do so, we first listen to copy job to figure out * the target URL. Then we store the position of this drop event in the * hash and eventually trigger a move request when we get notified about * the new file event from the source model. */ connect(dropJob, &KIO::DropJob::copyJobStarted, this, [this, dropPos, dropTargetUrl](KIO::CopyJob* copyJob) { auto map = [this, dropPos, dropTargetUrl](const QUrl &targetUrl) { m_dropTargetPositions.insert(targetUrl.fileName(), dropPos); m_dropTargetPositionsCleanup->start(); if (m_usedByContainment && !m_screenMapper->sharedDesktops()) { // assign a screen for the item before the copy is actually done, so // filterAcceptsRow doesn't assign the default screen to it QUrl url = resolvedUrl(); // if the folderview's folder is a standard path, just use the targetUrl for mapping if (targetUrl.toString().startsWith(url.toString())) { m_screenMapper->addMapping(targetUrl, m_screen, ScreenMapper::DelayedSignal); } else if (targetUrl.toString().startsWith(dropTargetUrl.toString())) { // if the folderview's folder is a special path, like desktop:// , we need to convert // the targetUrl file:// path to a desktop:/ path for mapping auto destPath = dropTargetUrl.path(); auto filePath = targetUrl.path(); if (filePath.startsWith(destPath)) { url.setPath(filePath.remove(0, destPath.length())); m_screenMapper->addMapping(url, m_screen, ScreenMapper::DelayedSignal); } } } }; // remember drop target position for target URL and forget about the source URL connect(copyJob, &KIO::CopyJob::copyingDone, this, [this, map](KIO::Job *, const QUrl &, const QUrl &targetUrl, const QDateTime &, bool, bool) { map(targetUrl); }); connect(copyJob, &KIO::CopyJob::copyingLinkDone, this, [this, map](KIO::Job *, const QUrl &, const QString &, const QUrl &targetUrl) { map(targetUrl); }); }); } void FolderModel::dropCwd(QObject* dropEvent) { QMimeData *mimeData = qobject_cast(dropEvent->property("mimeData").value()); if (!mimeData) { return; } if (mimeData->hasFormat(QStringLiteral("application/x-kde-ark-dndextract-service")) && mimeData->hasFormat(QStringLiteral("application/x-kde-ark-dndextract-path"))) { const QString remoteDBusClient = mimeData->data(QStringLiteral("application/x-kde-ark-dndextract-service")); const QString remoteDBusPath = mimeData->data(QStringLiteral("application/x-kde-ark-dndextract-path")); QDBusMessage message = QDBusMessage::createMethodCall(remoteDBusClient, remoteDBusPath, QStringLiteral("org.kde.ark.DndExtract"), QStringLiteral("extractSelectedFilesTo")); message.setArguments(QVariantList() << m_dirModel->dirLister()->url().adjusted(QUrl::PreferLocalFile).toString()); QDBusConnection::sessionBus().call(message, QDBus::NoBlock); } else { Qt::DropAction proposedAction((Qt::DropAction)dropEvent->property("proposedAction").toInt()); Qt::DropActions possibleActions(dropEvent->property("possibleActions").toInt()); Qt::MouseButtons buttons(dropEvent->property("buttons").toInt()); Qt::KeyboardModifiers modifiers(dropEvent->property("modifiers").toInt()); QDropEvent ev(QPoint(), possibleActions, mimeData, buttons, modifiers); ev.setDropAction(proposedAction); KIO::DropJob *dropJob = KIO::drop(&ev, m_dirModel->dirLister()->url().adjusted(QUrl::PreferLocalFile)); dropJob->uiDelegate()->setAutoErrorHandlingEnabled(true); } } void FolderModel::selectionChanged(const QItemSelection &selected, const QItemSelection &deselected) { QModelIndexList indices = selected.indexes(); indices.append(deselected.indexes()); QVector roles; roles.append(SelectedRole); foreach(const QModelIndex &index, indices) { emit dataChanged(index, index, roles); } if (!m_selectionModel->hasSelection()) { clearDragImages(); } else { foreach (const QModelIndex &idx, deselected.indexes()) { delete m_dragImages.take(idx.row()); } } updateActions(); } bool FolderModel::isBlank(int row) const { if (row < 0) { return true; } return data(index(row, 0), BlankRole).toBool(); } QVariant FolderModel::data(const QModelIndex& index, int role) const { if (!index.isValid()) { return QVariant(); } if (role == BlankRole) { return m_dragIndexes.contains(index); } else if (role == OverlaysRole) { const KFileItem item = itemForIndex(index); return item.overlays(); } else if (role == SelectedRole) { return m_selectionModel->isSelected(index); } else if (role == IsDirRole) { return isDir(mapToSource(index), m_dirModel); } else if (role == IsLinkRole) { const KFileItem item = itemForIndex(index); return item.isLink(); } else if (role == IsHiddenRole) { const KFileItem item = itemForIndex(index); return item.isHidden(); } else if (role == UrlRole) { return itemForIndex(index).url(); } else if (role == LinkDestinationUrl) { const KFileItem item = itemForIndex(index); if (m_parseDesktopFiles && item.isDesktopFile()) { const KDesktopFile file(item.targetUrl().path()); if (file.hasLinkType()) { return file.readUrl(); } } return item.targetUrl(); } else if (role == SizeRole) { bool isDir = data(index, IsDirRole).toBool(); if (!isDir) { return m_dirModel->data(mapToSource(QSortFilterProxyModel::index(index.row(), 1)), Qt::DisplayRole); } } else if (role == TypeRole) { return m_dirModel->data(mapToSource(QSortFilterProxyModel::index(index.row(), 6)), Qt::DisplayRole); } else if (role == FileNameRole) { return itemForIndex(index).url().fileName(); } return QSortFilterProxyModel::data(index, role); } int FolderModel::indexForUrl(const QUrl& url) const { return mapFromSource(m_dirModel->indexForUrl(url)).row(); } KFileItem FolderModel::itemForIndex(const QModelIndex &index) const { return m_dirModel->itemForIndex(mapToSource(index)); } bool FolderModel::isDir(const QModelIndex &index, const KDirModel *dirModel) const { KFileItem item = dirModel->itemForIndex(index); if (item.isDir()) { return true; } auto it = m_isDirCache.constFind(item.url()); if (it != m_isDirCache.constEnd()) { return *it; } if (m_parseDesktopFiles && item.isDesktopFile()) { // Check if the desktop file is a link to a directory KDesktopFile file(item.targetUrl().path()); if (!file.hasLinkType()) { return false; } const QUrl url(file.readUrl()); // Check if we already have a running StatJob for this URL. if (m_isDirJobs.contains(item.url())) { return false; } // Assume the root folder of a protocol is always a folder. // This avoids spinning up e.g. trash KIO slave just to check whether trash:/ is a folder. if (url.path() == QLatin1String("/")) { m_isDirCache.insert(item.url(), true); return true; } if (KProtocolInfo::protocolClass(url.scheme()) != QLatin1String(":local")) { return false; } KIO::StatJob *job = KIO::stat(url, KIO::HideProgressInfo); job->setProperty("org.kde.plasma.folder_url", item.url()); job->setSide(KIO::StatJob::SourceSide); job->setDetails(0); connect(job, &KJob::result, this, &FolderModel::statResult); m_isDirJobs.insert(item.url(), job); } return false; } void FolderModel::statResult(KJob *job) { KIO::StatJob *statJob = static_cast(job); const QUrl &url = statJob->property("org.kde.plasma.folder_url").toUrl(); const QModelIndex &idx = index(indexForUrl(url), 0); if (idx.isValid() && statJob->error() == KJob::NoError) { m_isDirCache[url] = statJob->statResult().isDir(); emit dataChanged(idx, idx, QVector() << IsDirRole); } m_isDirJobs.remove(url); } void FolderModel::evictFromIsDirCache(const KFileItemList& items) { foreach (const KFileItem &item, items) { m_screenMapper->removeFromMap(item.url()); m_isDirCache.remove(item.url()); } } bool FolderModel::lessThan(const QModelIndex &left, const QModelIndex &right) const { const KDirModel *dirModel = static_cast(sourceModel()); if (m_sortDirsFirst || left.column() == KDirModel::Size) { bool leftIsDir = isDir(left, dirModel); bool rightIsDir = isDir(right, dirModel); if (leftIsDir && !rightIsDir) { return (sortOrder() == Qt::AscendingOrder); } if (!leftIsDir && rightIsDir) { return (sortOrder() == Qt::DescendingOrder); } } const KFileItem leftItem = dirModel->data(left, KDirModel::FileItemRole).value(); const KFileItem rightItem = dirModel->data(right, KDirModel::FileItemRole).value(); const int column = left.column(); int result = 0; switch (column) { case KDirModel::Size: { if (isDir(left, dirModel) && isDir(right, dirModel)) { const int leftChildCount = dirModel->data(left, KDirModel::ChildCountRole).toInt(); const int rightChildCount = dirModel->data(right, KDirModel::ChildCountRole).toInt(); if (leftChildCount < rightChildCount) result = -1; else if (leftChildCount > rightChildCount) result = +1; } else { const KIO::filesize_t leftSize = leftItem.size(); const KIO::filesize_t rightSize = rightItem.size(); if (leftSize < rightSize) result = -1; else if (leftSize > rightSize) result = +1; } break; } case KDirModel::ModifiedTime: { const long long leftTime = leftItem.entry().numberValue(KIO::UDSEntry::UDS_MODIFICATION_TIME, -1); const long long rightTime = rightItem.entry().numberValue(KIO::UDSEntry::UDS_MODIFICATION_TIME, -1); if (leftTime < rightTime) result = -1; else if (leftTime > rightTime) result = +1; break; } case KDirModel::Type: result = QString::compare(dirModel->data(left, Qt::DisplayRole).toString(), dirModel->data(right, Qt::DisplayRole).toString()); break; default: break; } if (result != 0) return result < 0; QCollator collator; result = collator.compare(leftItem.text(), rightItem.text()); if (result != 0) return result < 0; result = collator.compare(leftItem.name(), rightItem.name()); if (result != 0) return result < 0; return QString::compare(leftItem.url().url(), rightItem.url().url(), Qt::CaseSensitive); } Qt::DropActions FolderModel::supportedDragActions() const { return Qt::CopyAction | Qt::MoveAction | Qt::LinkAction; } Qt::DropActions FolderModel::supportedDropActions() const { return Qt::CopyAction | Qt::MoveAction | Qt::LinkAction; } inline bool FolderModel::matchMimeType(const KFileItem &item) const { if (m_mimeSet.isEmpty()) { return false; } - if (m_mimeSet.contains(QLatin1String("all/all")) || m_mimeSet.contains(QStringLiteral("all/allfiles"))) { + if (m_mimeSet.contains(QLatin1String("all/all")) || m_mimeSet.contains(QLatin1String("all/allfiles"))) { return true; } const QString mimeType = item.determineMimeType().name(); return m_mimeSet.contains(mimeType); } inline bool FolderModel::matchPattern(const KFileItem &item) const { if (m_filterPatternMatchAll) { return true; } const QString name = item.name(); QListIterator i(m_regExps); while (i.hasNext()) { if (i.next().exactMatch(name)) { return true; } } return false; } bool FolderModel::filterAcceptsRow(int sourceRow, const QModelIndex &sourceParent) const { const KDirModel *dirModel = static_cast(sourceModel()); const KFileItem item = dirModel->itemForIndex(dirModel->index(sourceRow, KDirModel::Name, sourceParent)); if (m_usedByContainment && !m_screenMapper->sharedDesktops()) { const QUrl url = item.url(); const int screen = m_screenMapper->screenForItem(url); // don't do anything if the folderview is not associated with a screen if (m_screenUsed && screen == -1) { // The item is not associated with a screen, probably because this is the first // time we see it or the folderview was previously used as a regular applet. // Associated with this folderview if the view is on the first available screen if (m_screen == m_screenMapper->firstAvailableScreen(resolvedUrl())) { m_screenMapper->addMapping(url, m_screen, ScreenMapper::DelayedSignal); } else { return false; } } else if (m_screen != screen) { // the item belongs to a different screen, filter it out return false; } } if (m_filterMode == NoFilter) { return true; } if (m_filterMode == FilterShowMatches) { return (matchPattern(item) && matchMimeType(item)); } else { return !(matchPattern(item) && matchMimeType(item)); } } void FolderModel::createActions() { KIO::FileUndoManager *manager = KIO::FileUndoManager::self(); QAction *cut = KStandardAction::cut(this, &FolderModel::cut, this); QAction *copy = KStandardAction::copy(this, &FolderModel::copy, this); QAction *undo = KStandardAction::undo(manager, &KIO::FileUndoManager::undo, this); undo->setEnabled(manager->undoAvailable()); undo->setShortcutContext(Qt::WidgetShortcut); connect(manager, SIGNAL(undoAvailable(bool)), undo, SLOT(setEnabled(bool))); connect(manager, &KIO::FileUndoManager::undoTextChanged, this, &FolderModel::undoTextChanged); QAction *paste = KStandardAction::paste(this, &FolderModel::paste, this); QAction *pasteTo = KStandardAction::paste(this, &FolderModel::pasteTo, this); QAction *refresh = new QAction(QIcon::fromTheme(QStringLiteral("view-refresh")), i18n("&Refresh View"), this); refresh->setShortcut(QKeySequence(QKeySequence::Refresh)); connect(refresh, &QAction::triggered, this, &FolderModel::refresh); QAction *rename = KStandardAction::renameFile(this, &FolderModel::requestRename, this); QAction *trash = KStandardAction::moveToTrash(this, &FolderModel::moveSelectedToTrash, this); QAction *emptyTrash = new QAction(QIcon::fromTheme(QStringLiteral("trash-empty")), i18n("&Empty Trash"), this); connect(emptyTrash, &QAction::triggered, this, &FolderModel::emptyTrashBin); QAction *restoreFromTrash = new QAction(i18nc("Restore from trash", "Restore"), this); connect(restoreFromTrash, &QAction::triggered, this, &FolderModel::restoreSelectedFromTrash); QAction *del = KStandardAction::deleteFile(this, &FolderModel::deleteSelected, this); QAction *actOpen = new QAction(QIcon::fromTheme(QStringLiteral("window-new")), i18n("&Open"), this); connect(actOpen, &QAction::triggered, this, &FolderModel::openSelected); m_actionCollection.addAction(QStringLiteral("open"), actOpen); m_actionCollection.addAction(QStringLiteral("cut"), cut); m_actionCollection.addAction(QStringLiteral("undo"), undo); m_actionCollection.addAction(QStringLiteral("copy"), copy); m_actionCollection.addAction(QStringLiteral("paste"), paste); m_actionCollection.addAction(QStringLiteral("pasteto"), pasteTo); m_actionCollection.addAction(QStringLiteral("refresh"), refresh); m_actionCollection.addAction(QStringLiteral("rename"), rename); m_actionCollection.addAction(QStringLiteral("trash"), trash); m_actionCollection.addAction(QStringLiteral("del"), del); m_actionCollection.addAction(QStringLiteral("restoreFromTrash"), restoreFromTrash); m_actionCollection.addAction(QStringLiteral("emptyTrash"), emptyTrash); m_newMenu = new KNewFileMenu(&m_actionCollection, QStringLiteral("newMenu"), this); m_newMenu->setModal(false); connect(m_newMenu, &KNewFileMenu::directoryCreated, this, &FolderModel::newFileMenuItemCreated); connect(m_newMenu, &KNewFileMenu::fileCreated, this, &FolderModel::newFileMenuItemCreated); m_copyToMenu = new KFileCopyToMenu(nullptr); } QAction* FolderModel::action(const QString &name) const { return m_actionCollection.action(name); } QObject* FolderModel::newMenu() const { return m_newMenu->menu(); } void FolderModel::updateActions() { const QModelIndexList indexes = m_selectionModel->selectedIndexes(); KFileItemList items; QList urls; bool hasRemoteFiles = false; bool isTrashLink = false; const bool isTrash = (resolvedUrl().scheme() == QLatin1String("trash")); if (indexes.isEmpty()) { items << rootItem(); } else { items.reserve(indexes.count()); urls.reserve(indexes.count()); for (const QModelIndex &index : indexes) { KFileItem item = itemForIndex(index); if (!item.isNull()) { hasRemoteFiles |= item.localPath().isEmpty(); items.append(item); urls.append(item.url()); } } } KFileItemListProperties itemProperties(items); // Check if we're showing the menu for the trash link if (items.count() == 1 && items.at(0).isDesktopFile()) { KDesktopFile file(items.at(0).localPath()); if (file.hasLinkType() && file.readUrl() == QLatin1String("trash:/")) { isTrashLink = true; } } if (m_newMenu) { m_newMenu->checkUpToDate(); m_newMenu->setPopupFiles(m_dirModel->dirLister()->url()); // we need to set here as well, when the menu is shown via AppletInterface::eventFilter m_menuPosition = QCursor::pos(); if (QAction *newMenuAction = m_actionCollection.action(QStringLiteral("newMenu"))) { newMenuAction->setEnabled(itemProperties.supportsWriting()); newMenuAction->setVisible(!isTrash); } } if (QAction *emptyTrash = m_actionCollection.action(QStringLiteral("emptyTrash"))) { if (isTrash || isTrashLink) { emptyTrash->setVisible(true); emptyTrash->setEnabled(!isTrashEmpty()); } else { emptyTrash->setVisible(false); } } if (QAction *restoreFromTrash = m_actionCollection.action(QStringLiteral("restoreFromTrash"))) { restoreFromTrash->setVisible(isTrash); } if (QAction *moveToTrash = m_actionCollection.action(QStringLiteral("trash"))) { moveToTrash->setVisible(!hasRemoteFiles && itemProperties.supportsMoving()); } if (QAction *del = m_actionCollection.action(QStringLiteral("del"))) { del->setVisible(itemProperties.supportsDeleting()); } if (QAction *cut = m_actionCollection.action(QStringLiteral("cut"))) { cut->setEnabled(itemProperties.supportsDeleting()); cut->setVisible(!isTrash); } if (QAction *paste = m_actionCollection.action(QStringLiteral("paste"))) { bool enable = false; const QString pasteText = KIO::pasteActionText(QApplication::clipboard()->mimeData(), &enable, rootItem()); if (enable) { paste->setText(pasteText); paste->setEnabled(true); } else { paste->setText(i18n("&Paste")); paste->setEnabled(false); } if (QAction *pasteTo = m_actionCollection.action(QStringLiteral("pasteto"))) { pasteTo->setVisible(itemProperties.isDirectory() && itemProperties.supportsWriting()); pasteTo->setEnabled(paste->isEnabled()); pasteTo->setText(paste->text()); } } if (QAction *rename = m_actionCollection.action(QStringLiteral("rename"))) { rename->setEnabled(itemProperties.supportsMoving()); rename->setVisible(!isTrash); } } void FolderModel::openContextMenu(QQuickItem *visualParent, Qt::KeyboardModifiers modifiers) { if (m_usedByContainment && !KAuthorized::authorize(QStringLiteral("action/kdesktop_rmb"))) { return; } updateActions(); const QModelIndexList indexes = m_selectionModel->selectedIndexes(); QMenu *menu = new QMenu(); if (!m_fileItemActions) { m_fileItemActions = new KFileItemActions(this); m_fileItemActions->setParentWidget(QApplication::desktop()); } if (indexes.isEmpty()) { menu->addAction(m_actionCollection.action(QStringLiteral("newMenu"))); menu->addSeparator(); menu->addAction(m_actionCollection.action(QStringLiteral("paste"))); menu->addAction(m_actionCollection.action(QStringLiteral("undo"))); menu->addAction(m_actionCollection.action(QStringLiteral("refresh"))); menu->addAction(m_actionCollection.action(QStringLiteral("emptyTrash"))); menu->addSeparator(); KFileItemListProperties itemProperties(KFileItemList() << rootItem()); m_fileItemActions->setItemListProperties(itemProperties); menu->addAction(m_fileItemActions->preferredOpenWithAction(QString())); } else { KFileItemList items; QList urls; items.reserve(indexes.count()); urls.reserve(indexes.count()); for (const QModelIndex &index : indexes) { KFileItem item = itemForIndex(index); if (!item.isNull()) { items.append(item); urls.append(item.url()); } } KFileItemListProperties itemProperties(items); // Start adding the actions: // "Open" and "Open with" actions m_fileItemActions->setItemListProperties(itemProperties); m_fileItemActions->addOpenWithActionsTo(menu); menu->addSeparator(); menu->addAction(m_actionCollection.action(QStringLiteral("cut"))); menu->addAction(m_actionCollection.action(QStringLiteral("copy"))); menu->addAction(m_actionCollection.action(QStringLiteral("paste"))); menu->addSeparator(); menu->addAction(m_actionCollection.action(QStringLiteral("rename"))); menu->addAction(m_actionCollection.action(QStringLiteral("restoreFromTrash"))); KConfigGroup cg(KSharedConfig::openConfig(), "KDE"); bool showDeleteCommand = cg.readEntry("ShowDeleteCommand", false); menu->addAction(m_actionCollection.action(QStringLiteral("emptyTrash"))); QAction *trashAction = m_actionCollection.action(QStringLiteral("trash")); menu->addAction(trashAction); trashAction->setVisible(!modifiers.testFlag(Qt::ShiftModifier)); QAction *deleteAction = m_actionCollection.action(QStringLiteral("del")); menu->addAction(deleteAction); deleteAction->setVisible(showDeleteCommand || !trashAction->isVisible()); menu->addSeparator(); // Service actions m_fileItemActions->addServiceActionsTo(menu); menu->addSeparator(); // Plugin actions m_fileItemActions->addPluginActionsTo(menu); // Copy To, Move To KSharedConfig::Ptr dolphin = KSharedConfig::openConfig(QStringLiteral("dolphinrc")); if (KConfigGroup(dolphin, "General").readEntry("ShowCopyMoveMenu", false)) { m_copyToMenu->setUrls(urls); m_copyToMenu->setReadOnly(!itemProperties.supportsMoving()); m_copyToMenu->addActionsTo(menu); menu->addSeparator(); } // Properties if (KPropertiesDialog::canDisplay(items)) { menu->addSeparator(); QAction *act = new QAction(QIcon::fromTheme(QStringLiteral("document-properties")), i18n("&Properties"), menu); act->setShortcuts({Qt::ALT + Qt::Key_Return, Qt::ALT + Qt::Key_Enter}); QObject::connect(act, &QAction::triggered, this, &FolderModel::openPropertiesDialog); menu->addAction(act); } } if (visualParent) { m_menuPosition = visualParent->mapToGlobal(QPointF(0, visualParent->height())).toPoint(); } else { m_menuPosition = QCursor::pos(); } // Used to monitor Shift modifier usage while the menu is open, to // swap the Trash and Delete actions. menu->installEventFilter(this); menu->setAttribute(Qt::WA_TranslucentBackground); menu->winId(); //force surface creation before ensurePolish call in menu::Popup which happens before show menu->popup(m_menuPosition); connect(menu, &QMenu::aboutToHide, [menu]() { menu->deleteLater(); }); } void FolderModel::openPropertiesDialog() { const QModelIndexList indexes = m_selectionModel->selectedIndexes(); if (indexes.isEmpty()) { return; } KFileItemList items; items.reserve(indexes.count()); for (const QModelIndex &index : indexes) { KFileItem item = itemForIndex(index); if (!item.isNull()) { items.append(item); } } if (!KPropertiesDialog::canDisplay(items)) { return; } KPropertiesDialog::showDialog(items, nullptr, false /*non modal*/); } void FolderModel::linkHere(const QUrl &sourceUrl) { KIO::CopyJob *job = KIO::link(sourceUrl, m_dirModel->dirLister()->url()); KIO::FileUndoManager::self()->recordCopyJob(job); } QList FolderModel::selectedUrls() const { const auto indexes = m_selectionModel->selectedIndexes(); QList urls; urls.reserve(indexes.count()); for (const QModelIndex &index : indexes) { urls.append(itemForIndex(index).url()); } return urls; } void FolderModel::copy() { if (!m_selectionModel->hasSelection()) { return; } if (QAction *action = m_actionCollection.action(QStringLiteral("copy"))) { if (!action->isEnabled()) { return; } } QMimeData *mimeData = QSortFilterProxyModel::mimeData(m_selectionModel->selectedIndexes()); QApplication::clipboard()->setMimeData(mimeData); } void FolderModel::cut() { if (!m_selectionModel->hasSelection()) { return; } if (QAction *action = m_actionCollection.action(QStringLiteral("cut"))) { if (!action->isEnabled()) { return; } } QMimeData *mimeData = QSortFilterProxyModel::mimeData(m_selectionModel->selectedIndexes()); KIO::setClipboardDataCut(mimeData, true); QApplication::clipboard()->setMimeData(mimeData); } void FolderModel::paste() { if (QAction *action = m_actionCollection.action(QStringLiteral("paste"))) { if (!action->isEnabled()) { return; } } KIO::paste(QApplication::clipboard()->mimeData(), m_dirModel->dirLister()->url()); } void FolderModel::pasteTo() { const QList urls = selectedUrls(); Q_ASSERT(urls.count() == 1); KIO::paste(QApplication::clipboard()->mimeData(), urls.first()); } void FolderModel::refresh() { m_errorString.clear(); emit errorStringChanged(); m_dirModel->dirLister()->updateDirectory(m_dirModel->dirLister()->url()); } QObject *FolderModel::appletInterface() const { return m_appletInterface; } void FolderModel::setAppletInterface(QObject *appletInterface) { if (m_appletInterface != appletInterface) { Q_ASSERT(!m_appletInterface); m_appletInterface = appletInterface; if (appletInterface) { Plasma::Applet *applet = appletInterface->property("_plasma_applet").value(); if (applet) { Plasma::Containment *containment = applet->containment(); if (containment) { Plasma::Corona *corona = containment->corona(); if (corona) { m_screenMapper->setCorona(corona); } setScreen(containment->screen()); connect(containment, &Plasma::Containment::screenChanged, this, &FolderModel::setScreen); } } } emit appletInterfaceChanged(); } } void FolderModel::moveSelectedToTrash() { if (!m_selectionModel->hasSelection()) { return; } if (QAction *action = m_actionCollection.action(QStringLiteral("trash"))) { if (!action->isEnabled()) { return; } } const QList urls = selectedUrls(); KIO::JobUiDelegate uiDelegate; if (uiDelegate.askDeleteConfirmation(urls, KIO::JobUiDelegate::Trash, KIO::JobUiDelegate::DefaultConfirmation)) { KIO::Job* job = KIO::trash(urls); job->uiDelegate()->setAutoErrorHandlingEnabled(true); KIO::FileUndoManager::self()->recordJob(KIO::FileUndoManager::Trash, urls, QUrl(QStringLiteral("trash:/")), job); } } void FolderModel::deleteSelected() { if (!m_selectionModel->hasSelection()) { return; } if (QAction *action = m_actionCollection.action(QStringLiteral("del"))) { if (!action->isEnabled()) { return; } } const QList urls = selectedUrls(); KIO::JobUiDelegate uiDelegate; if (uiDelegate.askDeleteConfirmation(urls, KIO::JobUiDelegate::Delete, KIO::JobUiDelegate::DefaultConfirmation)) { KIO::Job* job = KIO::del(urls); job->uiDelegate()->setAutoErrorHandlingEnabled(true); } } void FolderModel::openSelected() { if (!m_selectionModel->hasSelection()) { return; } const QList urls = selectedUrls(); for (const QUrl &url : urls) { (void) new KRun(url, nullptr); } } void FolderModel::undo() { if (QAction *action = m_actionCollection.action(QStringLiteral("undo"))) { // trigger() doesn't check enabled and would crash if invoked nonetheless. if (action->isEnabled()) { action->trigger(); } } } void FolderModel::emptyTrashBin() { KIO::JobUiDelegate uiDelegate; uiDelegate.setWindow(QApplication::desktop()); if (uiDelegate.askDeleteConfirmation(QList(), KIO::JobUiDelegate::EmptyTrash, KIO::JobUiDelegate::DefaultConfirmation)) { KIO::Job* job = KIO::emptyTrash(); job->uiDelegate()->setAutoErrorHandlingEnabled(true); } } void FolderModel::restoreSelectedFromTrash() { if (!m_selectionModel->hasSelection()) { return; } const auto &urls = selectedUrls(); KIO::RestoreJob *job = KIO::restoreFromTrash(urls); job->uiDelegate()->setAutoErrorHandlingEnabled(true); } bool FolderModel::isTrashEmpty() { KConfig trashConfig(QStringLiteral("trashrc"), KConfig::SimpleConfig); return trashConfig.group("Status").readEntry("Empty", true); } void FolderModel::undoTextChanged(const QString &text) { if (QAction *action = m_actionCollection.action(QStringLiteral("undo"))) { action->setText(text); } } diff --git a/kcms/baloo/folderselectionwidget.cpp b/kcms/baloo/folderselectionwidget.cpp index 7d04a849d..77a3e5896 100644 --- a/kcms/baloo/folderselectionwidget.cpp +++ b/kcms/baloo/folderselectionwidget.cpp @@ -1,357 +1,357 @@ /* * This file is part of the KDE Baloo Project * Copyright (C) 2014 Vishesh Handa * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 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 * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA * */ #include "folderselectionwidget.h" #include #include #include #include #include #include #include #include #include #include #include #include #include FolderSelectionWidget::FolderSelectionWidget(QWidget* parent, Qt::WindowFlags f) : QWidget(parent, f) { m_listWidget = new QListWidget(this); m_listWidget->setAlternatingRowColors(true); m_messageWidget = new KMessageWidget(this); m_messageWidget->hide(); QVBoxLayout* layout = new QVBoxLayout(this); layout->addWidget(m_messageWidget); layout->addWidget(m_listWidget); QHBoxLayout* hLayout = new QHBoxLayout; m_addButton = new QPushButton(this); m_addButton->setIcon(QIcon::fromTheme(QStringLiteral("list-add"))); connect(m_addButton, &QPushButton::clicked, this, &FolderSelectionWidget::slotAddButtonClicked); m_removeButton = new QPushButton(this); m_removeButton->setIcon(QIcon::fromTheme(QStringLiteral("list-remove"))); m_removeButton->setEnabled(false); connect(m_removeButton, &QPushButton::clicked, this, &FolderSelectionWidget::slotRemoveButtonClicked); connect(m_listWidget, &QListWidget::currentItemChanged, this, &FolderSelectionWidget::slotCurrentItemChanged); hLayout->addWidget(m_addButton); hLayout->addWidget(m_removeButton); QSpacerItem* spacer = new QSpacerItem(0, 0, QSizePolicy::Expanding, QSizePolicy::Minimum); hLayout->addItem(spacer); layout->addItem(hLayout); } namespace { QStringList addTrailingSlashes(const QStringList& input) { QStringList output; Q_FOREACH (QString str, input) { if (!str.endsWith(QDir::separator())) str.append(QDir::separator()); output << str; } return output; } QString makeHomePretty(const QString& url) { if (url.startsWith(QDir::homePath())) return QString(url).replace(0, QDir::homePath().length(), QStringLiteral("~")); return url; } } void FolderSelectionWidget::setDirectoryList(QStringList includeDirs, QStringList exclude) { m_listWidget->clear(); m_mountPoints.clear(); QList devices = Solid::Device::listFromType(Solid::DeviceInterface::StorageAccess); Q_FOREACH (const Solid::Device& dev, devices) { const Solid::StorageAccess* sa = dev.as(); if (!sa->isAccessible()) continue; QString mountPath = sa->filePath(); if (!shouldShowMountPoint(mountPath)) continue; m_mountPoints << mountPath; } m_mountPoints << QDir::homePath(); m_mountPoints = addTrailingSlashes(m_mountPoints); includeDirs = addTrailingSlashes(includeDirs); exclude = addTrailingSlashes(exclude); QStringList excludeList = exclude; Q_FOREACH (const QString& mountPath, m_mountPoints) { if (includeDirs.contains(mountPath)) continue; if (exclude.contains(mountPath)) continue; if (!excludeList.contains(mountPath)) { excludeList << mountPath; } } Q_FOREACH (QString url, excludeList) { QListWidgetItem* item = new QListWidgetItem(m_listWidget); QString display = folderDisplayName(url); item->setData(Qt::DisplayRole, display); item->setData(Qt::WhatsThisRole, url); item->setData(UrlRole, url); item->setData(Qt::DecorationRole, QIcon::fromTheme(iconName(url))); item->setToolTip(makeHomePretty(url)); m_listWidget->addItem(item); } if (m_listWidget->count() == 0) { m_removeButton->setEnabled(false); } } QStringList FolderSelectionWidget::includeFolders() const { QStringList folders; Q_FOREACH (const QString& mountPath, m_mountPoints) { bool inExclude = false; for (int i=0; icount(); ++i) { QListWidgetItem* item = m_listWidget->item(i); QString url = item->data(UrlRole).toString(); if (mountPath == url) { inExclude = true; break; } } if (!inExclude && !folders.contains(mountPath)) { folders << mountPath; } } return folders; } QStringList FolderSelectionWidget::excludeFolders() const { QStringList folders; for (int i=0; icount(); ++i) { QListWidgetItem* item = m_listWidget->item(i); QString url = item->data(UrlRole).toString(); if (!folders.contains(url)) { folders << url; } } return folders; } QString FolderSelectionWidget::fetchMountPoint(const QString& url) const { QString mountPoint; Q_FOREACH (const QString& mount, m_mountPoints) { if (url.startsWith(mount) && mount.size() > mountPoint.size()) mountPoint = mount; } return mountPoint; } void FolderSelectionWidget::slotAddButtonClicked() { QString url = QFileDialog::getExistingDirectory(this, i18n("Select the folder which should be excluded")); if (url.isEmpty()) { return; } if (!url.endsWith(QDir::separator())) url.append(QDir::separator()); // We don't care about the root dir if (url == QLatin1String("/")) { showMessage(i18n("Not allowed to exclude root folder, please disable File Search if you do not want it")); return; } // Remove any existing folder with that name // Remove any folder which is a sub-folder QVector deleteList; for (int i=0; icount(); ++i) { QListWidgetItem* item = m_listWidget->item(i); QString existingUrl = item->data(UrlRole).toString(); if (existingUrl == url) { QString name = QUrl::fromLocalFile(url).fileName(); showMessage(i18n("Folder %1 is already excluded", name)); deleteList << item; continue; } QString existingMountPoint = fetchMountPoint(existingUrl); QString mountPoint = fetchMountPoint(url); if (existingMountPoint == mountPoint) { // existingUrl is not required since it comes under url if (existingUrl.startsWith(url)) { deleteList << item; } else if (url.startsWith(existingUrl)) { // No point adding ourselves since our parents exists // we just move the parent to the bottom url = existingUrl; deleteList << item; QString name = QUrl::fromLocalFile(url).adjusted(QUrl::StripTrailingSlash).fileName(); showMessage(i18n("Folder's parent %1 is already excluded", name)); } } } qDeleteAll(deleteList); QListWidgetItem* item = new QListWidgetItem(m_listWidget); QString displayName = folderDisplayName(url); item->setData(Qt::DisplayRole, displayName); item->setData(Qt::WhatsThisRole, url); item->setData(UrlRole, url); item->setData(Qt::DecorationRole, QIcon::fromTheme(iconName(url))); item->setToolTip(makeHomePretty(url)); m_listWidget->addItem(item); m_listWidget->setCurrentItem(item); Q_EMIT changed(); } void FolderSelectionWidget::slotRemoveButtonClicked() { QListWidgetItem* item = m_listWidget->currentItem(); delete item; Q_EMIT changed(); } void FolderSelectionWidget::slotCurrentItemChanged(QListWidgetItem* current, QListWidgetItem*) { m_removeButton->setEnabled(current != nullptr); } void FolderSelectionWidget::showMessage(const QString& message) { m_messageWidget->setText(message); m_messageWidget->setMessageType(KMessageWidget::Warning); m_messageWidget->animatedShow(); QTimer::singleShot(3000, m_messageWidget, &KMessageWidget::animatedHide); } QString FolderSelectionWidget::folderDisplayName(const QString& url) const { QString name = url; // Check Home Dir QString homePath = QDir::homePath() + QLatin1Char('/'); if (url == homePath) { return QDir(homePath).dirName(); } if (url.startsWith(homePath)) { name = url.mid(homePath.size()); } else { // Check Mount allMountPointsExcluded Q_FOREACH (QString mountPoint, m_mountPoints) { if (url.startsWith(mountPoint)) { - name = QLatin1Char('[') + QDir(mountPoint).dirName() + QStringLiteral("]/") + url.mid(mountPoint.length()); + name = QLatin1Char('[') + QDir(mountPoint).dirName() + QLatin1String("]/") + url.mid(mountPoint.length()); break; } } } if (name.endsWith(QLatin1Char('/'))) { name = name.mid(0, name.size() - 1); } return name; } bool FolderSelectionWidget::shouldShowMountPoint(const QString& mountPoint) { if (mountPoint == QLatin1String("/")) return false; if (mountPoint.startsWith(QLatin1String("/boot"))) return false; if (mountPoint.startsWith(QLatin1String("/tmp"))) return false; // The user's home directory is forcibly added so we can ignore /home // if /home actually contains the home directory if (mountPoint.startsWith(QLatin1String("/home")) && QDir::homePath().startsWith(QLatin1String("/home"))) return false; return true; } QString FolderSelectionWidget::iconName(QString path) const { // Ensure paths end with / if (!path.endsWith(QDir::separator())) path.append(QDir::separator()); QString homePath = QDir::homePath(); if (!homePath.endsWith(QDir::separator())) homePath.append(QDir::separator()); if (path == homePath) return QStringLiteral("user-home"); if (m_mountPoints.contains(path)) return QStringLiteral("drive-harddisk"); return QStringLiteral("folder"); } diff --git a/kcms/colors/colorsmodel.cpp b/kcms/colors/colorsmodel.cpp index 786f9398b..62371052a 100644 --- a/kcms/colors/colorsmodel.cpp +++ b/kcms/colors/colorsmodel.cpp @@ -1,242 +1,242 @@ /* * Copyright (C) 2007 Matthew Woehlke * Copyright (C) 2007 Jeremy Whiting * Copyright (C) 2016 Olivier Churlaud * Copyright (C) 2019 Kai Uwe Broulik * * 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) version 3 or any later version * accepted by the membership of KDE e.V. (or its successor approved * by the membership of KDE e.V.), which shall act as a proxy * defined in Section 14 of version 3 of the license. * * This program 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 General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ #include "colorsmodel.h" #include #include #include #include #include #include #include ColorsModel::ColorsModel(QObject *parent) : QAbstractListModel(parent) { } ColorsModel::~ColorsModel() = default; int ColorsModel::rowCount(const QModelIndex &parent) const { if (parent.isValid()) { return 0; } return m_data.count(); } QVariant ColorsModel::data(const QModelIndex &index, int role) const { if (!index.isValid() || index.row() >= m_data.count()) { return QVariant(); } const auto &item = m_data.at(index.row()); switch (role) { case Qt::DisplayRole: return item.display; case SchemeNameRole: return item.schemeName; case PaletteRole: return item.palette; case ActiveTitleBarBackgroundRole: return item.activeTitleBarBackground; case ActiveTitleBarForegroundRole: return item.activeTitleBarForeground; case PendingDeletionRole: return item.pendingDeletion; case RemovableRole: return item.removable; } return QVariant(); } bool ColorsModel::setData(const QModelIndex &index, const QVariant &value, int role) { if (!index.isValid() || index.row() >= m_data.count()) { return false; } if (role == PendingDeletionRole) { auto &item = m_data[index.row()]; const bool pendingDeletion = value.toBool(); if (item.pendingDeletion != pendingDeletion) { item.pendingDeletion = pendingDeletion; emit dataChanged(index, index, {PendingDeletionRole}); // move to the next non-pending theme const auto nonPending = match(index, PendingDeletionRole, false); if (!nonPending.isEmpty()) { setSelectedScheme(nonPending.first().data(SchemeNameRole).toString()); } emit pendingDeletionsChanged(); return true; } } return false; } QHash ColorsModel::roleNames() const { return { {Qt::DisplayRole, QByteArrayLiteral("display")}, {SchemeNameRole, QByteArrayLiteral("schemeName")}, {PaletteRole, QByteArrayLiteral("palette")}, {ActiveTitleBarBackgroundRole, QByteArrayLiteral("activeTitleBarBackground")}, {ActiveTitleBarForegroundRole, QByteArrayLiteral("activeTitleBarForeground")}, {RemovableRole, QByteArrayLiteral("removable")}, {PendingDeletionRole, QByteArrayLiteral("pendingDeletion")} }; } QString ColorsModel::selectedScheme() const { return m_selectedScheme; } void ColorsModel::setSelectedScheme(const QString &scheme) { if (m_selectedScheme == scheme) { return; } const bool firstTime = m_selectedScheme.isNull(); m_selectedScheme = scheme; if (!firstTime) { emit selectedSchemeChanged(scheme); } emit selectedSchemeIndexChanged(); } int ColorsModel::indexOfScheme(const QString &scheme) const { auto it = std::find_if(m_data.begin(), m_data.end(), [this, &scheme](const ColorsModelData &item) { return item.schemeName == scheme; }); if (it != m_data.end()) { return std::distance(m_data.begin(), it); } return -1; } int ColorsModel::selectedSchemeIndex() const { return indexOfScheme(m_selectedScheme); } void ColorsModel::load() { beginResetModel(); const int oldCount = m_data.count(); m_data.clear(); QStringList schemeFiles; const QStringList schemeDirs = QStandardPaths::locateAll(QStandardPaths::GenericDataLocation, QStringLiteral("color-schemes"), QStandardPaths::LocateDirectory); for (const QString &dir : schemeDirs) { const QStringList fileNames = QDir(dir).entryList(QStringList{QStringLiteral("*.colors")}); for (const QString &file : fileNames) { - const QString suffixedFileName = QStringLiteral("color-schemes/") + file; + const QString suffixedFileName = QLatin1String("color-schemes/") + file; // can't use QSet because of the transform below (passing const QString as this argument discards qualifiers) if (!schemeFiles.contains(suffixedFileName)) { schemeFiles.append(suffixedFileName); } } } std::transform(schemeFiles.begin(), schemeFiles.end(), schemeFiles.begin(), [](const QString &item) { return QStandardPaths::locate(QStandardPaths::GenericDataLocation, item); }); for (const QString &schemeFile : schemeFiles) { const QFileInfo fi(schemeFile); const QString baseName = fi.baseName(); KSharedConfigPtr config = KSharedConfig::openConfig(schemeFile, KConfig::SimpleConfig); KConfigGroup group(config, "General"); const QString name = group.readEntry("Name", baseName); const QPalette palette = KColorScheme::createApplicationPalette(config); // from kwin/decorations/decorationpalette.cpp KConfigGroup wmConfig(config, QStringLiteral("WM")); const QColor activeTitleBarBackground = wmConfig.readEntry("activeBackground", palette.color(QPalette::Active, QPalette::Highlight)); const QColor activeTitleBarForeground = wmConfig.readEntry("activeForeground", palette.color(QPalette::Active, QPalette::HighlightedText)); ColorsModelData item{ name, baseName, palette, activeTitleBarBackground, activeTitleBarForeground, fi.isWritable(), false, // pending deletion }; m_data.append(item); } QCollator collator; std::sort(m_data.begin(), m_data.end(), [&collator](const ColorsModelData &a, const ColorsModelData &b) { return collator.compare(a.display, b.display) < 0; }); endResetModel(); // an item might have been added before the currently selected one if (oldCount != m_data.count()) { emit selectedSchemeIndexChanged(); } } QStringList ColorsModel::pendingDeletions() const { QStringList pendingDeletions; for (const auto &item : m_data) { if (item.pendingDeletion) { pendingDeletions.append(item.schemeName); } } return pendingDeletions; } void ColorsModel::removeItemsPendingDeletion() { for (int i = m_data.count() - 1; i >= 0; --i) { if (m_data.at(i).pendingDeletion) { beginRemoveRows(QModelIndex(), i, i); m_data.remove(i); endRemoveRows(); } } } diff --git a/kcms/dateandtime/dtime.cpp b/kcms/dateandtime/dtime.cpp index f4407810a..5833caec9 100644 --- a/kcms/dateandtime/dtime.cpp +++ b/kcms/dateandtime/dtime.cpp @@ -1,490 +1,490 @@ /* * dtime.cpp * * Copyright (C) 1998 Luca Montecchiani * * Plasma analog-clock drawing code: * * Copyright 2007 by Aaron Seigo * Copyright 2007 by Riccardo Iaconelli * * 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. * * This program 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 General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. * */ #include "dtime.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "timedated_interface.h" #include "helper.h" Dtime::Dtime(QWidget * parent, bool haveTimeDated): QWidget(parent), m_haveTimedated(haveTimeDated) { setupUi(this); connect(setDateTimeAuto, &QCheckBox::toggled, this, &Dtime::serverTimeCheck); connect(setDateTimeAuto, &QCheckBox::toggled, this, &Dtime::configChanged); timeServerList->setEditable(false); connect(timeServerList, static_cast(&QComboBox::activated), this, &Dtime::configChanged); connect(timeServerList, &QComboBox::editTextChanged, this, &Dtime::configChanged); connect(setDateTimeAuto, &QCheckBox::toggled, timeServerList, &QComboBox::setEnabled); timeServerList->setEnabled(false); timeServerList->setEditable(true); if (!haveTimeDated) { findNTPutility(); if (ntpUtility.isEmpty()) { QString toolTip = i18n("No NTP utility has been found. " "Install 'ntpdate' or 'rdate' command to enable automatic " "updating of date and time."); setDateTimeAuto->setEnabled(false); setDateTimeAuto->setToolTip(toolTip); timeServerList->setToolTip(toolTip); } } QVBoxLayout *v2 = new QVBoxLayout( timeBox ); v2->setMargin( 0 ); kclock = new Kclock( timeBox ); kclock->setObjectName(QStringLiteral("Kclock")); kclock->setMinimumSize(150,150); v2->addWidget( kclock ); v2->addSpacing( KDialog::spacingHint() ); QHBoxLayout *v3 = new QHBoxLayout( ); v2->addLayout( v3 ); v3->addStretch(); timeEdit = new QTimeEdit( timeBox ); timeEdit->setWrapping(true); timeEdit->setDisplayFormat(KLocale::global()->use12Clock() ? "hh:mm:ss ap" : "HH:mm:ss"); v3->addWidget(timeEdit); v3->addStretch(); QString wtstr = i18n("Here you can change the system time. Click into the" " hours, minutes or seconds field to change the relevant value, either" " using the up and down buttons to the right or by entering a new value."); timeEdit->setWhatsThis( wtstr ); connect(timeEdit, &QTimeEdit::timeChanged, this, &Dtime::set_time); connect(cal, &KDatePicker::dateChanged, this, &Dtime::changeDate); connect(&internalTimer, &QTimer::timeout, this, &Dtime::timeout); kclock->setEnabled(false); //Timezone connect(tzonelist, &K4TimeZoneWidget::itemSelectionChanged, this, &Dtime::configChanged); tzonesearch->setTreeWidget(tzonelist); } void Dtime::currentZone() { KTimeZone localZone = KSystemTimeZones::local(); if (localZone.abbreviations().isEmpty()) { m_local->setText(i18nc("%1 is name of time zone", "Current local time zone: %1", K4TimeZoneWidget::displayName(localZone))); } else { m_local->setText(i18nc("%1 is name of time zone, %2 is its abbreviation", "Current local time zone: %1 (%2)", K4TimeZoneWidget::displayName(localZone), QString::fromUtf8(localZone.abbreviations().first()))); } } void Dtime::serverTimeCheck() { // Enable time and date if the ntp utility is missing bool enabled = ntpUtility.isEmpty() || !setDateTimeAuto->isChecked(); cal->setEnabled(enabled); timeEdit->setEnabled(enabled); //kclock->setEnabled(enabled); } void Dtime::findNTPutility(){ QByteArray envpath = qgetenv("PATH"); if (!envpath.isEmpty() && envpath.startsWith(':')) { - envpath = envpath.mid(1); + envpath.remove(0, 1); } QString path = QStringLiteral("/sbin:/usr/sbin:"); if (!envpath.isEmpty()) { path += QFile::decodeName(envpath); } else { path += QLatin1String("/bin:/usr/bin"); } foreach(const QString &possible_ntputility, QStringList() << "ntpdate" << "rdate" ) { if( !((ntpUtility = KStandardDirs::findExe(possible_ntputility, path)).isEmpty()) ) { qDebug() << "ntpUtility = " << ntpUtility; return; } } qDebug() << "ntpUtility not found!"; } void Dtime::set_time() { if( ontimeout ) return; internalTimer.stop(); time = timeEdit->time(); kclock->setTime( time ); emit timeChanged( true ); } void Dtime::changeDate(const QDate &d) { date = d; emit timeChanged( true ); } void Dtime::configChanged(){ emit timeChanged( true ); } void Dtime::load() { QString currentTimeZone; if (m_haveTimedated) { OrgFreedesktopTimedate1Interface timeDatedIface(QStringLiteral("org.freedesktop.timedate1"), QStringLiteral("/org/freedesktop/timedate1"), QDBusConnection::systemBus()); //the server list is not relevant for timesyncd, it fetches it from the network timeServerList->setVisible(false); timeServerLabel->setVisible(false); setDateTimeAuto->setEnabled(timeDatedIface.canNTP()); setDateTimeAuto->setChecked(timeDatedIface.nTP()); currentTimeZone = timeDatedIface.timezone(); } else { // The config is actually written to the system config, but the user does not have any local config, // since there is nothing writing it. KConfig _config( QStringLiteral("kcmclockrc"), KConfig::NoGlobals ); KConfigGroup config(&_config, "NTP"); timeServerList->clear(); timeServerList->addItems(config.readEntry("servers", i18n("Public Time Server (pool.ntp.org),\ asia.pool.ntp.org,\ europe.pool.ntp.org,\ north-america.pool.ntp.org,\ oceania.pool.ntp.org")).split(',', QString::SkipEmptyParts)); setDateTimeAuto->setChecked(config.readEntry("enabled", false)); if (ntpUtility.isEmpty()) { timeServerList->setEnabled(false); } currentTimeZone = KSystemTimeZones::local().name(); } // Reset to the current date and time time = QTime::currentTime(); date = QDate::currentDate(); cal->setDate(date); // start internal timer internalTimer.start( 1000 ); timeout(); //Timezone currentZone(); tzonelist->setSelected(currentTimeZone, true); emit timeChanged(false); } QString Dtime::selectedTimeZone() const { QStringList selectedZones(tzonelist->selection()); if (!selectedZones.isEmpty()) { return selectedZones.first(); } return QString(); } QStringList Dtime::ntpServers() const { // Save the order, but don't duplicate! QStringList list; if( timeServerList->count() != 0) list.append(timeServerList->currentText()); for ( int i=0; icount();i++ ) { QString text = timeServerList->itemText(i); if( !list.contains(text) ) list.append(text); // Limit so errors can go away and not stored forever if( list.count() == 10) break; } return list; } bool Dtime::ntpEnabled() const { return setDateTimeAuto->isChecked(); } QDateTime Dtime::userTime() const { return QDateTime(date, QTime(timeEdit->time())); } void Dtime::processHelperErrors( int code ) { if( code & ClockHelper::NTPError ) { KMessageBox::error( this, i18n("Unable to contact time server: %1.", timeServer) ); setDateTimeAuto->setChecked( false ); } if( code & ClockHelper::DateError ) { KMessageBox::error( this, i18n("Can not set date.")); } if( code & ClockHelper::TimezoneError) KMessageBox::error( this, i18n("Error setting new time zone."), i18n("Time zone Error")); } void Dtime::timeout() { // get current time time = QTime::currentTime(); ontimeout = true; timeEdit->setTime(time); ontimeout = false; kclock->setTime( time ); } QString Dtime::quickHelp() const { return i18n("

Date & Time

This system settings module can be used to set the system date and" " time. As these settings do not only affect you as a user, but rather the whole system, you" " can only change these settings when you start the System Settings as root. If you do not have" " the root password, but feel the system time should be corrected, please contact your system" " administrator."); } Kclock::Kclock(QWidget *parent) : QWidget(parent) { m_theme = new Plasma::Svg(this); m_theme->setImagePath(QStringLiteral("widgets/clock")); m_theme->setContainsMultipleImages(true); } Kclock::~Kclock() { delete m_theme; } void Kclock::showEvent( QShowEvent *event ) { setClockSize( size() ); QWidget::showEvent( event ); } void Kclock::resizeEvent( QResizeEvent * ) { setClockSize( size() ); } void Kclock::setClockSize(const QSize &size) { int dim = qMin(size.width(), size.height()); QSize newSize = QSize(dim, dim) * devicePixelRatioF(); if (newSize != m_faceCache.size()) { m_faceCache = QPixmap(newSize); m_handsCache = QPixmap(newSize); m_glassCache = QPixmap(newSize); m_faceCache.setDevicePixelRatio(devicePixelRatioF()); m_handsCache.setDevicePixelRatio(devicePixelRatioF()); m_glassCache.setDevicePixelRatio(devicePixelRatioF()); m_theme->resize(QSize(dim, dim)); m_repaintCache = RepaintAll; } } void Kclock::setTime(const QTime &time) { if (time.minute() != this->time.minute() || time.hour() != this->time.hour()) { if (m_repaintCache == RepaintNone) { m_repaintCache = RepaintHands; } } this->time = time; update(); } void Kclock::drawHand(QPainter *p, const QRect &rect, const qreal verticalTranslation, const qreal rotation, const QString &handName) { // this code assumes the following conventions in the svg file: // - the _vertical_ position of the hands should be set with respect to the center of the face // - the _horizontal_ position of the hands does not matter // - the _shadow_ elements should have the same vertical position as their _hand_ element counterpart QRectF elementRect; QString name = handName + "HandShadow"; if (m_theme->hasElement(name)) { p->save(); elementRect = m_theme->elementRect(name); if( rect.height() < 64 ) elementRect.setWidth( elementRect.width() * 2.5 ); static const QPoint offset = QPoint(2, 3); p->translate(rect.x() + (rect.width() / 2) + offset.x(), rect.y() + (rect.height() / 2) + offset.y()); p->rotate(rotation); p->translate(-elementRect.width()/2, elementRect.y()-verticalTranslation); m_theme->paint(p, QRectF(QPointF(0, 0), elementRect.size()), name); p->restore(); } p->save(); name = handName + "Hand"; elementRect = m_theme->elementRect(name); if (rect.height() < 64) { elementRect.setWidth(elementRect.width() * 2.5); } p->translate(rect.x() + rect.width()/2, rect.y() + rect.height()/2); p->rotate(rotation); p->translate(-elementRect.width()/2, elementRect.y()-verticalTranslation); m_theme->paint(p, QRectF(QPointF(0, 0), elementRect.size()), name); p->restore(); } void Kclock::paintInterface(QPainter *p, const QRect &rect) { const bool m_showSecondHand = true; // compute hand angles const qreal minutes = 6.0 * time.minute() - 180; const qreal hours = 30.0 * time.hour() - 180 + ((time.minute() / 59.0) * 30.0); qreal seconds = 0; if (m_showSecondHand) { static const double anglePerSec = 6; seconds = anglePerSec * time.second() - 180; } // paint face and glass cache QRect faceRect = m_faceCache.rect(); QRect targetRect = QRect(QPoint(0, 0), QSize(m_faceCache.width() / devicePixelRatioF(), m_faceCache.height() / devicePixelRatioF())); if (m_repaintCache == RepaintAll) { m_faceCache.fill(Qt::transparent); m_glassCache.fill(Qt::transparent); QPainter facePainter(&m_faceCache); QPainter glassPainter(&m_glassCache); facePainter.setRenderHint(QPainter::SmoothPixmapTransform); glassPainter.setRenderHint(QPainter::SmoothPixmapTransform); m_theme->paint(&facePainter, targetRect, QStringLiteral("ClockFace")); glassPainter.save(); QRectF elementRect = QRectF(QPointF(0, 0), m_theme->elementSize(QStringLiteral("HandCenterScrew"))); glassPainter.translate(faceRect.width() / (2 * devicePixelRatioF()) - elementRect.width() / 2, faceRect.height() / (2 * devicePixelRatioF()) - elementRect.height() / 2); m_theme->paint(&glassPainter, elementRect, QStringLiteral("HandCenterScrew")); glassPainter.restore(); m_theme->paint(&glassPainter, targetRect, QStringLiteral("Glass")); // get vertical translation, see drawHand() for more details m_verticalTranslation = m_theme->elementRect(QStringLiteral("ClockFace")).center().y(); } // paint hour and minute hands cache if (m_repaintCache == RepaintHands || m_repaintCache == RepaintAll) { m_handsCache.fill(Qt::transparent); QPainter handsPainter(&m_handsCache); handsPainter.drawPixmap(targetRect, m_faceCache, faceRect); handsPainter.setRenderHint(QPainter::SmoothPixmapTransform); drawHand(&handsPainter, targetRect, m_verticalTranslation, hours, QStringLiteral("Hour")); drawHand(&handsPainter, targetRect, m_verticalTranslation, minutes, QStringLiteral("Minute")); } // reset repaint cache flag m_repaintCache = RepaintNone; // paint caches and second hand if (targetRect.width() < rect.width()) { targetRect.moveLeft((rect.width() - targetRect.width()) / 2); } p->drawPixmap(targetRect, m_handsCache, faceRect); if (m_showSecondHand) { p->setRenderHint(QPainter::SmoothPixmapTransform); drawHand(p, targetRect, m_verticalTranslation, seconds, QStringLiteral("Second")); } p->drawPixmap(targetRect, m_glassCache, faceRect); } void Kclock::paintEvent( QPaintEvent * ) { QPainter paint(this); paint.setRenderHint(QPainter::Antialiasing); paintInterface(&paint, rect()); } diff --git a/kcms/desktoptheme/kcm.cpp b/kcms/desktoptheme/kcm.cpp index 40647b4d3..7eb759377 100644 --- a/kcms/desktoptheme/kcm.cpp +++ b/kcms/desktoptheme/kcm.cpp @@ -1,368 +1,368 @@ /* This file is part of the KDE Project Copyright (c) 2014 Marco Martin Copyright (c) 2014 Vishesh Handa Copyright (c) 2016 David Rosca Copyright (c) 2018 Kai Uwe Broulik 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 "kcm.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include Q_LOGGING_CATEGORY(KCM_DESKTOP_THEME, "kcm_desktoptheme") K_PLUGIN_FACTORY_WITH_JSON(KCMDesktopThemeFactory, "kcm_desktoptheme.json", registerPlugin();) KCMDesktopTheme::KCMDesktopTheme(QObject *parent, const QVariantList &args) : KQuickAddons::ConfigModule(parent, args) , m_defaultTheme(new Plasma::Theme(this)) , m_haveThemeExplorerInstalled(false) { qmlRegisterType(); KAboutData* about = new KAboutData(QStringLiteral("kcm_desktoptheme"), i18n("Plasma Style"), QStringLiteral("0.1"), QString(), KAboutLicense::LGPL); about->addAuthor(i18n("David Rosca"), QString(), QStringLiteral("nowrep@gmail.com")); setAboutData(about); setButtons(Apply | Default | Help); m_model = new QStandardItemModel(this); QHash roles = m_model->roleNames(); roles[PluginNameRole] = QByteArrayLiteral("pluginName"); roles[ThemeNameRole] = QByteArrayLiteral("themeName"); roles[DescriptionRole] = QByteArrayLiteral("description"); roles[IsLocalRole] = QByteArrayLiteral("isLocal"); roles[PendingDeletionRole] = QByteArrayLiteral("pendingDeletion"); m_model->setItemRoleNames(roles); m_haveThemeExplorerInstalled = !QStandardPaths::findExecutable(QStringLiteral("plasmathemeexplorer")).isEmpty(); } KCMDesktopTheme::~KCMDesktopTheme() { delete m_defaultTheme; } QStandardItemModel *KCMDesktopTheme::desktopThemeModel() const { return m_model; } QString KCMDesktopTheme::selectedPlugin() const { return m_selectedPlugin; } void KCMDesktopTheme::setSelectedPlugin(const QString &plugin) { if (m_selectedPlugin == plugin) { return; } m_selectedPlugin = plugin; emit selectedPluginChanged(m_selectedPlugin); emit selectedPluginIndexChanged(); updateNeedsSave(); } int KCMDesktopTheme::selectedPluginIndex() const { const auto results = m_model->match(m_model->index(0, 0), PluginNameRole, m_selectedPlugin); if (results.count() == 1) { return results.first().row(); } return -1; } bool KCMDesktopTheme::downloadingFile() const { return m_tempCopyJob; } void KCMDesktopTheme::setPendingDeletion(int index, bool pending) { QModelIndex idx = m_model->index(index, 0); m_model->setData(idx, pending, PendingDeletionRole); if (pending && selectedPluginIndex() == index) { // move to the next non-pending theme const auto nonPending = m_model->match(idx, PendingDeletionRole, false); setSelectedPlugin(nonPending.first().data(PluginNameRole).toString()); } updateNeedsSave(); } void KCMDesktopTheme::getNewStuff(QQuickItem *ctx) { if (!m_newStuffDialog) { m_newStuffDialog = new KNS3::DownloadDialog(QStringLiteral("plasma-themes.knsrc")); m_newStuffDialog.data()->setWindowTitle(i18n("Download New Plasma Styles")); m_newStuffDialog->setWindowModality(Qt::WindowModal); m_newStuffDialog->winId(); // so it creates the windowHandle(); connect(m_newStuffDialog.data(), &KNS3::DownloadDialog::accepted, this, &KCMDesktopTheme::load); } if (ctx && ctx->window()) { m_newStuffDialog->windowHandle()->setTransientParent(ctx->window()); } m_newStuffDialog.data()->show(); } void KCMDesktopTheme::installThemeFromFile(const QUrl &url) { if (url.isLocalFile()) { installTheme(url.toLocalFile()); return; } if (m_tempCopyJob) { return; } m_tempInstallFile.reset(new QTemporaryFile()); if (!m_tempInstallFile->open()) { emit showErrorMessage(i18n("Unable to create a temporary file.")); m_tempInstallFile.reset(); return; } m_tempCopyJob = KIO::file_copy(url, QUrl::fromLocalFile(m_tempInstallFile->fileName()), -1, KIO::Overwrite); m_tempCopyJob->uiDelegate()->setAutoErrorHandlingEnabled(true); emit downloadingFileChanged(); connect(m_tempCopyJob, &KIO::FileCopyJob::result, this, [this, url](KJob *job) { if (job->error() != KJob::NoError) { emit showErrorMessage(i18n("Unable to download the theme: %1", job->errorText())); return; } installTheme(m_tempInstallFile->fileName()); m_tempInstallFile.reset(); }); connect(m_tempCopyJob, &QObject::destroyed, this, &KCMDesktopTheme::downloadingFileChanged); } void KCMDesktopTheme::installTheme(const QString &path) { qCDebug(KCM_DESKTOP_THEME) << "Installing ... " << path; const QString program = QStringLiteral("kpackagetool5"); const QStringList arguments = { QStringLiteral("--type"), QStringLiteral("Plasma/Theme"), QStringLiteral("--install"), path}; - qCDebug(KCM_DESKTOP_THEME) << program << arguments.join(QLatin1String(" ")); + qCDebug(KCM_DESKTOP_THEME) << program << arguments.join(QLatin1Char(' ')); QProcess *myProcess = new QProcess(this); connect(myProcess, static_cast(&QProcess::finished), this, [this, myProcess](int exitCode, QProcess::ExitStatus exitStatus) { Q_UNUSED(exitStatus); if (exitCode == 0) { emit showSuccessMessage(i18n("Theme installed successfully.")); load(); } else { Q_EMIT showErrorMessage(i18n("Theme installation failed.")); } }); connect(myProcess, static_cast(&QProcess::error), this, [this](QProcess::ProcessError e) { qCWarning(KCM_DESKTOP_THEME) << "Theme installation failed: " << e; Q_EMIT showErrorMessage(i18n("Theme installation failed.")); }); myProcess->start(program, arguments); } void KCMDesktopTheme::applyPlasmaTheme(QQuickItem *item, const QString &themeName) { if (!item) { return; } Plasma::Theme *theme = m_themes[themeName]; if (!theme) { theme = new Plasma::Theme(themeName, this); m_themes[themeName] = theme; } Q_FOREACH (Plasma::Svg *svg, item->findChildren()) { svg->setTheme(theme); svg->setUsingRenderingCache(false); } } void KCMDesktopTheme::load() { m_pendingRemoval.clear(); // Get all desktop themes QStringList themes; const QStringList &packs = QStandardPaths::locateAll(QStandardPaths::GenericDataLocation, QStringLiteral("plasma/desktoptheme"), QStandardPaths::LocateDirectory); Q_FOREACH (const QString &ppath, packs) { const QDir cd(ppath); const QStringList &entries = cd.entryList(QDir::Dirs | QDir::Hidden | QDir::NoDotAndDotDot); Q_FOREACH (const QString &pack, entries) { const QString _metadata = ppath + QLatin1Char('/') + pack + QStringLiteral("/metadata.desktop"); if (QFile::exists(_metadata)) { themes << _metadata; } } } m_model->clear(); Q_FOREACH (const QString &theme, themes) { int themeSepIndex = theme.lastIndexOf(QLatin1Char('/'), -1); const QString themeRoot = theme.left(themeSepIndex); int themeNameSepIndex = themeRoot.lastIndexOf(QLatin1Char('/'), -1); const QString packageName = themeRoot.right(themeRoot.length() - themeNameSepIndex - 1); KDesktopFile df(theme); if (df.noDisplay()) { continue; } QString name = df.readName(); if (name.isEmpty()) { name = packageName; } const bool isLocal = QFileInfo(theme).isWritable(); if (m_model->findItems(packageName).isEmpty()) { QStandardItem *item = new QStandardItem; item->setText(packageName); item->setData(packageName, PluginNameRole); item->setData(name, ThemeNameRole); item->setData(df.readComment(), DescriptionRole); item->setData(isLocal, IsLocalRole); item->setData(false, PendingDeletionRole); m_model->appendRow(item); } } m_model->setSortRole(ThemeNameRole); // FIXME the model should really be just using Qt::DisplayRole m_model->sort(0 /*column*/); KConfigGroup cg(KSharedConfig::openConfig(QStringLiteral("plasmarc")), "Theme"); setSelectedPlugin(cg.readEntry("name", m_defaultTheme->themeName())); emit selectedPluginIndexChanged(); updateNeedsSave(); } void KCMDesktopTheme::save() { if (m_defaultTheme->themeName() != m_selectedPlugin) { m_defaultTheme->setThemeName(m_selectedPlugin); } processPendingDeletions(); updateNeedsSave(); } void KCMDesktopTheme::defaults() { setSelectedPlugin(QStringLiteral("default")); // can this be done more elegantly? const auto pendingDeletions = m_model->match(m_model->index(0, 0), PendingDeletionRole, true); for (const QModelIndex &idx : pendingDeletions) { m_model->setData(idx, false, PendingDeletionRole); } } bool KCMDesktopTheme::canEditThemes() const { return m_haveThemeExplorerInstalled; } void KCMDesktopTheme::editTheme(const QString &theme) { QProcess::startDetached(QStringLiteral("plasmathemeexplorer -t ") % theme); } void KCMDesktopTheme::updateNeedsSave() { setNeedsSave(!m_model->match(m_model->index(0, 0), PendingDeletionRole, true).isEmpty() || m_selectedPlugin != m_defaultTheme->themeName()); } void KCMDesktopTheme::processPendingDeletions() { const QString program = QStringLiteral("plasmapkg2"); const auto pendingDeletions = m_model->match(m_model->index(0, 0), PendingDeletionRole, true, -1 /*all*/); QVector persistentPendingDeletions; // turn into persistent model index so we can delete as we go std::transform(pendingDeletions.begin(), pendingDeletions.end(), std::back_inserter(persistentPendingDeletions), [](const QModelIndex &idx) { return QPersistentModelIndex(idx); }); for (const QPersistentModelIndex &idx : persistentPendingDeletions) { const QString pluginName = idx.data(PluginNameRole).toString(); const QString displayName = idx.data(Qt::DisplayRole).toString(); Q_ASSERT(pluginName != m_selectedPlugin); const QStringList arguments = {QStringLiteral("-t"), QStringLiteral("theme"), QStringLiteral("-r"), pluginName}; QProcess *process = new QProcess(this); connect(process, static_cast(&QProcess::finished), this, [this, process, idx, pluginName, displayName](int exitCode, QProcess::ExitStatus exitStatus) { Q_UNUSED(exitStatus); if (exitCode == 0) { m_model->removeRow(idx.row()); } else { emit showErrorMessage(i18n("Removing theme failed: %1", QString::fromLocal8Bit(process->readAllStandardOutput().trimmed()))); m_model->setData(idx, false, PendingDeletionRole); } process->deleteLater(); }); process->start(program, arguments); process->waitForFinished(); // needed so it deletes fine when "OK" is clicked and the dialog destroyed } } #include "kcm.moc" diff --git a/kcms/formats/kcmformats.cpp b/kcms/formats/kcmformats.cpp index 78b91fecc..86e6dc355 100644 --- a/kcms/formats/kcmformats.cpp +++ b/kcms/formats/kcmformats.cpp @@ -1,360 +1,360 @@ /* * kcmformats.cpp * Copyright 2014 Sebastian Kügler * * 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. * * This program 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 General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software */ // own #include "kcmformats.h" #include "ui_kcmformatswidget.h" #include "writeexports.h" // Qt #include #include #include #include #include #include #include #include // Frameworks #include #include #include #include K_PLUGIN_FACTORY_WITH_JSON(KCMFormatsFactory, "formats.json", registerPlugin();) KCMFormats::KCMFormats(QWidget *parent, const QVariantList &args) : KCModule(parent, args) { setQuickHelp(i18n("

Formats

" "You can configure the formats used for time, dates, money and other numbers here.")); m_ui = new Ui::KCMFormatsWidget; m_ui->setupUi(this); m_combos << m_ui->comboGlobal << m_ui->comboNumbers << m_ui->comboTime << m_ui->comboCurrency << m_ui->comboMeasurement << m_ui->comboCollate; } KCMFormats::~KCMFormats() { delete m_ui; } bool countryLessThan(const QLocale & c1, const QLocale & c2) { // Ensure that the "Default (C)" locale always appears at the top - if (c1.name()==QLatin1String("C") && c2.name()!=QLatin1String("C")) return true; - if (c2.name()==QLatin1String("C")) return false; + if (c1.name()== QLatin1Char('C') && c2.name()!=QLatin1String("C")) return true; + if (c2.name()== QLatin1Char('C')) return false; const QString ncn1 = !c1.nativeCountryName().isEmpty() ? c1.nativeCountryName() : QLocale::countryToString(c1.country()); const QString ncn2 = !c2.nativeCountryName().isEmpty() ? c2.nativeCountryName() : QLocale::countryToString(c2.country()); return QString::localeAwareCompare(ncn1, ncn2) < 0; } void KCMFormats::load() { QList allLocales = QLocale::matchingLocales(QLocale::AnyLanguage, QLocale::AnyScript, QLocale::AnyCountry); std::sort(allLocales.begin(), allLocales.end(), countryLessThan); foreach(QComboBox * combo, m_combos) { initCombo(combo, allLocales); } readConfig(); foreach(QComboBox * combo, m_combos) { connectCombo(combo); } connect(m_ui->checkDetailed, &QAbstractButton::toggled, [ = ]() { updateExample(); updateEnabled(); emit changed(true); }); updateEnabled(); updateExample(); emit changed(false); } void KCMFormats::initCombo(QComboBox *combo, const QList & allLocales) { combo->clear(); const QString clabel = i18n("No change"); combo->addItem(clabel, QString()); foreach(const QLocale & l, allLocales) { addLocaleToCombo(combo, l); } } void KCMFormats::connectCombo(QComboBox *combo) { connect(combo, &QComboBox::currentTextChanged, [ = ]() { emit changed(true); updateExample(); }); } void KCMFormats::addLocaleToCombo(QComboBox *combo, const QLocale &locale) { const QString clabel = !locale.nativeCountryName().isEmpty() ? locale.nativeCountryName() : QLocale::countryToString(locale.country()); // This needs to use name() rather than bcp47name() or later on the export will generate a non-sense locale (e.g. "it" instead of // "it_IT") // TODO: Properly handle scripts (@foo) QString cvalue = locale.name(); if (!cvalue.contains(QLatin1Char('.')) && cvalue != QLatin1Char('C')) { // explicitly add the encoding, // otherwise Qt doesn't accept dead keys and garbles the output as well cvalue.append(QLatin1Char('.') + QTextCodec::codecForLocale()->name()); } QString flagcode; const QStringList split = locale.name().split(QLatin1Char('_')); if (split.count() > 1) { flagcode = split[1].toLower(); } QIcon flagIcon = loadFlagIcon(flagcode); const QString nativeLangName = locale.nativeLanguageName(); if (!nativeLangName.isEmpty()) { combo->addItem(flagIcon, i18n("%1 - %2 (%3)", clabel, nativeLangName, locale.name()), cvalue); } else { combo->addItem(flagIcon, i18n("%1 (%2)", clabel, locale.name()), cvalue); } } void setCombo(QComboBox *combo, const QString &key) { const int ix = combo->findData(key); if (ix > -1) { combo->setCurrentIndex(ix); } } QIcon KCMFormats::loadFlagIcon(const QString &flagCode) { QIcon icon = m_cachedFlags.value(flagCode); if (!icon.isNull()) { return icon; } QString flag(QStandardPaths::locate(QStandardPaths::GenericDataLocation, QStringLiteral("kf5/locale/countries/%1/flag.png").arg(flagCode))); if (!flag.isEmpty()) { icon = QIcon(flag); } else { if (m_cachedUnknown.isNull()) { m_cachedUnknown = QIcon::fromTheme("unknown"); } icon = m_cachedUnknown; } m_cachedFlags.insert(flagCode, icon); return icon; } void KCMFormats::readConfig() { m_config = KConfigGroup(KSharedConfig::openConfig(configFile), "Formats"); bool useDetailed = m_config.readEntry("useDetailed", false); m_ui->checkDetailed->setChecked(useDetailed); setCombo(m_ui->comboGlobal, m_config.readEntry(lcLang, qgetenv(lcLang.toLatin1()))); setCombo(m_ui->comboNumbers, m_config.readEntry(lcNumeric, qgetenv(lcNumeric.toLatin1()))); setCombo(m_ui->comboTime, m_config.readEntry(lcTime, qgetenv(lcTime.toLatin1()))); setCombo(m_ui->comboCollate, m_config.readEntry(lcCollate, qgetenv(lcCollate.toLatin1()))); setCombo(m_ui->comboCurrency, m_config.readEntry(lcMonetary, qgetenv(lcMonetary.toLatin1()))); setCombo(m_ui->comboMeasurement, m_config.readEntry(lcMeasurement, qgetenv(lcMeasurement.toLatin1()))); updateEnabled(); } void KCMFormats::writeConfig() { m_config = KConfigGroup(KSharedConfig::openConfig(configFile), "Formats"); // global ends up empty here when OK button is clicked from kcmshell5, // apparently the data in the combo is gone by the time save() is called. // This might be a problem in KCModule, but does not directly affect us // since within systemsettings, it works fine. // See https://bugs.kde.org/show_bug.cgi?id=334624 if (m_ui->comboGlobal->count() == 0) { qWarning() << "Couldn't read data from UI, writing configuration failed."; return; } const QString global = m_ui->comboGlobal->currentData().toString(); if (!m_ui->checkDetailed->isChecked()) { // Global setting, clean up config m_config.deleteEntry("useDetailed"); if (global.isEmpty()) { m_config.deleteEntry(lcLang); } else { m_config.writeEntry(lcLang, global); } m_config.deleteEntry(lcNumeric); m_config.deleteEntry(lcTime); m_config.deleteEntry(lcMonetary); m_config.deleteEntry(lcMeasurement); m_config.deleteEntry(lcCollate); m_config.deleteEntry(lcCtype); } else { // Save detailed settings m_config.writeEntry("useDetailed", true); if (global.isEmpty()) { m_config.deleteEntry(lcLang); } else { m_config.writeEntry(lcLang, global); } const QString numeric = m_ui->comboNumbers->currentData().toString(); if (numeric.isEmpty()) { m_config.deleteEntry(lcNumeric); } else { m_config.writeEntry(lcNumeric, numeric); } const QString time = m_ui->comboTime->currentData().toString(); if (time.isEmpty()) { m_config.deleteEntry(lcTime); } else { m_config.writeEntry(lcTime, time); } const QString monetary = m_ui->comboCurrency->currentData().toString(); if (monetary.isEmpty()) { m_config.deleteEntry(lcMonetary); } else { m_config.writeEntry(lcMonetary, monetary); } const QString measurement = m_ui->comboMeasurement->currentData().toString(); if (measurement.isEmpty()) { m_config.deleteEntry(lcMeasurement); } else { m_config.writeEntry(lcMeasurement, measurement); } const QString collate = m_ui->comboCollate->currentData().toString(); if (collate.isEmpty()) { m_config.deleteEntry(lcCollate); } else { m_config.writeEntry(lcCollate, collate); } } m_config.sync(); } void KCMFormats::save() { writeConfig(); writeExports(); KMessageBox::information(this, i18n("Your changes will take effect the next time you log in."), i18n("Format Settings Changed"), QStringLiteral("FormatSettingsChanged")); } void KCMFormats::defaults() { m_ui->checkDetailed->setChecked(false); // restore user defaults from env vars setCombo(m_ui->comboGlobal, qgetenv(lcLang.toLatin1())); setCombo(m_ui->comboNumbers, qgetenv(lcNumeric.toLatin1())); setCombo(m_ui->comboTime, qgetenv(lcTime.toLatin1())); setCombo(m_ui->comboCollate, qgetenv(lcCollate.toLatin1())); setCombo(m_ui->comboCurrency, qgetenv(lcMonetary.toLatin1())); setCombo(m_ui->comboMeasurement, qgetenv(lcMeasurement.toLatin1())); updateEnabled(); } void KCMFormats::updateEnabled() { const bool enabled = m_ui->checkDetailed->isChecked(); m_ui->labelNumbers->setEnabled(enabled); m_ui->labelTime->setEnabled(enabled); m_ui->labelCurrency->setEnabled(enabled); m_ui->labelMeasurement->setEnabled(enabled); m_ui->labelCollate->setEnabled(enabled); m_ui->comboNumbers->setEnabled(enabled); m_ui->comboTime->setEnabled(enabled); m_ui->comboCurrency->setEnabled(enabled); m_ui->comboMeasurement->setEnabled(enabled); m_ui->comboCollate->setEnabled(enabled); } void KCMFormats::updateExample() { const bool useDetailed = m_ui->checkDetailed->isChecked(); QLocale nloc; QLocale tloc; QLocale cloc; QLocale mloc; QString str; QString glob = m_ui->comboGlobal->currentData().toString(); if (useDetailed) { str = m_ui->comboNumbers->currentData().toString(); nloc = str.isEmpty() ? QLocale(glob) : QLocale(str); str = m_ui->comboTime->currentData().toString(); tloc = str.isEmpty() ? QLocale(glob) : QLocale(str); str = m_ui->comboCurrency->currentData().toString(); cloc = str.isEmpty() ? QLocale(glob) : QLocale(str); str = m_ui->comboMeasurement->currentData().toString(); mloc = str.isEmpty() ? QLocale(glob) : QLocale(str); } else { nloc = QLocale(glob); tloc = QLocale(glob); cloc = QLocale(glob); mloc = QLocale(glob); } const QString numberExample = nloc.toString(1000.01); - const QString timeExample = i18n("%1 (long format)", tloc.toString(QDateTime::currentDateTime())) + QStringLiteral("\n") + + const QString timeExample = i18n("%1 (long format)", tloc.toString(QDateTime::currentDateTime())) + QLatin1Char('\n') + i18n("%1 (short format)", tloc.toString(QDateTime::currentDateTime(), QLocale::ShortFormat)); const QString currencyExample = cloc.toCurrencyString(24.00); QString measurementSetting; if (mloc.measurementSystem() == QLocale::ImperialUKSystem) { measurementSetting = i18nc("Measurement combobox", "Imperial UK"); } else if (mloc.measurementSystem() == QLocale::ImperialUSSystem || mloc.measurementSystem() == QLocale::ImperialSystem) { measurementSetting = i18nc("Measurement combobox", "Imperial US"); } else { measurementSetting = i18nc("Measurement combobox", "Metric"); } m_ui->exampleNumbers->setText(numberExample); m_ui->exampleTime->setText(timeExample); m_ui->exampleCurrency->setText(currencyExample); m_ui->exampleMeasurement->setText(measurementSetting); } #include "kcmformats.moc" diff --git a/kcms/hardware/joystick/joywidget.cpp b/kcms/hardware/joystick/joywidget.cpp index a5af1bd4d..b5a451884 100644 --- a/kcms/hardware/joystick/joywidget.cpp +++ b/kcms/hardware/joystick/joywidget.cpp @@ -1,417 +1,417 @@ /*************************************************************************** * Copyright (C) 2003,2012 by Martin Koller * * kollix@aon.at * * This file is part of the KDE Control Center Module for Joysticks * * * * 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. * * * * This program 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 General Public License for more details. * * * * You should have received a copy of the GNU General Public License * * along with this program; if not, write to the * * Free Software Foundation, Inc., * * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. * ***************************************************************************/ #include "joywidget.h" #include "joydevice.h" #include "poswidget.h" #include "caldialog.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include //-------------------------------------------------------------- static QString PRESSED = I18N_NOOP("PRESSED"); //-------------------------------------------------------------- class TableWidget : public QTableWidget { public: TableWidget(int row, int col) : QTableWidget(row, col) {} QSize sizeHint() const override { return QSize(150, 100); // return a smaller size than the Qt default(256, 192) } }; //-------------------------------------------------------------- JoyWidget::JoyWidget(QWidget *parent) : QWidget(parent), idle(nullptr), joydev(nullptr) { QVBoxLayout *mainVbox = new QVBoxLayout(this); mainVbox->setSpacing(KDialog::spacingHint()); mainVbox->setContentsMargins(0, 0, 0, 0); // create area to show an icon + message if no joystick was detected { messageBox = new KMessageWidget(this); messageBox->setMessageType(KMessageWidget::Error); messageBox->setCloseButtonVisible(false); messageBox->hide(); mainVbox->addWidget(messageBox); } QHBoxLayout *devHbox = new QHBoxLayout; devHbox->setSpacing(KDialog::spacingHint()); devHbox->addWidget(new QLabel(i18n("Device:"))); devHbox->addWidget(device = new KComboBox(true)); device->setInsertPolicy(QComboBox::NoInsert); KUrlCompletion *kc = new KUrlCompletion(KUrlCompletion::FileCompletion); device->setCompletionObject(kc); device->setAutoDeleteCompletionObject(true); connect(device, SIGNAL(activated(QString)), this, SLOT(deviceChanged(QString))); connect(device, SIGNAL(returnPressed(QString)), this, SLOT(deviceChanged(QString))); devHbox->setStretchFactor(device, 3); QHBoxLayout *hbox = new QHBoxLayout; hbox->setSpacing(KDialog::spacingHint()); mainVbox->addLayout(devHbox); mainVbox->addLayout(hbox); QVBoxLayout *vboxLeft = new QVBoxLayout; vboxLeft->setSpacing(KDialog::spacingHint()); vboxLeft->addWidget(new QLabel(i18nc("Cue for deflection of the stick", "Position:"))); vboxLeft->addWidget(xyPos = new PosWidget); vboxLeft->addWidget(trace = new QCheckBox(i18n("Show trace"))); connect(trace, &QAbstractButton::toggled, this, &JoyWidget::traceChanged); QVBoxLayout *vboxMid = new QVBoxLayout; vboxMid->setSpacing(KDialog::spacingHint()); QVBoxLayout *vboxRight = new QVBoxLayout; vboxRight->setSpacing(KDialog::spacingHint()); // calculate the column width we need QFontMetrics fm(font()); int colWidth = qMax(fm.width(PRESSED), fm.width(QStringLiteral("-32767"))) + 10; // -32767 largest string vboxMid->addWidget(new QLabel(i18n("Buttons:"))); buttonTbl = new TableWidget(0, 1); buttonTbl->setSelectionMode(QAbstractItemView::NoSelection); buttonTbl->setEditTriggers(QAbstractItemView::NoEditTriggers); buttonTbl->setHorizontalHeaderLabels(QStringList(i18n("State"))); buttonTbl->setSortingEnabled(false); buttonTbl->horizontalHeader()->setSectionsClickable(false); buttonTbl->horizontalHeader()->setSectionResizeMode(QHeaderView::Stretch); buttonTbl->horizontalHeader()->resizeSection(0, colWidth); buttonTbl->verticalHeader()->setSectionsClickable(false); vboxMid->addWidget(buttonTbl); vboxRight->addWidget(new QLabel(i18n("Axes:"))); axesTbl = new TableWidget(0, 1); axesTbl->setSelectionMode(QAbstractItemView::NoSelection); axesTbl->setEditTriggers(QAbstractItemView::NoEditTriggers); axesTbl->setHorizontalHeaderLabels(QStringList(i18n("Value"))); axesTbl->setSortingEnabled(false); axesTbl->horizontalHeader()->setSectionsClickable(false); axesTbl->horizontalHeader()->setSectionResizeMode(QHeaderView::Stretch); axesTbl->horizontalHeader()->resizeSection(0, colWidth); axesTbl->verticalHeader()->setSectionsClickable(false); vboxRight->addWidget(axesTbl); hbox->addLayout(vboxLeft); hbox->addLayout(vboxMid); hbox->addLayout(vboxRight); // calibrate button calibrate = new QPushButton(i18n("Calibrate")); connect(calibrate, &QAbstractButton::clicked, this, &JoyWidget::calibrateDevice); calibrate->setEnabled(false); vboxLeft->addStretch(); vboxLeft->addWidget(calibrate); // set up a timer for idle processing of joystick events idle = new QTimer(this); connect(idle, &QTimer::timeout, this, &JoyWidget::checkDevice); // check which devicefiles we have init(); } //-------------------------------------------------------------- JoyWidget::~JoyWidget() { delete joydev; } //-------------------------------------------------------------- void JoyWidget::init() { // check which devicefiles we have int i; bool first = true; char dev[30]; device->clear(); buttonTbl->setRowCount(0); axesTbl->setRowCount(0); for (i = 0; i < 5; i++) // check the first 5 devices { sprintf(dev, "/dev/js%d", i); // first look in /dev JoyDevice *joy = new JoyDevice(dev); if ( joy->open() != JoyDevice::SUCCESS ) { delete joy; sprintf(dev, "/dev/input/js%d", i); // then look in /dev/input joy = new JoyDevice(dev); if ( joy->open() != JoyDevice::SUCCESS ) { delete joy; continue; // try next number } } // we found one device->addItem(QStringLiteral("%1 (%2)").arg(joy->text()).arg(joy->device())); // display values for first device if ( first ) { showDeviceProps(joy); // this sets the joy object into this->joydev first = false; } else delete joy; } /* KDE 4: Remove this check(and i18n) when all KCM wrappers properly test modules */ if ( device->count() == 0 ) { messageBox->show(); messageBox->setText(QStringLiteral("%1").arg( i18n("No joystick device automatically found on this computer.
" "Checks were done in /dev/js[0-4] and /dev/input/js[0-4]
" "If you know that there is one attached, please enter the correct device file."))); } } //-------------------------------------------------------------- void JoyWidget::traceChanged(bool state) { xyPos->showTrace(state); } //-------------------------------------------------------------- void JoyWidget::restoreCurrDev() { if ( !joydev ) // no device open { device->setEditText(QLatin1String("")); calibrate->setEnabled(false); } else { // try to find the current open device in the combobox list int index = device->findText(joydev->device(), Qt::MatchContains); if ( index == -1 ) // the current open device is one the user entered (not in the list) device->setEditText(joydev->device()); else device->setEditText(device->itemText(index)); } } //-------------------------------------------------------------- void JoyWidget::deviceChanged(const QString &dev) { // find "/dev" in given string int start, stop; QString devName; if ( (start = dev.indexOf(QLatin1String("/dev"))) == -1 ) { KMessageBox::sorry(this, i18n("The given device name is invalid (does not contain /dev).\n" "Please select a device from the list or\n" "enter a device file, like /dev/js0."), i18n("Unknown Device")); restoreCurrDev(); return; } - if ( (stop = dev.indexOf(QLatin1String(")"), start)) != -1 ) // seems to be text selected from our list + if ( (stop = dev.indexOf(QLatin1Char(')'), start)) != -1 ) // seems to be text selected from our list devName = dev.mid(start, stop - start); else devName = dev.mid(start); if ( joydev && (devName == joydev->device()) ) return; // user selected the current device; ignore it JoyDevice *joy = new JoyDevice(devName); JoyDevice::ErrorCode ret = joy->open(); if ( ret != JoyDevice::SUCCESS ) { KMessageBox::error(this, joy->errText(ret), i18n("Device Error")); delete joy; restoreCurrDev(); return; } showDeviceProps(joy); } //-------------------------------------------------------------- void JoyWidget::showDeviceProps(JoyDevice *joy) { joydev = joy; buttonTbl->setRowCount(joydev->numButtons()); axesTbl->setRowCount(joydev->numAxes()); if ( joydev->numAxes() >= 2 ) { axesTbl->setVerticalHeaderItem(0, new QTableWidgetItem(i18n("1(x)"))); axesTbl->setVerticalHeaderItem(1, new QTableWidgetItem(i18n("2(y)"))); } calibrate->setEnabled(true); idle->start(0); // make both tables use the same space for header; this looks nicer // TODO: Don't know how to do this in Qt4; the following does no longer work // Probably by setting a sizeHint for every single header item ? /* buttonTbl->verticalHeader()->setFixedWidth(qMax(buttonTbl->verticalHeader()->width(), axesTbl->verticalHeader()->width())); axesTbl->verticalHeader()->setFixedWidth(buttonTbl->verticalHeader()->width()); */ } //-------------------------------------------------------------- void JoyWidget::checkDevice() { if ( !joydev ) return; // no open device yet JoyDevice::EventType type; int number, value; if ( !joydev->getEvent(type, number, value) ) return; if ( type == JoyDevice::BUTTON ) { if ( ! buttonTbl->item(number, 0) ) buttonTbl->setItem(number, 0, new QTableWidgetItem()); if ( value == 0 ) // button release buttonTbl->item(number, 0)->setText(QStringLiteral("-")); else buttonTbl->item(number, 0)->setText(PRESSED); } if ( type == JoyDevice::AXIS ) { if ( number == 0 ) // x-axis xyPos->changeX(value); if ( number == 1 ) // y-axis xyPos->changeY(value); if ( ! axesTbl->item(number, 0) ) axesTbl->setItem(number, 0, new QTableWidgetItem()); axesTbl->item(number, 0)->setText(QStringLiteral("%1").arg(int(value))); } } //-------------------------------------------------------------- void JoyWidget::calibrateDevice() { if ( !joydev ) return; // just to be save JoyDevice::ErrorCode ret = joydev->initCalibration(); if ( ret != JoyDevice::SUCCESS ) { KMessageBox::error(this, joydev->errText(ret), i18n("Communication Error")); return; } if ( KMessageBox::messageBox(this, KMessageBox::Information, i18n("Calibration is about to check the precision.

" "Please move all axes to their center position and then " "do not touch the joystick anymore.

" "Click OK to start the calibration.
"), i18n("Calibration"), KStandardGuiItem::ok(), KStandardGuiItem::cancel()) != KMessageBox::Ok ) return; idle->stop(); // stop the joystick event getting; this must be done inside the calibrate dialog CalDialog dlg(this, joydev); dlg.calibrate(); // user canceled somewhere during calibration, therefore the device is in a bad state if ( dlg.result() == QDialog::Rejected ) joydev->restoreCorr(); idle->start(0); // continue with event getting } //-------------------------------------------------------------- void JoyWidget::resetCalibration() { if ( !joydev ) return; // just to be save JoyDevice::ErrorCode ret = joydev->restoreCorr(); if ( ret != JoyDevice::SUCCESS ) { KMessageBox::error(this, joydev->errText(ret), i18n("Communication Error")); } else { KMessageBox::information(this, i18n("Restored all calibration values for joystick device %1.", joydev->device()), i18n("Calibration Success")); } } //-------------------------------------------------------------- diff --git a/kcms/keyboard/preview/geometry_parser.cpp b/kcms/keyboard/preview/geometry_parser.cpp index 068a7b7d6..b87a3c7bf 100644 --- a/kcms/keyboard/preview/geometry_parser.cpp +++ b/kcms/keyboard/preview/geometry_parser.cpp @@ -1,593 +1,593 @@ /* * Copyright (C) 2013 Shivam Makkar (amourphious1992@gmail.com) * * 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. * * This program 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 General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #include "geometry_parser.h" #include "geometry_components.h" #include "xkb_rules.h" #include #include #include #include #include #include #include namespace grammar { keywords::keywords() { add ("shape", 1) ("height", 2) ("width", 3) ("description", 4) ("keys", 5) ("row", 6) ("section", 7) ("key", 8) ("//", 9) ("/*", 10) ; } template GeometryParser::GeometryParser(): GeometryParser::base_type(start) { using qi::lexeme; using qi::char_; using qi::lit; using qi::_1; using qi::_val; using qi::int_; using qi::double_; using qi::eol; name = '"' >> +(char_ - '"') >> '"'; ignore = (lit("outline") || lit("overlay") || lit("text")) >> *(char_ - lit("};")) >> lit("};") || lit("solid") >> *(char_ - lit("};")) >> lit("};") || lit("indicator") >> *(char_ - ';' - '{') >> ';' || '{' >> *(char_ - lit("};")) >> lit("};") || lit("indicator") >> '.' >> lit("shape") >> '=' >> name >> ';'; comments = lexeme[ lit("//") >> *(char_ - eol || keyword - eol) >> eol || lit("/*") >> *(char_ - lit("*/") || keyword - lit("*/")) >> lit("*/") ]; cordinates = ('[' >> double_[phx::ref(shapeLenX) = _1] >> ',' >> double_[phx::ref(shapeLenY) = _1] >> ']') || '[' >> double_ >> "," >> double_ >> ']' ; cordinatea = '[' >> double_[phx::ref(approxLenX) = _1] >> "," >> double_[phx::ref(approxLenY) = _1] >> ']'; set = '{' >> cordinates >> *(',' >> cordinates) >> '}'; setap = '{' >> cordinatea >> *(',' >> cordinatea) >> '}'; seta = '{' >> cordinates[phx::bind(&GeometryParser::setCord, this)] >> *(',' >> cordinates[phx::bind(&GeometryParser::setCord, this)]) >> '}' ; description = lit("description") >> '=' >> name[phx::bind(&GeometryParser::getDescription, this, _1)] >> ';'; cornerRadius = (lit("cornerRadius") || lit("corner")) >> '=' >> double_; shapeDef = lit("shape") >> name[phx::bind(&GeometryParser::getShapeName, this, _1)] >> '{' >> *(lit("approx") >> '=' >> setap[phx::bind(&GeometryParser::setApprox, this)] >> ',' || cornerRadius >> ',' || comments) >> seta >> *((',' >> (set || lit("approx") >> '=' >> setap[phx::bind(&GeometryParser::setApprox, this)] || cornerRadius) || comments)) >> lit("};") ; keyName = '<' >> +(char_ - '>') >> '>'; keyShape = *(lit("key.")) >> lit("shape") >> '=' >> name[phx::bind(&GeometryParser::setKeyShape, this, _1)] || name[phx::bind(&GeometryParser::setKeyShape, this, _1)]; keyColor = lit("color") >> '=' >> name; keygap = lit("gap") >> '=' >> double_[phx::ref(KeyOffset) = _1] || double_[phx::ref(KeyOffset) = _1]; keyDesc = keyName[phx::bind(&GeometryParser::setKeyNameandShape, this, _1)] || '{' >> (keyName[phx::bind(&GeometryParser::setKeyNameandShape, this, _1)] || keyShape || keygap[phx::bind(&GeometryParser::setKeyOffset, this)] || keyColor) >> *((',' >> (keyName || keyShape || keygap[phx::bind(&GeometryParser::setKeyOffset, this)] || keyColor)) || comments) >> '}'; keys = lit("keys") >> '{' >> keyDesc[phx::bind(&GeometryParser::setKeyCordi, this)] >> *((*lit(',') >> keyDesc[phx::bind(&GeometryParser::setKeyCordi, this)] >> *lit(',')) || comments) >> lit("};"); geomShape = ((lit("key.shape") >> '=' >> name[phx::bind(&GeometryParser::setGeomShape, this, _1)]) || (lit("key.color") >> '=' >> name)) >> ';'; geomLeft = lit("section.left") >> '=' >> double_[phx::ref(geom.sectionLeft) = _1] >> ';'; geomTop = lit("section.top") >> '=' >> double_[phx::ref(geom.sectionTop) = _1] >> ';'; geomRowTop = lit("row.top") >> '=' >> double_[phx::ref(geom.rowTop) = _1] >> ';'; geomRowLeft = lit("row.left") >> '=' >> double_[phx::ref(geom.rowLeft) = _1] >> ';'; geomGap = lit("key.gap") >> '=' >> double_[phx::ref(geom.keyGap) = _1] >> ';'; geomVertical = *lit("row.") >> lit("vertical") >> '=' >> (lit("True") || lit("true")) >> ';'; geomAtt = geomLeft || geomTop || geomRowTop || geomRowLeft || geomGap; top = lit("top") >> '=' >> double_ >> ';'; left = lit("left") >> '=' >> double_ >> ';'; row = lit("row")[phx::bind(&GeometryParser::rowinit, this)] >> '{' >> *(top[phx::bind(&GeometryParser::setRowTop, this, _1)] || left[phx::bind(&GeometryParser::setRowLeft, this, _1)] || localShape[phx::bind(&GeometryParser::setRowShape, this, _1)] || localColor || comments || geomVertical[phx::bind(&GeometryParser::setVerticalRow, this)] || keys ) >> lit("};") || ignore || geomVertical[phx::bind(&GeometryParser::setVerticalSection, this)]; angle = lit("angle") >> '=' >> double_ >> ';'; localShape = lit("key.shape") >> '=' >> name[_val = _1] >> ';'; localColor = lit("key.color") >> '=' >> name >> ';'; localDimension = (lit("height") || lit("width")) >> '=' >> double_ >> ';'; priority = lit("priority") >> '=' >> double_ >> ';'; section = lit("section")[phx::bind(&GeometryParser::sectioninit, this)] >> name[phx::bind(&GeometryParser::sectionName, this, _1)] >> '{' >> *(top[phx::bind(&GeometryParser::setSectionTop, this, _1)] || left[phx::bind(&GeometryParser::setSectionLeft, this, _1)] || angle[phx::bind(&GeometryParser::setSectionAngle, this, _1)] || row[phx::bind(&GeometryParser::addRow, this)] || localShape[phx::bind(&GeometryParser::setSectionShape, this, _1)] || geomAtt || localColor || localDimension || priority || comments) >> lit("};") || geomVertical[phx::bind(&GeometryParser::setVerticalGeometry, this)]; shapeC = lit("shape") >> '.' >> cornerRadius >> ';'; shape = shapeDef || shapeC; input = '{' >> +(width || height || comments || ignore || description || (char_ - keyword - '}' || shape[phx::bind(&Geometry::addShape, &geom)] || section[phx::bind(&Geometry::addSection, &geom)] || geomAtt || geomShape )) >> '}'; width = lit("width") >> '=' >> double_[phx::bind(&Geometry::setWidth, &geom, _1)] >> ";"; height = lit("height") >> '=' >> double_[phx::bind(&Geometry::setHeight, &geom, _1)] >> ";"; start %= *(lit("default")) >> lit("xkb_geometry") >> name[phx::bind(&GeometryParser::getName, this, _1)] >> input >> ';' >> *(comments || char_ - lit("xkb_geometry")); } template void GeometryParser::setCord() { geom.setShapeCord(shapeLenX, shapeLenY); } template void GeometryParser::setSectionShape(std::string n) { geom.sectionList[geom.getSectionCount()].setShapeName(QString::fromUtf8(n.data(), n.size())); } template void GeometryParser::getName(std::string n) { geom.setName(QString::fromUtf8(n.data(), n.size())); } template void GeometryParser::getDescription(std::string n) { geom.setDescription(QString::fromUtf8(n.data(), n.size())); } template void GeometryParser::getShapeName(std::string n) { geom.setShapeName(QString::fromUtf8(n.data(), n.size())); } template void GeometryParser::setGeomShape(std::string n) { geom.setKeyShape(QString::fromUtf8(n.data(), n.size())); } template void GeometryParser::setRowShape(std::string n) { int secn = geom.getSectionCount(); int rown = geom.sectionList[secn].getRowCount(); geom.sectionList[secn].rowList[rown].setShapeName(QString::fromUtf8(n.data(), n.size())); } template void GeometryParser::setApprox() { geom.setShapeApprox(approxLenX, approxLenY); } template void GeometryParser::addRow() { geom.sectionList[geom.getSectionCount()].addRow(); } template void GeometryParser::sectionName(std::string n) { geom.sectionList[geom.getSectionCount()].setName(QString::fromUtf8(n.data(), n.size())); } template void GeometryParser::rowinit() { int secn = geom.getSectionCount(); int rown = geom.sectionList[secn].getRowCount(); double tempTop = geom.sectionList[secn].getTop(); QString tempShape = geom.sectionList[secn].getShapeName(); geom.sectionList[secn].rowList[rown].setTop(tempTop); geom.sectionList[secn].rowList[rown].setLeft(geom.sectionList[secn].getLeft()); geom.sectionList[secn].rowList[rown].setShapeName(tempShape); keyCordiX = geom.sectionList[secn].rowList[rown].getLeft(); keyCordiY = geom.sectionList[secn].rowList[rown].getTop(); tempTop = geom.sectionList[secn].getVertical(); geom.sectionList[secn].rowList[rown].setVertical(tempTop); } template void GeometryParser::sectioninit() { int secn = geom.getSectionCount(); geom.sectionList[secn].setTop(geom.sectionTop); geom.sectionList[secn].setLeft(geom.sectionLeft); keyCordiX = geom.sectionList[secn].getLeft(); keyCordiY = geom.sectionList[secn].getTop(); geom.sectionList[secn].setShapeName(geom.getKeyShape()); geom.sectionList[secn].setVertical(geom.getVertical()); } template void GeometryParser::setRowTop(double a) { int secn = geom.getSectionCount(); int rown = geom.sectionList[secn].getRowCount(); double tempTop = geom.sectionList[secn].getTop(); geom.sectionList[secn].rowList[rown].setTop(a + tempTop); keyCordiY = geom.sectionList[secn].rowList[rown].getTop(); } template void GeometryParser::setRowLeft(double a) { int secn = geom.getSectionCount(); int rown = geom.sectionList[secn].getRowCount(); double tempLeft = geom.sectionList[secn].getLeft(); geom.sectionList[secn].rowList[rown].setLeft(a + tempLeft); keyCordiX = geom.sectionList[secn].rowList[rown].getLeft(); } template void GeometryParser::setSectionTop(double a) { //qCDebug(KEYBOARD_PREVIEW) << "\nsectionCount" << geom.sectionCount; int secn = geom.getSectionCount(); geom.sectionList[secn].setTop(a + geom.sectionTop); keyCordiY = geom.sectionList[secn].getTop(); } template void GeometryParser::setSectionLeft(double a) { //qCDebug(KEYBOARD_PREVIEW) << "\nsectionCount" << geom.sectionCount; int secn = geom.getSectionCount(); geom.sectionList[secn].setLeft(a + geom.sectionLeft); keyCordiX = geom.sectionList[secn].getLeft(); } template void GeometryParser::setSectionAngle(double a) { //qCDebug(KEYBOARD_PREVIEW) << "\nsectionCount" << geom.sectionCount; int secn = geom.getSectionCount(); geom.sectionList[secn].setAngle(a); } template void GeometryParser::setVerticalRow() { int secn = geom.getSectionCount(); int rown = geom.sectionList[secn].getRowCount(); geom.sectionList[secn].rowList[rown].setVertical(1); } template void GeometryParser::setVerticalSection() { int secn = geom.getSectionCount(); geom.sectionList[secn].setVertical(1); } template void GeometryParser::setVerticalGeometry() { geom.setVertical(1); } template void GeometryParser::setKeyName(std::string n) { int secn = geom.getSectionCount(); int rown = geom.sectionList[secn].getRowCount(); int keyn = geom.sectionList[secn].rowList[rown].getKeyCount(); //qCDebug(KEYBOARD_PREVIEW) << "\nsC: " << secn << "\trC: " << rown << "\tkn: " << keyn; geom.sectionList[secn].rowList[rown].keyList[keyn].setKeyName(QString::fromUtf8(n.data(), n.size())); } template void GeometryParser::setKeyShape(std::string n) { int secn = geom.getSectionCount(); int rown = geom.sectionList[secn].getRowCount(); int keyn = geom.sectionList[secn].rowList[rown].getKeyCount(); //qCDebug(KEYBOARD_PREVIEW) << "\nsC: " << secn << "\trC: " << rown << "\tkn: " << keyn; geom.sectionList[secn].rowList[rown].keyList[keyn].setShapeName(QString::fromUtf8(n.data(), n.size())); } template void GeometryParser::setKeyNameandShape(std::string n) { int secn = geom.getSectionCount(); int rown = geom.sectionList[secn].getRowCount(); setKeyName(n); setKeyShape(geom.sectionList[secn].rowList[rown].getShapeName().toUtf8().constData()); } template void GeometryParser::setKeyOffset() { //qCDebug(KEYBOARD_PREVIEW) << "\nhere\n"; int secn = geom.getSectionCount(); int rown = geom.sectionList[secn].getRowCount(); int keyn = geom.sectionList[secn].rowList[rown].getKeyCount(); //qCDebug(KEYBOARD_PREVIEW) << "\nsC: " << secn << "\trC: " << rown << "\tkn: " << keyn; geom.sectionList[secn].rowList[rown].keyList[keyn].setOffset(KeyOffset); } template void GeometryParser::setKeyCordi() { int secn = geom.getSectionCount(); int rown = geom.sectionList[secn].getRowCount(); int keyn = geom.sectionList[secn].rowList[rown].getKeyCount(); int vertical = geom.sectionList[secn].rowList[rown].getVertical(); Key key = geom.sectionList[secn].rowList[rown].keyList[keyn]; if (vertical == 0) { keyCordiX += key.getOffset(); } else { keyCordiY += key.getOffset(); } geom.sectionList[secn].rowList[rown].keyList[keyn].setKeyPosition(keyCordiX, keyCordiY); QString shapeStr = key.getShapeName(); if (shapeStr.isEmpty()) { shapeStr = geom.getKeyShape(); } GShape shapeObj = geom.findShape(shapeStr); int a = shapeObj.size(vertical); if (vertical == 0) { keyCordiX += a + geom.keyGap; } else { keyCordiY += a + geom.keyGap; } geom.sectionList[secn].rowList[rown].addKey(); } Geometry parseGeometry(const QString &model) { using boost::spirit::iso8859_1::space; typedef std::string::const_iterator iterator_type; typedef grammar::GeometryParser GeometryParser; GeometryParser geometryParser; Rules::GeometryId geoId = Rules::getGeometryId(model); QString geometryFile = geoId.fileName; QString geometryName = geoId.geoName; qCDebug(KEYBOARD_PREVIEW) << "looking for model" << model << "geometryName" << geometryName << "in" << geometryFile; QString input = getGeometry(geometryFile, geometryName); if (! input.isEmpty()) { geometryParser.geom = Geometry(); input = includeGeometry(input); std::string parserInput = input.toUtf8().constData(); std::string::const_iterator iter = parserInput.begin(); std::string::const_iterator end = parserInput.end(); bool success = phrase_parse(iter, end, geometryParser, space); if (success && iter == end) { // qCDebug(KEYBOARD_PREVIEW) << "Geometry parsing succeeded for" << input.left(20); geometryParser.geom.setParsing(true); return geometryParser.geom; } else { qCritical() << "Geometry parsing failed for\n\t" << input.left(30); geometryParser.geom.setParsing(false); } } if (geometryParser.geom.getParsing()) { return geometryParser.geom; } qCritical() << "Failed to get geometry" << geometryParser.geom.getName() << "falling back to pc104"; return parseGeometry(QStringLiteral("pc104")); } QString includeGeometry(QString geometry) { - QStringList lines = geometry.split(QStringLiteral("\n")); + QStringList lines = geometry.split(QLatin1Char('\n')); int includeLine = -1; QString includeLineStr; QString startLine = lines[0]; for (int i = 0; i < lines.size(); i++) { includeLineStr = lines[i]; lines[i].remove(QStringLiteral(" ")); lines[i].remove(QStringLiteral("\r")); if (lines[i].startsWith(QLatin1String("include"))) { includeLine = i; break; } } if (includeLine == -1) { return geometry; } geometry.remove(includeLineStr); lines[includeLine].remove(QStringLiteral("include")); lines[includeLine].remove(QStringLiteral("\"")); lines[includeLine].remove(QStringLiteral(")")); - if (lines[includeLine].contains(QLatin1String("("))) { - QString includeFile = lines[includeLine].split(QStringLiteral("("))[0]; - QString includeGeom = lines[includeLine].split(QStringLiteral("("))[1]; + if (lines[includeLine].contains(QLatin1Char('('))) { + QString includeFile = lines[includeLine].split(QLatin1Char('('))[0]; + QString includeGeom = lines[includeLine].split(QLatin1Char('('))[1]; qCDebug(KEYBOARD_PREVIEW) << "looking to include " << "geometryName" << includeGeom << "in" << includeFile; QString includeStr = getGeometry(includeFile, includeGeom); includeStr = getGeometryStrContent(includeStr); geometry.remove(startLine); geometry.prepend(includeStr); geometry.prepend(startLine); includeGeometry(geometry); } return geometry; } QString getGeometryStrContent(QString geometryStr) { int k = geometryStr.indexOf(QLatin1String("{")); int k2 = geometryStr.lastIndexOf(QLatin1String("};")); geometryStr = geometryStr.mid(k + 1, k2 - k - 2); return geometryStr; } QString getGeometry(QString geometryFile, QString geometryName) { QString xkbParentDir = findGeometryBaseDir(); geometryFile.prepend(xkbParentDir); QFile gfile(geometryFile); if (!gfile.open(QIODevice::ReadOnly | QIODevice::Text)) { qCritical() << "Unable to open the file" << geometryFile; return QString(); } QString gcontent = gfile.readAll(); gfile.close(); QStringList gcontentList = gcontent.split(QStringLiteral("xkb_geometry ")); int current = 0; for (int i = 1; i < gcontentList.size(); i++) { if (gcontentList[i].startsWith("\"" + geometryName + "\"")) { current = i; break; } } if (current != 0) { return gcontentList[current].prepend("xkb_geometry "); } else { return QString(); } } QString findGeometryBaseDir() { QString xkbDir = Rules::findXkbDir(); return QStringLiteral("%1/geometry/").arg(xkbDir); } } diff --git a/kcms/keyboard/preview/keysymhelper.cpp b/kcms/keyboard/preview/keysymhelper.cpp index d5d138662..c8c9eaca9 100644 --- a/kcms/keyboard/preview/keysymhelper.cpp +++ b/kcms/keyboard/preview/keysymhelper.cpp @@ -1,83 +1,83 @@ /* * Copyright (C) 2012 Shivam Makkar (amourphious1992@gmail.com) * * 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. * * This program 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 General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #include "keysymhelper.h" #include "keysym2ucs.h" #include #include #include #include KeySymHelper::KeySymHelper() { nill = 0; } QString KeySymHelper::getKeySymbol(const QString &opton) { if (keySymbolMap.contains(opton)) { return keySymbolMap[opton]; } const char *str = opton.toLatin1().data(); #if 0 //TODO: figure out how to use this so we don't need our own symkey2ucs mapping int res = Xutf8LookupString(XIC ic, XKeyPressedEvent * event, char *buffer_return, int bytes_buffer, KeySym * keysym_return, Status * status_return); #else KeySym keysym = XStringToKeysym(str); //TODO: make it more generic // if( keysym == 0xfe03 ) // return "L3"; long ucs = keysym2ucs(keysym); // if( ucs == -1 && (keysym >= 0xFE50 && keysym <= 0xFE5F) ) { // ucs = 0x0300 + (keysym & 0x000F); // qWarning() << "Got dead symbol" << QString("0x%1").arg(keysym, 0, 16) << "named" << opton << "will use" << QString("0x%1").arg(ucs, 0, 16) << "as UCS"; // } if (ucs == -1) { nill++; qWarning() << "No mapping from keysym:" << QStringLiteral("0x%1").arg(keysym, 0, 16) << "named:" << opton << "to UCS"; } QString ucsStr = QString(QChar((int)ucs)); // Combining Diacritical Marks if (ucs >= 0x0300 && ucs <= 0x036F) { - ucsStr = " " + ucsStr + " "; + ucsStr = " " + ucsStr + QLatin1Char(' '); } // qWarning() << "--" << opton << "keysym: " << keysym << QString("0x%1").arg(keysym, 0, 16) << "keysym2string" << XKeysymToString(keysym) // << "---" << QString("0x%1").arg(ucs, 0, 16) << ucsStr; keySymbolMap[opton] = ucsStr; return ucsStr; #endif } diff --git a/kcms/keyboard/preview/symbol_parser.cpp b/kcms/keyboard/preview/symbol_parser.cpp index b608fbdb1..9a4d73830 100644 --- a/kcms/keyboard/preview/symbol_parser.cpp +++ b/kcms/keyboard/preview/symbol_parser.cpp @@ -1,288 +1,288 @@ /* * Copyright (C) 2013 Shivam Makkar (amourphious1992@gmail.com) * * 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. * * This program 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 General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #include "symbol_parser.h" #include "xkb_rules.h" #include #include #include #include #include namespace grammar { symbol_keywords::symbol_keywords() { add("key", 2)("include", 1)("//", 3)("*/", 4); } levels::levels() { add("ONE", 1)("TWO", 2)("THREE", 3)("FOUR", 4)("SIX", 6)("EIGHT", 8); } template SymbolParser::SymbolParser() : SymbolParser::base_type(start) { using qi::lexeme; using qi::char_; using qi::lit; using qi::_1; using qi::_val; using qi::int_; using qi::double_; using qi::eol; newKey = 0; name %= '"' >> +(char_ - '"') >> '"'; group = lit("Group") >> int_; comments = lexeme[lit("//") >> *(char_ - eol || symbolKeyword - eol) >> eol || lit("/*") >> *(char_ - lit("*/") || symbolKeyword - lit("*/")) >> lit("*/")]; include = lit("include") >> name[phx::bind(&SymbolParser::getInclude, this, _1)]; type = lit("type") >> '[' >> group >> lit(']') >> lit('=') >> lit("\"") >> *(char_ - lvl) >> *lvl[phx::bind(&SymbolParser::setLevel, this, _1)] >> *(char_ - lvl - '"') >> lit("\""); symbol = +(char_ - ',' - ']'); symbols = *(lit("symbols") >> '[' >> group >> lit(']') >> lit('=')) >> '[' >> symbol[phx::bind(&SymbolParser::getSymbol, this, _1)] >> *(',' >> symbol[phx::bind(&SymbolParser::getSymbol, this, _1)]) >> ']'; keyName = '<' >> *(char_ - '>') >> '>'; key = (lit("key") >> keyName[phx::bind(&SymbolParser::addKeyName, this, _1)] >> '{' >> *(type >> ',') >> symbols >> *(',' >> type) >> lit("};")) || lit("key") >> lit(".") >> type >> lit(";"); ee = *(char_ - symbolKeyword - '{') >> '{' >> *(char_ - '}' - ';') >> lit("};"); start = *(char_ - lit("xkb_symbols") || comments) >> lit("xkb_symbols") >> name[phx::bind(&SymbolParser::setName, this, _1)] >> '{' >> *(key[phx::bind(&SymbolParser::addKey, this)] || include || ee || char_ - '}' - symbolKeyword || comments) >> lit("};") >> *(comments || char_); } template void SymbolParser::getSymbol(std::string n) { int index = layout.keyList[keyIndex].getSymbolCount(); layout.keyList[keyIndex].addSymbol(QString::fromUtf8(n.data(), n.size()), index); //qCDebug(KEYBOARD_PREVIEW) << "adding symbol: " << QString::fromUtf8(n.data(), n.size()); //qCDebug(KEYBOARD_PREVIEW) << "added symbol: " << layout.keyList[keyIndex].getSymbol(index) << " in " << keyIndex << " at " << index; } template void SymbolParser::addKeyName(std::string n) { QString kname = QString::fromUtf8(n.data(), n.size()); if (kname.startsWith(QLatin1String("Lat"))) { kname = alias.getAlias(layout.country, kname); } keyIndex = layout.findKey(kname); //qCDebug(KEYBOARD_PREVIEW) << layout.getKeyCount(); if (keyIndex == -1) { layout.keyList[layout.getKeyCount()].keyName = kname; keyIndex = layout.getKeyCount(); newKey = 1; } // qCDebug(KEYBOARD_PREVIEW) << "key at" << keyIndex; } template void SymbolParser::addKey() { if (newKey == 1) { layout.addKey(); newKey = 0; //qCDebug(KEYBOARD_PREVIEW) << "new key"; } } template void SymbolParser::getInclude(std::string n) { layout.addInclude(QString::fromUtf8(n.data(), n.size())); } template void SymbolParser::setName(std::string n) { layout.setName(QString::fromUtf8(n.data(), n.size())); //qCDebug(KEYBOARD_PREVIEW) << layout.getLayoutName(); } template void SymbolParser::setLevel(int lvl) { if (lvl > layout.getLevel()) { layout.setLevel(lvl); qCDebug(KEYBOARD_PREVIEW) << lvl; } } QString findSymbolBaseDir() { QString xkbDir = Rules::findXkbDir(); return QStringLiteral("%1/symbols/").arg(xkbDir); } QString findLayout(const QString &layout, const QString &layoutVariant) { QString symbolBaseDir = findSymbolBaseDir(); QString symbolFile = symbolBaseDir.append(layout); QFile sfile(symbolFile); if (!sfile.open(QIODevice::ReadOnly | QIODevice::Text)) { //qCDebug(KEYBOARD_PREVIEW) << "unable to open the file"; return QStringLiteral("I/O ERROR"); } QString scontent = sfile.readAll(); sfile.close(); QStringList scontentList = scontent.split(QStringLiteral("xkb_symbols")); QString variant; QString input; if (layoutVariant.isEmpty()) { input = scontentList.at(1); input.prepend("xkb_symbols"); } else { int current = 1; while (layoutVariant != variant && current < scontentList.size()) { input = scontentList.at(current); QString symbolCont = scontentList.at(current); int index = symbolCont.indexOf(QLatin1String("\"")); symbolCont = symbolCont.mid(index); index = symbolCont.indexOf(QLatin1String("{")); - symbolCont = symbolCont.left(index); + symbolCont.truncate(index); symbolCont.remove(QStringLiteral(" ")); variant = symbolCont.remove(QStringLiteral("\"")); input.prepend("xkb_symbols"); current++; } } return input; } KbLayout parseSymbols(const QString &layout, const QString &layoutVariant) { using boost::spirit::iso8859_1::space; typedef std::string::const_iterator iterator_type; typedef grammar::SymbolParser SymbolParser; SymbolParser symbolParser; symbolParser.layout.country = layout; QString input = findLayout(layout, layoutVariant); if (input == QLatin1String("I/O ERROR")) { symbolParser.layout.setParsedSymbol(false); return symbolParser.layout; } std::string parserInput = input.toUtf8().constData(); std::string::const_iterator iter = parserInput.begin(); std::string::const_iterator end = parserInput.end(); bool success = phrase_parse(iter, end, symbolParser, space); if (success && iter == end) { qCDebug(KEYBOARD_PREVIEW) << "Symbols Parsing succeeded"; symbolParser.layout.setParsedSymbol(true); } else { qWarning() << "Symbols Parsing failed\n" << input; symbolParser.layout.setParsedSymbol(false); } for (int currentInclude = 0; currentInclude < symbolParser.layout.getIncludeCount(); currentInclude++) { QString include = symbolParser.layout.getInclude(currentInclude); - QStringList includeFile = include.split(QStringLiteral("(")); + QStringList includeFile = include.split(QLatin1Char('(')); if (includeFile.size() == 2) { QString file = includeFile.at(0); QString layout = includeFile.at(1); layout.remove(QStringLiteral(")")); input = findLayout(file, layout); } else { QString a; a.clear(); input = findLayout(includeFile.at(0), a); } parserInput = input.toUtf8().constData(); std::string::const_iterator iter = parserInput.begin(); std::string::const_iterator end = parserInput.end(); success = phrase_parse(iter, end, symbolParser, space); if (success && iter == end) { qCDebug(KEYBOARD_PREVIEW) << "Symbols Parsing succeeded"; symbolParser.layout.setParsedSymbol(true); } else { qCDebug(KEYBOARD_PREVIEW) << "Symbols Parsing failed\n"; qCDebug(KEYBOARD_PREVIEW) << input; symbolParser.layout.setParsedSymbol(false); } } //s.layout.display(); if (symbolParser.layout.getParsedSymbol()) { return symbolParser.layout; } else { return parseSymbols(QStringLiteral("us"), QStringLiteral("basic")); } } } diff --git a/kcms/keyboard/x11_helper.h b/kcms/keyboard/x11_helper.h index 23b03e3e7..a7c2f0589 100644 --- a/kcms/keyboard/x11_helper.h +++ b/kcms/keyboard/x11_helper.h @@ -1,215 +1,215 @@ /* * Copyright (C) 2010 Andriy Rysin (rysin@kde.org) * * 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. * * This program 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 General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #ifndef X11_HELPER_H_ #define X11_HELPER_H_ #include #include #include #include #include #include #define explicit explicit_is_keyword_in_cpp #include #include #undef explicit namespace { typedef union { /* All XKB events share these fields. */ struct { uint8_t response_type; uint8_t xkbType; uint16_t sequence; xcb_timestamp_t time; uint8_t deviceID; } any; xcb_xkb_new_keyboard_notify_event_t new_keyboard_notify; xcb_xkb_map_notify_event_t map_notify; xcb_xkb_state_notify_event_t state_notify; } _xkb_event; } class XEventNotifier : public QObject, public QAbstractNativeEventFilter { Q_OBJECT Q_SIGNALS: void layoutChanged(); void layoutMapChanged(); public: XEventNotifier(); ~XEventNotifier() override {} virtual void start(); virtual void stop(); protected: // bool x11Event(XEvent * e); virtual bool processOtherEvents(xcb_generic_event_t* e); virtual bool processXkbEvents(xcb_generic_event_t* e); bool nativeEventFilter(const QByteArray &eventType, void *message, long *) override; private: int registerForXkbEvents(Display* display); bool isXkbEvent(xcb_generic_event_t* event); bool isGroupSwitchEvent(_xkb_event* event); bool isLayoutSwitchEvent(_xkb_event* event); int xkbOpcode; }; struct XkbConfig { QString keyboardModel; QStringList layouts; QStringList variants; QStringList options; bool isValid() { return ! layouts.empty(); } }; class LayoutUnit { public: static const int MAX_LABEL_LENGTH; LayoutUnit() {} explicit LayoutUnit(const QString& fullLayoutName); LayoutUnit(const QString& layout, const QString& variant) { m_layout = layout; m_variant = variant; } /*explicit*/ LayoutUnit(const LayoutUnit& other) { operator=(other); } LayoutUnit &operator=(const LayoutUnit &other) { if (this != &other) { m_layout = other.m_layout; m_variant = other.m_variant; displayName = other.displayName; shortcut = other.shortcut; } return *this; } QString getRawDisplayName() const { return displayName; } QString getDisplayName() const { return !displayName.isEmpty() ? displayName : m_layout; } void setDisplayName(const QString& name) { displayName = name; } void setShortcut(const QKeySequence& shortcut) { this->shortcut = shortcut; } QKeySequence getShortcut() const { return shortcut; } QString layout() const { return m_layout; } void setLayout(const QString &layout) { m_layout = layout; } QString variant() const { return m_variant; } void setVariant(const QString &variant) { m_variant = variant; } bool isEmpty() const { return m_layout.isEmpty(); } bool isValid() const { return ! isEmpty(); } bool operator==(const LayoutUnit& layoutItem) const { // FIXME: why not compare the other properties? return m_layout == layoutItem.m_layout && m_variant == layoutItem.m_variant; } bool operator!=(const LayoutUnit& layoutItem) const { return ! (*this == layoutItem); } QString toString() const; private: QString displayName; QKeySequence shortcut; QString m_layout; QString m_variant; }; struct LayoutSet { QList layouts; LayoutUnit currentLayout; LayoutSet() {} LayoutSet(const LayoutSet& other) { operator=(other); } bool isValid() const { return currentLayout.isValid() && layouts.contains(currentLayout); } bool operator == (const LayoutSet& currentLayouts) const { return this->layouts == currentLayouts.layouts && this->currentLayout == currentLayouts.currentLayout; } LayoutSet& operator = (const LayoutSet& currentLayouts) { this->layouts = currentLayouts.layouts; this->currentLayout = currentLayouts.currentLayout; return *this; } QString toString() const { QString str(currentLayout.toString()); str += QLatin1String(": "); foreach(const LayoutUnit& layoutUnit, layouts) { - str += layoutUnit.toString() + " "; + str += layoutUnit.toString() + QLatin1Char(' '); } return str; } static QString toString(const QList& layoutUnits) { QString str; foreach(const LayoutUnit& layoutUnit, layoutUnits) { - str += layoutUnit.toString() + ","; + str += layoutUnit.toString() + QLatin1Char(','); } return str; } }; class X11Helper { public: static const int MAX_GROUP_COUNT; static const int ARTIFICIAL_GROUP_LIMIT_COUNT; static const char LEFT_VARIANT_STR[]; static const char RIGHT_VARIANT_STR[]; static bool xkbSupported(int* xkbOpcode); static void switchToNextLayout(); static void scrollLayouts(int delta); static bool isDefaultLayout(); static bool setDefaultLayout(); static bool setLayout(const LayoutUnit& layout); static LayoutUnit getCurrentLayout(); static LayoutSet getCurrentLayouts(); static QList getLayoutsList(); static QStringList getLayoutsListAsString(const QList& layoutsList); enum FetchType { ALL, LAYOUTS_ONLY, MODEL_ONLY }; static bool getGroupNames(Display* dpy, XkbConfig* xkbConfig, FetchType fetchType); private: static unsigned int getGroup(); static bool setGroup(unsigned int group); }; #endif /* X11_HELPER_H_ */ diff --git a/kcms/keyboard/xkb_helper.cpp b/kcms/keyboard/xkb_helper.cpp index 969614536..4a2948048 100644 --- a/kcms/keyboard/xkb_helper.cpp +++ b/kcms/keyboard/xkb_helper.cpp @@ -1,184 +1,184 @@ /* * Copyright (C) 2010 Andriy Rysin (rysin@kde.org) * * 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. * * This program 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 General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #include "xkb_helper.h" #include "debug.h" #include #include #include #include #include #include #include #include #include #include "keyboard_config.h" static const char SETXKBMAP_EXEC[] = "setxkbmap"; static const char XMODMAP_EXEC[] = "xmodmap"; static bool setxkbmapNotFound = false; static QString setxkbmapExe; static bool xmodmapNotFound = false; static QString xmodmapExe; static const QString COMMAND_OPTIONS_SEPARATOR(QStringLiteral(",")); static QString getSetxkbmapExe() { if( setxkbmapNotFound ) return QLatin1String(""); if( setxkbmapExe.isEmpty() ) { setxkbmapExe = QStandardPaths::findExecutable(SETXKBMAP_EXEC); if( setxkbmapExe.isEmpty() ) { setxkbmapNotFound = true; qCCritical(KCM_KEYBOARD) << "Can't find" << SETXKBMAP_EXEC << "- keyboard layouts won't be configured"; return QLatin1String(""); } } return setxkbmapExe; } static void executeXmodmap(const QString& configFileName) { if( xmodmapNotFound ) return; if( QFile(configFileName).exists() ) { if( xmodmapExe.isEmpty() ) { xmodmapExe = QStandardPaths::findExecutable(XMODMAP_EXEC); if( xmodmapExe.isEmpty() ) { xmodmapNotFound = true; qCCritical(KCM_KEYBOARD) << "Can't find" << XMODMAP_EXEC << "- xmodmap files won't be run"; return; } } KProcess xmodmapProcess; xmodmapProcess << xmodmapExe; xmodmapProcess << configFileName; - qCDebug(KCM_KEYBOARD) << "Executing" << xmodmapProcess.program().join(QLatin1String(" ")); + qCDebug(KCM_KEYBOARD) << "Executing" << xmodmapProcess.program().join(QLatin1Char(' ')); if( xmodmapProcess.execute() != 0 ) { qCCritical(KCM_KEYBOARD) << "Failed to execute " << xmodmapProcess.program(); } } } static void restoreXmodmap() { // TODO: is just home .Xmodmap enough or should system be involved too? // QString configFileName = QDir("/etc/X11/xinit").filePath(".Xmodmap"); // executeXmodmap(configFileName); QString configFileName = QDir::home().filePath(QStringLiteral(".Xmodmap")); executeXmodmap(configFileName); } //TODO: make private bool XkbHelper::runConfigLayoutCommand(const QStringList& setxkbmapCommandArguments) { QTime timer; timer.start(); KProcess setxkbmapProcess; setxkbmapProcess << getSetxkbmapExe() << setxkbmapCommandArguments; int res = setxkbmapProcess.execute(); if( res == 0 ) { // restore Xmodmap mapping reset by setxkbmap - qCDebug(KCM_KEYBOARD) << "Executed successfully in " << timer.elapsed() << "ms" << setxkbmapProcess.program().join(QLatin1String(" ")); + qCDebug(KCM_KEYBOARD) << "Executed successfully in " << timer.elapsed() << "ms" << setxkbmapProcess.program().join(QLatin1Char(' ')); restoreXmodmap(); qCDebug(KCM_KEYBOARD) << "\t and with xmodmap" << timer.elapsed() << "ms"; return true; } else { - qCCritical(KCM_KEYBOARD) << "Failed to run" << setxkbmapProcess.program().join(QLatin1String(" ")) << "return code:" << res; + qCCritical(KCM_KEYBOARD) << "Failed to run" << setxkbmapProcess.program().join(QLatin1Char(' ')) << "return code:" << res; } return false; } bool XkbHelper::initializeKeyboardLayouts(const QList& layoutUnits) { QStringList layouts; QStringList variants; foreach (const LayoutUnit& layoutUnit, layoutUnits) { layouts.append(layoutUnit.layout()); variants.append(layoutUnit.variant()); } QStringList setxkbmapCommandArguments; setxkbmapCommandArguments.append(QStringLiteral("-layout")); setxkbmapCommandArguments.append(layouts.join(COMMAND_OPTIONS_SEPARATOR)); if( ! variants.join(QLatin1String("")).isEmpty() ) { setxkbmapCommandArguments.append(QStringLiteral("-variant")); setxkbmapCommandArguments.append(variants.join(COMMAND_OPTIONS_SEPARATOR)); } return runConfigLayoutCommand(setxkbmapCommandArguments); } bool XkbHelper::initializeKeyboardLayouts(KeyboardConfig& config) { QStringList setxkbmapCommandArguments; if( ! config.keyboardModel.isEmpty() ) { XkbConfig xkbConfig; X11Helper::getGroupNames(QX11Info::display(), &xkbConfig, X11Helper::MODEL_ONLY); if( xkbConfig.keyboardModel != config.keyboardModel ) { setxkbmapCommandArguments.append(QStringLiteral("-model")); setxkbmapCommandArguments.append(config.keyboardModel); } } if( config.configureLayouts ) { QStringList layouts; QStringList variants; QList defaultLayouts = config.getDefaultLayouts(); foreach (const LayoutUnit& layoutUnit, defaultLayouts) { layouts.append(layoutUnit.layout()); variants.append(layoutUnit.variant()); } setxkbmapCommandArguments.append(QStringLiteral("-layout")); setxkbmapCommandArguments.append(layouts.join(COMMAND_OPTIONS_SEPARATOR)); if( ! variants.join(QLatin1String("")).isEmpty() ) { setxkbmapCommandArguments.append(QStringLiteral("-variant")); setxkbmapCommandArguments.append(variants.join(COMMAND_OPTIONS_SEPARATOR)); } } if( config.resetOldXkbOptions ) { setxkbmapCommandArguments.append(QStringLiteral("-option")); } if( ! config.xkbOptions.isEmpty() ) { setxkbmapCommandArguments.append(QStringLiteral("-option")); setxkbmapCommandArguments.append(config.xkbOptions.join(COMMAND_OPTIONS_SEPARATOR)); } if( ! setxkbmapCommandArguments.isEmpty() ) { return runConfigLayoutCommand(setxkbmapCommandArguments); if( config.configureLayouts ) { X11Helper::setDefaultLayout(); } } return false; } diff --git a/kcms/keyboard/xkb_rules.cpp b/kcms/keyboard/xkb_rules.cpp index 64b4777e0..03dc76e91 100644 --- a/kcms/keyboard/xkb_rules.cpp +++ b/kcms/keyboard/xkb_rules.cpp @@ -1,495 +1,495 @@ /* * Copyright (C) 2010 Andriy Rysin (rysin@kde.org) * * 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. * * This program 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 General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #include "xkb_rules.h" #include "config-workspace.h" #include "debug.h" #include #include #include #include // for Qt::escape #include #include //#include //#include #include "x11_helper.h" // for findXkbRuleFile #include #include #include #include #include #include #include class RulesHandler : public QXmlDefaultHandler { public: RulesHandler(Rules* rules_, bool fromExtras_): rules(rules_), fromExtras(fromExtras_){} bool startElement(const QString &namespaceURI, const QString &localName, const QString &qName, const QXmlAttributes &attributes) override; bool endElement(const QString &namespaceURI, const QString &localName, const QString &qName) override; bool characters(const QString &str) override; // bool fatalError(const QXmlParseException &exception); // QString errorString() const; private: // QString getString(const QString& text); QStringList path; Rules* rules; const bool fromExtras; }; static QString translate_xml_item(const QString& itemText) { if (itemText.isEmpty()) { // i18n warns on empty input strings return itemText; } //messages are already extracted from the source XML files by xkb //the characters '<' and '>' (but not '"') are HTML-escaped in the xkeyboard-config translation files, so we need to convert them before/after looking up the translation //note that we cannot use QString::toHtmlEscaped() here because that would convert '"' as well QString msgid(itemText); - return i18nd("xkeyboard-config", msgid.replace(QLatin1Literal("<"), QLatin1Literal("<")).replace(QLatin1Literal(">"), QLatin1Literal(">")).toUtf8()).replace(QLatin1Literal("<"), QLatin1Literal("<")).replace(QLatin1Literal(">"), QLatin1Literal(">")); + return i18nd("xkeyboard-config", msgid.replace(QLatin1String("<"), QLatin1String("<")).replace(QLatin1String(">"), QLatin1String(">")).toUtf8()).replace(QLatin1String("<"), QLatin1String("<")).replace(QLatin1String(">"), QLatin1String(">")); } static QString translate_description(ConfigItem* item) { return item->description.isEmpty() ? item->name : translate_xml_item(item->description); } static bool notEmpty(const ConfigItem* item) { return ! item->name.isEmpty(); } template void removeEmptyItems(QList& list) { #ifdef __GNUC__ #if __GNUC__ == 4 && (__GNUC_MINOR__ == 8 && __GNUC_PATCHLEVEL__ < 3) || (__GNUC_MINOR__ == 7 && __GNUC_PATCHLEVEL__ < 4) #warning Compiling with a workaround for GCC < 4.8.3 || GCC < 4.7.4 http://gcc.gnu.org/bugzilla/show_bug.cgi?id=58800 Q_FOREACH(T* x, list) { ConfigItem *y = static_cast(x); if (y->name.isEmpty()) { list.removeAll(x); } } #else QtConcurrent::blockingFilter(list, notEmpty); #endif #endif } static void postProcess(Rules* rules) { //TODO remove elements with empty names to safeguard us removeEmptyItems(rules->layoutInfos); removeEmptyItems(rules->modelInfos); removeEmptyItems(rules->optionGroupInfos); // setlocale(LC_ALL, ""); // bindtextdomain("xkeyboard-config", LOCALE_DIR); foreach(ModelInfo* modelInfo, rules->modelInfos) { modelInfo->vendor = translate_xml_item(modelInfo->vendor); modelInfo->description = translate_description(modelInfo); } foreach(LayoutInfo* layoutInfo, rules->layoutInfos) { layoutInfo->description = translate_description(layoutInfo); removeEmptyItems(layoutInfo->variantInfos); foreach(VariantInfo* variantInfo, layoutInfo->variantInfos) { variantInfo->description = translate_description(variantInfo); } } foreach(OptionGroupInfo* optionGroupInfo, rules->optionGroupInfos) { optionGroupInfo->description = translate_description(optionGroupInfo); removeEmptyItems(optionGroupInfo->optionInfos); foreach(OptionInfo* optionInfo, optionGroupInfo->optionInfos) { optionInfo->description = translate_description(optionInfo); } } } Rules::Rules(): version(QStringLiteral("1.0")) { } QString Rules::getRulesName() { if (!QX11Info::isPlatformX11()) { return QString(); } XkbRF_VarDefsRec vd; char *tmp = nullptr; if (XkbRF_GetNamesProp(QX11Info::display(), &tmp, &vd) && tmp != nullptr ) { // qCDebug(KCM_KEYBOARD) << "namesprop" << tmp ; const QString name(tmp); XFree(tmp); return name; } return {}; } QString Rules::findXkbDir() { return QStringLiteral(XKBDIR); } static QString findXkbRulesFile() { QString rulesFile; QString rulesName = Rules::getRulesName(); const QString xkbDir = Rules::findXkbDir(); if ( ! rulesName.isNull() ) { rulesFile = QStringLiteral("%1/rules/%2.xml").arg(xkbDir, rulesName); } else { // default to evdev rulesFile = QStringLiteral("%1/rules/evdev.xml").arg(xkbDir); } return rulesFile; } static void mergeRules(Rules* rules, Rules* extraRules) { rules->modelInfos.append( extraRules->modelInfos ); rules->optionGroupInfos.append( extraRules->optionGroupInfos ); // need to iterate and merge? QList layoutsToAdd; foreach(LayoutInfo* extraLayoutInfo, extraRules->layoutInfos) { LayoutInfo* layoutInfo = findByName(rules->layoutInfos, extraLayoutInfo->name); if( layoutInfo != nullptr ) { layoutInfo->variantInfos.append( extraLayoutInfo->variantInfos ); layoutInfo->languages.append( extraLayoutInfo->languages ); } else { layoutsToAdd.append(extraLayoutInfo); } } rules->layoutInfos.append(layoutsToAdd); qCDebug(KCM_KEYBOARD) << "Merged from extra rules:" << extraRules->layoutInfos.size() << "layouts," << extraRules->modelInfos.size() << "models," << extraRules->optionGroupInfos.size() << "option groups"; // base rules now own the objects - remove them from extra rules so that it does not try to delete them extraRules->layoutInfos.clear(); extraRules->modelInfos.clear(); extraRules->optionGroupInfos.clear(); } const char Rules::XKB_OPTION_GROUP_SEPARATOR = ':'; Rules* Rules::readRules(ExtrasFlag extrasFlag) { Rules* rules = new Rules(); QString rulesFile = findXkbRulesFile(); if( ! readRules(rules, rulesFile, false) ) { delete rules; return nullptr; } if( extrasFlag == Rules::READ_EXTRAS ) { QRegExp regex(QStringLiteral("\\.xml$")); Rules* rulesExtra = new Rules(); QString extraRulesFile = rulesFile.replace(regex, QStringLiteral(".extras.xml")); if( readRules(rulesExtra, extraRulesFile, true) ) { // not fatal if it fails mergeRules(rules, rulesExtra); } delete rulesExtra; } return rules; } Rules* Rules::readRules(Rules* rules, const QString& filename, bool fromExtras) { QFile file(filename); if( !file.open(QFile::ReadOnly | QFile::Text) ) { qCCritical(KCM_KEYBOARD) << "Cannot open the rules file" << file.fileName(); return nullptr; } RulesHandler rulesHandler(rules, fromExtras); QXmlSimpleReader reader; reader.setContentHandler(&rulesHandler); reader.setErrorHandler(&rulesHandler); QXmlInputSource xmlInputSource(&file); qCDebug(KCM_KEYBOARD) << "Parsing xkb rules from" << file.fileName(); if( ! reader.parse(xmlInputSource) ) { qCCritical(KCM_KEYBOARD) << "Failed to parse the rules file" << file.fileName(); return nullptr; } postProcess(rules); return rules; } bool RulesHandler::startElement(const QString &/*namespaceURI*/, const QString &/*localName*/, const QString &qName, const QXmlAttributes &attributes) { path << QString(qName); QString strPath = path.join(QLatin1String("/")); if( strPath.endsWith(QLatin1String("layoutList/layout/configItem")) ) { rules->layoutInfos << new LayoutInfo(fromExtras); } else if( strPath.endsWith(QLatin1String("layoutList/layout/variantList/variant")) ) { rules->layoutInfos.last()->variantInfos << new VariantInfo(fromExtras); } else if( strPath.endsWith(QLatin1String("modelList/model")) ) { rules->modelInfos << new ModelInfo(); } else if( strPath.endsWith(QLatin1String("optionList/group")) ) { rules->optionGroupInfos << new OptionGroupInfo(); rules->optionGroupInfos.last()->exclusive = (attributes.value(QStringLiteral("allowMultipleSelection")) != QLatin1String("true")); } else if( strPath.endsWith(QLatin1String("optionList/group/option")) ) { rules->optionGroupInfos.last()->optionInfos << new OptionInfo(); } else if( strPath == ("xkbConfigRegistry") && ! attributes.value(QStringLiteral("version")).isEmpty() ) { rules->version = attributes.value(QStringLiteral("version")); qCDebug(KCM_KEYBOARD) << "xkbConfigRegistry version" << rules->version; } return true; } bool RulesHandler::endElement(const QString &/*namespaceURI*/, const QString &/*localName*/, const QString &/*qName*/) { path.removeLast(); return true; } bool RulesHandler::characters(const QString &str) { if( !str.trimmed().isEmpty() ) { QString strPath = path.join(QLatin1String("/")); if( strPath.endsWith(QLatin1String("layoutList/layout/configItem/name")) ) { if( rules->layoutInfos.last() != nullptr ) { rules->layoutInfos.last()->name = str.trimmed(); // qCDebug(KCM_KEYBOARD) << "name:" << str; } // skipping invalid entry } else if( strPath.endsWith(QLatin1String("layoutList/layout/configItem/description")) ) { rules->layoutInfos.last()->description = str.trimmed(); // qCDebug(KCM_KEYBOARD) << "descr:" << str; } else if( strPath.endsWith(QLatin1String("layoutList/layout/configItem/languageList/iso639Id")) ) { rules->layoutInfos.last()->languages << str.trimmed(); // qCDebug(KCM_KEYBOARD) << "\tlang:" << str; } else if( strPath.endsWith(QLatin1String("layoutList/layout/variantList/variant/configItem/name")) ) { rules->layoutInfos.last()->variantInfos.last()->name = str.trimmed(); // qCDebug(KCM_KEYBOARD) << "\tvariant name:" << str; } else if( strPath.endsWith(QLatin1String("layoutList/layout/variantList/variant/configItem/description")) ) { rules->layoutInfos.last()->variantInfos.last()->description = str.trimmed(); // qCDebug(KCM_KEYBOARD) << "\tvariant descr:" << str; } else if( strPath.endsWith(QLatin1String("layoutList/layout/variantList/variant/configItem/languageList/iso639Id")) ) { rules->layoutInfos.last()->variantInfos.last()->languages << str.trimmed(); // qCDebug(KCM_KEYBOARD) << "\tvlang:" << str; } else if( strPath.endsWith(QLatin1String("modelList/model/configItem/name")) ) { rules->modelInfos.last()->name = str.trimmed(); // qCDebug(KCM_KEYBOARD) << "name:" << str; } else if( strPath.endsWith(QLatin1String("modelList/model/configItem/description")) ) { rules->modelInfos.last()->description = str.trimmed(); // qCDebug(KCM_KEYBOARD) << "\tdescr:" << str; } else if( strPath.endsWith(QLatin1String("modelList/model/configItem/vendor")) ) { rules->modelInfos.last()->vendor = str.trimmed(); // qCDebug(KCM_KEYBOARD) << "\tvendor:" << str; } else if( strPath.endsWith(QLatin1String("optionList/group/configItem/name")) ) { rules->optionGroupInfos.last()->name = str.trimmed(); // qCDebug(KCM_KEYBOARD) << "name:" << str; } else if( strPath.endsWith(QLatin1String("optionList/group/configItem/description")) ) { rules->optionGroupInfos.last()->description = str.trimmed(); // qCDebug(KCM_KEYBOARD) << "\tdescr:" << str; } else if( strPath.endsWith(QLatin1String("optionList/group/option/configItem/name")) ) { rules->optionGroupInfos.last()->optionInfos.last()->name = str.trimmed(); // qCDebug(KCM_KEYBOARD) << "name:" << str; } else if( strPath.endsWith(QLatin1String("optionList/group/option/configItem/description")) ) { rules->optionGroupInfos.last()->optionInfos.last()->description = str.trimmed(); // qCDebug(KCM_KEYBOARD) << "\tdescr:" << str; } } return true; } bool LayoutInfo::isLanguageSupportedByLayout(const QString& lang) const { if( languages.contains(lang) || isLanguageSupportedByVariants(lang) ) return true; // // return yes if no languages found in layout or its variants // if( languages.empty() ) { // foreach(const VariantInfo* info, variantInfos) { // if( ! info->languages.empty() ) // return false; // } // return true; // } return false; } bool LayoutInfo::isLanguageSupportedByVariants(const QString& lang) const { foreach(const VariantInfo* info, variantInfos) { if( info->languages.contains(lang) ) return true; } return false; } bool LayoutInfo::isLanguageSupportedByDefaultVariant(const QString& lang) const { if( languages.contains(lang) ) return true; if( languages.empty() && isLanguageSupportedByVariants(lang) ) return true; return false; } bool LayoutInfo::isLanguageSupportedByVariant(const VariantInfo* variantInfo, const QString& lang) const { if( variantInfo->languages.contains(lang) ) return true; // if variant has no languages try to "inherit" them from layout if( variantInfo->languages.empty() && languages.contains(lang) ) return true; return false; } #ifdef NEW_GEOMETRY Rules::GeometryId Rules::getGeometryId(const QString& model) { QString xkbDir = Rules::findXkbDir(); QString rulesName = Rules::getRulesName(); QString ruleFileName = QStringLiteral("%1/rules/%2").arg(xkbDir, rulesName); QFile ruleFile(ruleFileName); GeometryId defaultGeoId(QStringLiteral("pc"), QStringLiteral("pc104")); if ( ! ruleFile.open(QIODevice::ReadOnly | QIODevice::Text) ){ qCCritical(KCM_KEYBOARD) << "Unable to open file" << ruleFileName; return defaultGeoId; } QString modelGeoId = model; bool inTable = false; QTextStream in(&ruleFile); while (!in.atEnd()) { QString line = in.readLine().trimmed(); if( line.isEmpty() || QRegExp(QStringLiteral("^\\s*//")).indexIn(line) != -1 ) continue; QRegExp modelGroupRegex(QStringLiteral("!\\s*(\\$[a-zA-Z0-9_]+)\\s*=(.*)")); if( modelGroupRegex.indexIn(line) != -1 ) { QStringList parts = modelGroupRegex.capturedTexts(); QString groupName = parts[1]; QStringList models = parts[2].split(QRegExp(QStringLiteral("\\s+")), QString::SkipEmptyParts); // qCDebug(KCM_KEYBOARD) << "modelGroup definition" << groupName << ":" << models; if( models.contains(model) ) { modelGeoId = groupName; } continue; } if( inTable ) { QRegExp modelTableEntry (QStringLiteral("\\s*(\\$?[a-zA-Z0-9_]+|\\*)\\s*=\\s*([a-zA-Z0-9_]+)\\(([a-zA-Z0-9_%]+)\\)")); if( modelTableEntry.indexIn(line) == -1 ) { if( QRegExp(QStringLiteral("^!\\s*")).indexIn(line) != -1 ) break; qCWarning(KCM_KEYBOARD) << "could not parse geometry line" << line; continue; } QStringList parts = modelTableEntry.capturedTexts(); QString modelName = parts[1]; QString fileName = parts[2]; QString geoName = parts[3]; if( geoName == QLatin1String("%m") ) { geoName = model; } if( modelName == QLatin1String("*") ) { defaultGeoId = GeometryId(fileName, geoName); } // qCDebug(KCM_KEYBOARD) << "geo entry" << modelName << fileName << geoName; if( modelName == model ) { return GeometryId(fileName, geoName); } continue; } QRegExp modelTableHeader (QStringLiteral("!\\s+model\\s*=\\s*geometry")); if( modelTableHeader.indexIn(line) != -1 ) { inTable = true; continue; } } return defaultGeoId; } #endif diff --git a/kcms/keys/kglobalshortcutseditor.cpp b/kcms/keys/kglobalshortcutseditor.cpp index f06c116d6..cfa348e34 100644 --- a/kcms/keys/kglobalshortcutseditor.cpp +++ b/kcms/keys/kglobalshortcutseditor.cpp @@ -1,847 +1,847 @@ /* * Copyright 2008 Michael Jansen * * 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. * * This program 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 General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #include "kglobalshortcutseditor.h" #include "ui_kglobalshortcutseditor.h" #include "ui_select_application.h" #include "export_scheme_dialog.h" #include "select_scheme_dialog.h" #include "globalshortcuts.h" #include "kglobalaccel_interface.h" #include "kglobalaccel_component_interface.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include /* * README * * This class was created because the kshortcutseditor class has some shortcomings. That class uses * QTreeWidget and therefore makes it impossible for an outsider to switch the models. But the * global shortcuts editor did that. Each global component ( kded, krunner, kopete ... ) was * destined to be separately edited. If you selected another component the kshortcutseditor was * cleared and refilled. But the items take care of undoing. Therefore when switching the component * you lost the undo history. * * To solve that problem this class keeps one kshortcuteditor for each component. That is easier * than rewrite that dialog to a model/view framework. * * It perfectly covers a bug of KExtedableItemDelegate when clearing and refilling the associated * model. */ class ComponentData { public: ComponentData( const QString &uniqueName, const QDBusObjectPath &path, KShortcutsEditor *_editor); ~ComponentData(); QString uniqueName() const; KShortcutsEditor *editor(); QDBusObjectPath dbusPath(); private: QString _uniqueName; QDBusObjectPath _path; QPointer _editor; }; ComponentData::ComponentData( const QString &uniqueName, const QDBusObjectPath &path, KShortcutsEditor *editor) : _uniqueName(uniqueName), _path(path), _editor(editor) {} ComponentData::~ComponentData() { delete _editor; _editor = nullptr; } QString ComponentData::uniqueName() const { return _uniqueName; } QDBusObjectPath ComponentData::dbusPath() { return _path; } KShortcutsEditor *ComponentData::editor() { return _editor; } class KGlobalShortcutsEditor::KGlobalShortcutsEditorPrivate { public: KGlobalShortcutsEditorPrivate(KGlobalShortcutsEditor *q) : q(q), bus(QDBusConnection::sessionBus()) {} //! Setup the gui void initGUI(); //! Load the component at @a componentPath bool loadComponent(const QDBusObjectPath &componentPath); //! Return the componentPath for component QDBusObjectPath componentPath(const QString &componentUnique); //! Remove the component void removeComponent(const QString &componentUnique); KGlobalShortcutsEditor *q; Ui::KGlobalShortcutsEditor ui; Ui::SelectApplicationDialog selectApplicationDialogUi; QDialog *selectApplicationDialog = nullptr; QStackedWidget *stack = nullptr; KShortcutsEditor::ActionTypes actionTypes; QHash components; QDBusConnection bus; QStandardItemModel *model = nullptr; KCategorizedSortFilterProxyModel *proxyModel = nullptr; }; void loadAppsCategory(KServiceGroup::Ptr group, QStandardItemModel *model, QStandardItem *item) { if (group && group->isValid()) { KServiceGroup::List list = group->entries(); for( KServiceGroup::List::ConstIterator it = list.constBegin(); it != list.constEnd(); ++it) { const KSycocaEntry::Ptr p = (*it); if (p->isType(KST_KService)) { const KService::Ptr service(static_cast(p.data())); if (!service->noDisplay()) { QString genericName = service->genericName(); if (genericName.isNull()) { genericName = service->comment(); } QString description; if (!service->genericName().isEmpty() && service->genericName() != service->name()) { description = service->genericName(); } else if (!service->comment().isEmpty()) { description = service->comment(); } QStandardItem *subItem = new QStandardItem(QIcon::fromTheme(service->icon()), service->name()); subItem->setData(service->entryPath()); if (item) { item->appendRow(subItem); } else { model->appendRow(subItem); } } } else if (p->isType(KST_KServiceGroup)) { KServiceGroup::Ptr subGroup(static_cast(p.data())); if (!subGroup->noDisplay() && subGroup->childCount() > 0) { if (item) { loadAppsCategory(subGroup, model, item); } else { QStandardItem *subItem = new QStandardItem(QIcon::fromTheme(subGroup->icon()), subGroup->caption()); model->appendRow(subItem); loadAppsCategory(subGroup, model, subItem); } } } } } } void KGlobalShortcutsEditor::KGlobalShortcutsEditorPrivate::initGUI() { ui.setupUi(q); selectApplicationDialog = new QDialog(); selectApplicationDialogUi.setupUi(selectApplicationDialog); // Create a stacked widget. stack = new QStackedWidget(q); ui.currentComponentLayout->addWidget(stack); //HACK to make those two un-alignable components, aligned ui.componentLabel->setMinimumHeight(ui.lineEditSpacer->sizeHint().height()); ui.lineEditSpacer->setVisible(false); ui.addButton->setIcon(QIcon::fromTheme("list-add")); ui.removeButton->setIcon(QIcon::fromTheme("list-remove")); ui.components->setCategoryDrawer(new KCategoryDrawer(ui.components)); ui.components->setModelColumn(0); // Build the menu QMenu *menu = new QMenu(q); menu->addAction( QIcon::fromTheme(QStringLiteral("document-import")), i18n("Import Scheme..."), q, SLOT(importScheme())); menu->addAction( QIcon::fromTheme(QStringLiteral("document-export")), i18n("Export Scheme..."), q, SLOT(exportScheme())); menu->addAction( i18n("Set All Shortcuts to None"), q, SLOT(clearConfiguration())); connect(ui.addButton, &QToolButton::clicked, [this]() { if (!selectApplicationDialogUi.treeView->model()) { KRecursiveFilterProxyModel *filterModel = new KRecursiveFilterProxyModel(selectApplicationDialogUi.treeView); QStandardItemModel *appModel = new QStandardItemModel(selectApplicationDialogUi.treeView); selectApplicationDialogUi.kfilterproxysearchline->setProxy(filterModel); filterModel->setSourceModel(appModel); appModel->setHorizontalHeaderLabels({i18n("Applications")}); loadAppsCategory(KServiceGroup::root(), appModel, nullptr); selectApplicationDialogUi.treeView->setModel(filterModel); } selectApplicationDialog->show(); }); connect(selectApplicationDialog, &QDialog::accepted, [this]() { if (selectApplicationDialogUi.treeView->selectionModel()->selectedIndexes().length() == 1) { const QString desktopPath = selectApplicationDialogUi.treeView->model()->data(selectApplicationDialogUi.treeView->selectionModel()->selectedIndexes().first(), Qt::UserRole+1).toString(); if (!desktopPath.isEmpty() &&QFile::exists(desktopPath) ) { const QString desktopFile = desktopPath.split(QLatin1Char('/')).last(); if (!desktopPath.isEmpty()) { KDesktopFile sourceDF(desktopPath); - KDesktopFile *destinationDF = sourceDF.copyTo(QStandardPaths::writableLocation(QStandardPaths::GenericDataLocation) + QStringLiteral("/kglobalaccel/") + desktopFile); + KDesktopFile *destinationDF = sourceDF.copyTo(QStandardPaths::writableLocation(QStandardPaths::GenericDataLocation) + QLatin1String("/kglobalaccel/") + desktopFile); qWarning()<sync(); //TODO: a DBUS call to tell the daemon to refresh desktop files // Create a action collection for our current component:context KActionCollection *col = new KActionCollection(q, desktopFile); foreach(const QString &actionId, sourceDF.readActions()) { const QString friendlyName = sourceDF.actionGroup(actionId).readEntry(QStringLiteral("Name")); QAction *action = col->addAction(actionId); action->setProperty("isConfigurationAction", QVariant(true)); // see KAction::~KAction action->setProperty("componentDisplayName", friendlyName); action->setText(friendlyName); KGlobalAccel::self()->setShortcut(action, QList()); QStringList sequencesStrings = sourceDF.actionGroup(actionId).readEntry(QStringLiteral("X-KDE-Shortcuts"), QString()).split(QLatin1Char('/')); QList sequences; if (!sequencesStrings.isEmpty()) { Q_FOREACH (const QString &seqString, sequencesStrings) { sequences.append(QKeySequence(seqString)); } } if (!sequences.isEmpty()) { KGlobalAccel::self()->setDefaultShortcut(action, sequences); } } //Global launch action { const QString friendlyName = i18n("Launch %1", sourceDF.readName()); QAction *action = col->addAction(QStringLiteral("_launch")); action->setProperty("isConfigurationAction", QVariant(true)); // see KAction::~KAction action->setProperty("componentDisplayName", friendlyName); action->setText(friendlyName); KGlobalAccel::self()->setShortcut(action, QList()); QStringList sequencesStrings = sourceDF.desktopGroup().readEntry(QStringLiteral("X-KDE-Shortcuts"), QString()).split(QLatin1Char('/')); QList sequences; if (!sequencesStrings.isEmpty()) { Q_FOREACH (const QString &seqString, sequencesStrings) { sequences.append(QKeySequence(seqString)); } } if (!sequences.isEmpty()) { KGlobalAccel::self()->setDefaultShortcut(action, sequences); } } q->addCollection(col, QDBusObjectPath(), desktopFile, sourceDF.readName()); } } } }); connect(ui.removeButton, &QToolButton::clicked, [this]() { //TODO: different way to remove components that are desktop files //disabled desktop files need Hidden=true key QString name = proxyModel->data(ui.components->currentIndex()).toString(); QString componentUnique = components.value(name)->uniqueName(); // The confirmation text is different when the component is active if (KGlobalAccel::isComponentActive(componentUnique)) { if (KMessageBox::questionYesNo( q, i18n("Component '%1' is currently active. Only global shortcuts currently not active will be removed from the list.\n" "All global shortcuts will reregister themselves with their defaults when they are next started.", componentUnique), i18n("Remove component")) != KMessageBox::Yes) { return; } } else { if (KMessageBox::questionYesNo( q, i18n("Are you sure you want to remove the registered shortcuts for component '%1'? " "The component and shortcuts will reregister themselves with their default settings" " when they are next started.", componentUnique), i18n("Remove component")) != KMessageBox::Yes) { return; } } // Initiate the removing of the component. if (KGlobalAccel::cleanComponent(componentUnique)) { // Get the objectPath BEFORE we delete the source of it QDBusObjectPath oPath = components.value(name)->dbusPath(); // Remove the component from the gui removeComponent(componentUnique); // Load it again // ############# if (loadComponent(oPath)) { // Active it q->activateComponent(name); } } }); ui.menu_button->setMenu(menu); proxyModel = new KCategorizedSortFilterProxyModel(q); proxyModel->setCategorizedModel(true); model = new QStandardItemModel(0, 1, proxyModel); proxyModel->setSourceModel(model); proxyModel->setSortCaseSensitivity(Qt::CaseInsensitive); ui.components->setModel(proxyModel); connect(ui.components->selectionModel(), &QItemSelectionModel::currentChanged, q, [this](const QModelIndex &index) { QString name = proxyModel->data(index).toString(); q->activateComponent(name); }); } KGlobalShortcutsEditor::KGlobalShortcutsEditor(QWidget *parent, KShortcutsEditor::ActionTypes actionTypes) : QWidget(parent), d(new KGlobalShortcutsEditorPrivate(this)) { d->actionTypes = actionTypes; // Setup the ui d->initGUI(); } KGlobalShortcutsEditor::~KGlobalShortcutsEditor() { // Before closing the door, undo all changes undo(); delete d->selectApplicationDialog; qDeleteAll(d->components); delete d; } void KGlobalShortcutsEditor::activateComponent(const QString &component) { QHash::Iterator iter = d->components.find(component); if (iter == d->components.end()) { Q_ASSERT(iter != d->components.end()); return; } else { QModelIndexList results = d->proxyModel->match(d->proxyModel->index(0, 0), Qt::DisplayRole, component); Q_ASSERT(!results.isEmpty()); if (results.first().isValid()) { // Known component. Get it. d->ui.components->setCurrentIndex(results.first()); d->stack->setCurrentWidget((*iter)->editor()); } } } void KGlobalShortcutsEditor::addCollection( KActionCollection *collection, const QDBusObjectPath &objectPath, const QString &id, const QString &friendlyName) { KShortcutsEditor *editor; // Check if this component is known QHash::Iterator iter = d->components.find(friendlyName); if (iter == d->components.end()) { // Unknown component. Create an editor. editor = new KShortcutsEditor(this, d->actionTypes); d->stack->addWidget(editor); // try to find one appropriate icon ( allowing NULL pixmap to be returned) QPixmap pixmap = KIconLoader::global()->loadIcon(id, KIconLoader::Small, 0, KIconLoader::DefaultState, QStringList(), nullptr, true); if (pixmap.isNull()) { KService::Ptr service = KService::serviceByStorageId(id); if(service) { pixmap = KIconLoader::global()->loadIcon(service->icon(), KIconLoader::Small, 0, KIconLoader::DefaultState, QStringList(), nullptr, true); } } // if NULL pixmap is returned, use the F.D.O "system-run" icon if (pixmap.isNull()) { pixmap = KIconLoader::global()->loadIcon(QStringLiteral("system-run"), KIconLoader::Small); } // Add to the component list QStandardItem *item = new QStandardItem(pixmap, friendlyName); if (id.endsWith(QLatin1String(".desktop"))) { item->setData(i18n("Application Launchers"), KCategorizedSortFilterProxyModel::CategoryDisplayRole); item->setData(0, KCategorizedSortFilterProxyModel::CategorySortRole); } else { item->setData(i18n("Other Shortcuts"), KCategorizedSortFilterProxyModel::CategoryDisplayRole); item->setData(1, KCategorizedSortFilterProxyModel::CategorySortRole); } d->model->appendRow(item); d->proxyModel->sort(0); // Add to our component registry ComponentData *cd = new ComponentData(id, objectPath, editor); d->components.insert(friendlyName, cd); connect(editor, &KShortcutsEditor::keyChange, this, &KGlobalShortcutsEditor::_k_key_changed); } else { // Known component. editor = (*iter)->editor(); } // Add the collection to the editor of the component editor->addCollection(collection, friendlyName); if (d->proxyModel->rowCount() > -1) { d->ui.components->setCurrentIndex(d->proxyModel->index(0, 0)); QString name = d->proxyModel->data(d->proxyModel->index(0, 0)).toString(); activateComponent(name); } } void KGlobalShortcutsEditor::clearConfiguration() { QString name = d->proxyModel->data(d->ui.components->currentIndex()).toString(); d->components[name]->editor()->clearConfiguration(); } void KGlobalShortcutsEditor::defaults(ComponentScope scope) { switch (scope) { case AllComponents: Q_FOREACH (ComponentData *cd, d->components) { // The editors are responsible for the reset cd->editor()->allDefault(); } break; case CurrentComponent: { QString name = d->proxyModel->data(d->ui.components->currentIndex()).toString(); // The editors are responsible for the reset d->components[name]->editor()->allDefault(); } break; default: Q_ASSERT(false); }; } void KGlobalShortcutsEditor::clear() { // Remove all components and their associated editors qDeleteAll(d->components); d->components.clear(); d->model->clear(); } static bool compare(const QString &a, const QString &b) { return a.toLower().localeAwareCompare(b.toLower()) < 0; } void KGlobalShortcutsEditor::exportScheme() { QStringList keys = d->components.keys(); std::sort(keys.begin(), keys.end(), compare); ExportSchemeDialog dia(keys); if (dia.exec() != KMessageBox::Ok) { return; } const QUrl url = QFileDialog::getSaveFileUrl(this, QString(), QUrl(), QStringLiteral("*.kksrc")); if (!url.isEmpty()) { KConfig config(url.path(), KConfig::SimpleConfig); // TODO: Bug ossi to provide a method for this Q_FOREACH(const QString &group, config.groupList()) { // do not overwrite the Settings group. That makes it possible to // update the standard scheme kksrc file with the editor. if (group == QLatin1String("Settings")) continue; config.deleteGroup(group); } exportConfiguration(dia.selectedComponents(), &config); } } void KGlobalShortcutsEditor::importScheme() { // Check for unsaved modifications if (isModified()) { int choice = KMessageBox::warningContinueCancel( this, i18n("Your current changes will be lost if you load another scheme before saving this one"), i18n("Load Shortcut Scheme"), KGuiItem(i18n("Load"))); if (choice != KMessageBox::Continue) { return; } } SelectSchemeDialog dialog(this); if (dialog.exec() != KDialog::Accepted) { return; } QUrl url = dialog.selectedScheme(); if (!url.isLocalFile()) { KMessageBox::sorry(this, i18n("This file (%1) does not exist. You can only select local files.", url.url())); return; } KConfig config(url.path(), KConfig::SimpleConfig); importConfiguration(&config); } void KGlobalShortcutsEditor::load() { // Connect to kglobalaccel. If that fails there is no need to continue. qRegisterMetaType >(); qDBusRegisterMetaType >(); qDBusRegisterMetaType >(); qDBusRegisterMetaType(); org::kde::KGlobalAccel kglobalaccel( QStringLiteral("org.kde.kglobalaccel"), QStringLiteral("/kglobalaccel"), d->bus); if (!kglobalaccel.isValid()) { QString errorString; QDBusError error = kglobalaccel.lastError(); // The global shortcuts DBus service manages all global shortcuts and we // can't do anything useful without it. if (error.isValid()) { errorString = i18n("Message: %1\nError: %2", error.message(), error.name()); } KMessageBox::sorry( this, i18n("Failed to contact the KDE global shortcuts daemon\n") + errorString ); return; } // Undo all changes not yet applied undo(); clear(); QDBusReply< QList > componentsRc = kglobalaccel.allComponents(); if (!componentsRc.isValid()) { // Sometimes error pop up only after the first real call. QString errorString; QDBusError error = componentsRc.error(); // The global shortcuts DBus service manages all global shortcuts and we // can't do anything useful without it. if (error.isValid()) { errorString = i18n("Message: %1\nError: %2", error.message(), error.name()); } KMessageBox::sorry( this, i18n("Failed to contact the KDE global shortcuts daemon\n") + errorString ); return; } QList components = componentsRc; Q_FOREACH(const QDBusObjectPath &componentPath, components) { d->loadComponent(componentPath); } // Q_FOREACH(component) } void KGlobalShortcutsEditor::save() { // The editors are responsible for the saving //qDebug() << "Save the changes"; Q_FOREACH (ComponentData *cd, d->components) { cd->editor()->commit(); } } void KGlobalShortcutsEditor::importConfiguration(KConfigBase *config) { //qDebug() << config->groupList(); // In a first step clean out the current configurations. We do this // because we want to minimize the chance of conflicts. Q_FOREACH (ComponentData *cd, d->components) { KConfigGroup group(config, cd->uniqueName()); //qDebug() << cd->uniqueName() << group.name(); if (group.exists()) { //qDebug() << "Removing" << cd->uniqueName(); cd->editor()->clearConfiguration(); } } // Now import the new configurations. Q_FOREACH (ComponentData *cd, d->components) { KConfigGroup group(config, cd->uniqueName()); if (group.exists()) { //qDebug() << "Importing" << cd->uniqueName(); cd->editor()->importConfiguration(&group); } } } void KGlobalShortcutsEditor::exportConfiguration(QStringList components, KConfig *config) const { Q_FOREACH (const QString &componentFriendly, components) { QHash::Iterator iter = d->components.find(componentFriendly); if (iter == d->components.end()) { Q_ASSERT(iter != d->components.end()); continue; } else { KConfigGroup group(config, (*iter)->uniqueName()); (*iter)->editor()->exportConfiguration(&group); } } } void KGlobalShortcutsEditor::undo() { // The editors are responsible for the undo //qDebug() << "Undo the changes"; Q_FOREACH (ComponentData *cd, d->components) { cd->editor()->undoChanges(); } } bool KGlobalShortcutsEditor::isModified() const { Q_FOREACH (ComponentData *cd, d->components) { if (cd->editor()->isModified()) { return true; } } return false; } void KGlobalShortcutsEditor::_k_key_changed() { emit changed(isModified()); } QDBusObjectPath KGlobalShortcutsEditor::KGlobalShortcutsEditorPrivate::componentPath(const QString &componentUnique) { return QDBusObjectPath(QStringLiteral("/component/") + componentUnique); } bool KGlobalShortcutsEditor::KGlobalShortcutsEditorPrivate::loadComponent(const QDBusObjectPath &componentPath) { // Get the component org::kde::kglobalaccel::Component component( QStringLiteral("org.kde.kglobalaccel"), componentPath.path(), bus); if (!component.isValid()) { //qDebug() << "Component " << componentPath.path() << "not valid! Skipping!"; return false; } // Get the shortcut contexts. QDBusReply shortcutContextsRc = component.getShortcutContexts(); if (!shortcutContextsRc.isValid()) { //qDebug() << "Failed to get contexts for component " //<< componentPath.path() <<"! Skipping!"; //qDebug() << shortcutContextsRc.error(); return false; } QStringList shortcutContexts = shortcutContextsRc; // We add the shortcuts for all shortcut contexts to the editor. This // way the user keeps full control of it's shortcuts. Q_FOREACH (const QString &shortcutContext, shortcutContexts) { QDBusReply< QList > shortcutsRc = component.allShortcutInfos(shortcutContext); if (!shortcutsRc.isValid()) { //qDebug() << "allShortcutInfos() failed for " << componentPath.path() << shortcutContext; continue; } QList shortcuts = shortcutsRc; // Shouldn't happen. But you never know if (shortcuts.isEmpty()) { //qDebug() << "Got shortcut context" << shortcutContext << "without shortcuts for" //<< componentPath.path(); continue; } // It's safe now const QString componentUnique = shortcuts[0].componentUniqueName(); QString componentContextId = componentUnique; // kglobalaccel knows that '|' is our separator between // component and context if (shortcutContext != QLatin1String("default")) { - componentContextId += QStringLiteral("|") + shortcutContext; + componentContextId += QLatin1String("|") + shortcutContext; } // Create a action collection for our current component:context KActionCollection* col = new KActionCollection( q, componentContextId); // Now add the shortcuts. Q_FOREACH (const KGlobalShortcutInfo &shortcut, shortcuts) { const QString &objectName = shortcut.uniqueName(); QAction *action = col->addAction(objectName); action->setProperty("isConfigurationAction", QVariant(true)); // see KAction::~KAction action->setProperty("componentDisplayName", shortcut.componentFriendlyName()); action->setText(shortcut.friendlyName()); // Always call this to enable global shortcuts for the action. The editor widget // checks it. // Also actually loads the shortcut using the KAction::Autoloading mechanism. // Avoid setting the default shortcut; it would just be written to the global // configuration so we would not get the real one below. KGlobalAccel::self()->setShortcut(action, QList()); // The default shortcut will never be loaded because it's pointless in a real // application. There are no scarce resources [i.e. physical keys] to manage // so applications can set them at will and there's no autoloading. QList sc = shortcut.defaultKeys(); if (sc.count()>0) { KGlobalAccel::self()->setDefaultShortcut(action, sc); } } // Q_FOREACH(shortcut) QString componentFriendlyName = shortcuts[0].componentFriendlyName(); if (shortcuts[0].contextUniqueName() != QLatin1String("default")) { componentFriendlyName += QString('[') + shortcuts[0].contextFriendlyName() + QString(']'); } q->addCollection(col, componentPath, componentContextId, componentFriendlyName ); } // Q_FOREACH(context) return true; } void KGlobalShortcutsEditor::KGlobalShortcutsEditorPrivate::removeComponent( const QString &componentUnique ) { // TODO: Remove contexts too. Q_FOREACH (const QString &text, components.keys()) { if (components.value(text)->uniqueName() == componentUnique) { // Remove from QComboBox QModelIndexList results = proxyModel->match(proxyModel->index(0, 0), Qt::DisplayRole, text); Q_ASSERT(!results.isEmpty()); model->removeRow(proxyModel->mapToSource(results.first()).row()); // Remove from QStackedWidget stack->removeWidget(components[text]->editor()); // Remove the componentData delete components.take(text); } } } diff --git a/kcms/kfontinst/kcmfontinst/FontList.cpp b/kcms/kfontinst/kcmfontinst/FontList.cpp index 1b64e5e98..45d74aa61 100644 --- a/kcms/kfontinst/kcmfontinst/FontList.cpp +++ b/kcms/kfontinst/kcmfontinst/FontList.cpp @@ -1,2060 +1,2060 @@ /* * KFontInst - KDE Font Installer * * Copyright 2003-2007 Craig Drummond * * ---- * * 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. * * This program 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 * General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; see the file COPYING. If not, write to * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, * Boston, MA 02110-1301, USA. */ #include "FontList.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "FcEngine.h" #include "Fc.h" #include "KfiConstants.h" #include "GroupList.h" #include "FontInstInterface.h" #include "XmlStrings.h" #include "Family.h" #include "Style.h" #include "File.h" namespace KFI { const QStringList CFontList::fontMimeTypes(QStringList() << "font/ttf" << "font/otf" << "application/x-font-ttf" << "application/x-font-otf" << "application/x-font-type1" << "application/x-font-pcf" << "application/x-font-bdf" << "application/vnd.kde.fontspackage"); static const int constMaxSlowed = 250; static void decompose(const QString &name, QString &family, QString &style) { int commaPos=name.lastIndexOf(','); family=-1==commaPos ? name : name.left(commaPos); style=-1==commaPos ? KFI_WEIGHT_REGULAR : name.mid(commaPos+2); } static void addFont(CFontItem *font, CJobRunner::ItemList &urls, QStringList &fontNames, QSet *fonts, QSet &usedFonts, bool getEnabled, bool getDisabled) { if(!usedFonts.contains(font) && ( (getEnabled && font->isEnabled()) || (getDisabled && !font->isEnabled()) ) ) { urls.append(CJobRunner::Item(font->url(), font->name(), !font->isEnabled())); fontNames.append(font->name()); usedFonts.insert(font); if(fonts) fonts->insert(Misc::TFont(font->family(), font->styleInfo())); } } static QString replaceEnvVar(const QString &text) { QString mod(text); int endPos(text.indexOf('/')); if(endPos==-1) endPos=text.length()-1; else endPos--; if(endPos>0) { QString envVar(text.mid(1, endPos)); const char *val=getenv(envVar.toLatin1().constData()); if(val) mod=Misc::fileSyntax(QFile::decodeName(val)+mod.mid(endPos+1)); } return mod; } // // Convert from list such as: // // Arial // Arial, Bold // Courier // Times // Times, Italic // // To: // // Arial (Regular, Bold) // Coutier // Times (Regular, Italic) QStringList CFontList::compact(const QStringList &fonts) { QString lastFamily, entry; QStringList::ConstIterator it(fonts.begin()), end(fonts.end()); QStringList compacted; QSet usedStyles; for(; it!=end; ++it) { QString family, style; decompose(*it, family, style); if(family!=lastFamily) { usedStyles.clear(); if(entry.length()) { entry+=')'; compacted.append(entry); } entry=QString(family+" ("); lastFamily=family; } if(!usedStyles.contains(style)) { usedStyles.clear(); if(entry.length() && '('!=entry[entry.length()-1]) entry+=", "; entry+=style; usedStyles.insert(style); } } if(entry.length()) { entry+=')'; compacted.append(entry); } return compacted; } QString capitaliseFoundry(const QString &foundry) { QString f(foundry.toLower()); if(f==QLatin1String("ibm")) return QLatin1String("IBM"); else if(f==QLatin1String("urw")) return QLatin1String("URW"); else if(f==QLatin1String("itc")) return QLatin1String("ITC"); else if(f==QLatin1String("nec")) return QLatin1String("NEC"); else if(f==QLatin1String("b&h")) return QLatin1String("B&H"); else if(f==QLatin1String("dec")) return QLatin1String("DEC"); else { QChar *ch(f.data()); int len(f.length()); bool isSpace(true); while(len--) { if (isSpace) *ch=ch->toUpper(); isSpace=ch->isSpace(); ++ch; } } return f; } inline bool isSysFolder(const QString §) { return i18n(KFI_KIO_FONTS_SYS)==sect || KFI_KIO_FONTS_SYS==sect; } CFontItem::CFontItem(CFontModelItem *p, const Style &s, bool sys) : CFontModelItem(p), itsStyleName(FC::createStyleName(s.value())), itsStyle(s) { refresh(); if(!Misc::root()) setIsSystem(sys); } void CFontItem::refresh() { FileCont::ConstIterator it(itsStyle.files().begin()), end(itsStyle.files().end()); itsEnabled=false; for(; it!=end; ++it) if(!Misc::isHidden(Misc::getFile((*it).path()))) { itsEnabled=true; break; } } CFamilyItem::CFamilyItem(CFontList &p, const Family &f, bool sys) : CFontModelItem(nullptr), itsStatus(ENABLED), itsRealStatus(ENABLED), itsRegularFont(nullptr), itsParent(p) { itsName=f.name(); addFonts(f.styles(), sys); //updateStatus(); } CFamilyItem::~CFamilyItem() { qDeleteAll(itsFonts); itsFonts.clear(); } bool CFamilyItem::addFonts(const StyleCont &styles, bool sys) { StyleCont::ConstIterator it(styles.begin()), end(styles.end()); bool modified=false; for(; it!=end; ++it) { CFontItem *font=findFont((*it).value(), sys); if(!font) { // New font style! itsFonts.append(new CFontItem(this, *it, sys)); modified=true; } else { int before=(*it).files().size(); font->addFiles((*it).files()); if((*it).files().size()!=before) { modified=true; font->refresh(); } } } return modified; } CFontItem * CFamilyItem::findFont(quint32 style, bool sys) { CFontItemCont::ConstIterator fIt(itsFonts.begin()), fEnd(itsFonts.end()); for(; fIt!=fEnd; ++fIt) if((*(*fIt)).styleInfo()==style && (*(*fIt)).isSystem()==sys) return (*fIt); return nullptr; } void CFamilyItem::getFoundries(QSet &foundries) const { CFontItemCont::ConstIterator it(itsFonts.begin()), end(itsFonts.end()); for(; it!=end; ++it) { FileCont::ConstIterator fIt((*it)->files().begin()), fEnd((*it)->files().end()); for(; fIt!=fEnd; ++fIt) if(!(*fIt).foundry().isEmpty()) foundries.insert(capitaliseFoundry((*fIt).foundry())); } } bool CFamilyItem::usable(const CFontItem *font, bool root) { return ( root || (font->isSystem() && itsParent.allowSys()) || (!font->isSystem() && itsParent.allowUser())); } void CFamilyItem::addFont(CFontItem *font, bool update) { itsFonts.append(font); if(update) { updateStatus(); updateRegularFont(font); } } void CFamilyItem::removeFont(CFontItem *font, bool update) { itsFonts.removeAll(font); if(update) updateStatus(); if(itsRegularFont==font) { itsRegularFont=nullptr; if(update) updateRegularFont(nullptr); } delete font; } void CFamilyItem::refresh() { updateStatus(); itsRegularFont=nullptr; updateRegularFont(nullptr); } bool CFamilyItem::updateStatus() { bool root(Misc::root()); EStatus oldStatus(itsStatus); CFontItemCont::ConstIterator it(itsFonts.begin()), end(itsFonts.end()); int en(0), dis(0), allEn(0), allDis(0); bool oldSys(isSystem()), sys(false); itsFontCount=0; for(; it!=end; ++it) if(usable(*it, root)) { if((*it)->isEnabled()) en++; else dis++; if(!sys) sys=(*it)->isSystem(); itsFontCount++; } else if((*it)->isEnabled()) allEn++; else allDis++; allEn+=en; allDis+=dis; itsStatus=en && dis ? PARTIAL : en ? ENABLED : DISABLED; itsRealStatus=allEn && allDis ? PARTIAL : allEn ? ENABLED : DISABLED; if(!root) setIsSystem(sys); return itsStatus!=oldStatus || isSystem()!=oldSys; } bool CFamilyItem::updateRegularFont(CFontItem *font) { static const quint32 constRegular=FC::createStyleVal(FC_WEIGHT_REGULAR, KFI_FC_WIDTH_NORMAL, FC_SLANT_ROMAN); CFontItem *oldFont(itsRegularFont); bool root(Misc::root()); if(font && usable(font, root)) { if(itsRegularFont) { int regDiff=abs((long)(itsRegularFont->styleInfo()-constRegular)), fontDiff=abs((long)(font->styleInfo()-constRegular)); if(fontDiffstyleInfo()-constRegular)); if(diff)), SLOT(fontList(int,QList))); } CFontList::~CFontList() { qDeleteAll(itsFamilies); itsFamilies.clear(); itsFamilyHash.clear(); } void CFontList::dbusServiceOwnerChanged(const QString &name, const QString &from, const QString &to) { Q_UNUSED(from); Q_UNUSED(to); if(name==QLatin1String(OrgKdeFontinstInterface::staticInterfaceName())) load(); } void CFontList::fontList(int pid, const QList &families) { // printf("**** fontList:%d/%d %d\n", pid, getpid(), families.count()); if(pid==getpid()) { QList::ConstIterator it(families.begin()), end(families.end()); int count(families.size()); for(int i=0; it!=end; ++it, ++i) { fontsAdded(*it); emit listingPercent(i*100/count); } emit listingPercent(100); } } void CFontList::unsetSlowUpdates() { setSlowUpdates(false); } void CFontList::load() { for(int t=0; tlist(FontInst::SYS_MASK|FontInst::USR_MASK, getpid()); } void CFontList::setSlowUpdates(bool slow) { if(itsSlowUpdates!=slow) { if(!slow) for(int i=0; i families; QDataStream ds(&encodedData, QIODevice::WriteOnly); for(; it!=end; ++it) if((*it).isValid()) { if((static_cast((*it).internalPointer()))->isFont()) { CFontItem *font=static_cast((*it).internalPointer()); families.insert(font->family()); } else { CFamilyItem *fam=static_cast((*it).internalPointer()); families.insert(fam->name()); } } ds << families; mimeData->setData(KFI_FONT_DRAG_MIME, encodedData); return mimeData; } QStringList CFontList::mimeTypes() const { QStringList types; types << "text/uri-list"; return types; } QVariant CFontList::headerData(int section, Qt::Orientation orientation, int role) const { if (orientation == Qt::Horizontal) switch(role) { case Qt::DisplayRole: switch(section) { case COL_FONT: return i18n("Font"); case COL_STATUS: return i18n("Status"); default: break; } break; // case Qt::DecorationRole: // if(COL_STATUS==section) // return SmallIcon("fontstatus"); // break; case Qt::TextAlignmentRole: return QVariant(Qt::AlignLeft | Qt::AlignVCenter); case Qt::ToolTipRole: if(COL_STATUS==section) return i18n("This column shows the status of the font family, and of the " "individual font styles."); break; case Qt::WhatsThisRole: return whatsThis(); default: break; } return QVariant(); } QModelIndex CFontList::index(int row, int column, const QModelIndex &parent) const { if(parent.isValid()) // Then font... { CFamilyItem *fam=static_cast(parent.internalPointer()); if(rowfonts().count()) return createIndex(row, column, fam->fonts().at(row)); } else // Family.... if(row(index.internalPointer()); if(mi->isFamily()) return QModelIndex(); else { CFontItem *font=static_cast(index.internalPointer()); return createIndex(itsFamilies.indexOf(((CFamilyItem *)font->parent())), 0, font->parent()); } } int CFontList::rowCount(const QModelIndex &parent) const { if(parent.isValid()) { CFontModelItem *mi=static_cast(parent.internalPointer()); if(mi->isFont()) return 0; CFamilyItem *fam=static_cast(parent.internalPointer()); return fam->fonts().count(); } else return itsFamilies.count(); } void CFontList::refresh(bool allowSys, bool allowUser) { itsAllowSys=allowSys; itsAllowUser=allowUser; CFamilyItemCont::ConstIterator it(itsFamilies.begin()), end(itsFamilies.end()); for(; it!=end; ++it) (*it)->refresh(); } void CFontList::getFamilyStats(QSet &enabled, QSet &disabled, QSet &partial) { CFamilyItemCont::ConstIterator it(itsFamilies.begin()), end(itsFamilies.end()); for(; it!=end; ++it) { switch((*it)->realStatus()) { case CFamilyItem::ENABLED: enabled.insert((*it)->name()); break; case CFamilyItem::PARTIAL: partial.insert((*it)->name()); break; case CFamilyItem::DISABLED: disabled.insert((*it)->name()); break; } } } void CFontList::getFoundries(QSet &foundries) const { CFamilyItemCont::ConstIterator it(itsFamilies.begin()), end(itsFamilies.end()); for(; it!=end; ++it) (*it)->getFoundries(foundries); } QString CFontList::whatsThis() const { return i18n("

This list shows your installed fonts. The fonts are grouped by family, and the" " number in square brackets represents the number of styles in which the family is" " available. e.g.

" "
    " "
  • Times [4]" "
    • Regular
    • " "
    • Bold
    • " "
    • Bold Italic
    • " "
    • Italic
    • " "
    " "
  • " "
"); } void CFontList::fontsAdded(const KFI::Families &families) { // printf("**** FONT ADDED:%d\n", families.items.count()); if(itsSlowUpdates) storeSlowedMessage(families, MSG_ADD); else addFonts(families.items, families.isSystem && !Misc::root()); } void CFontList::fontsRemoved(const KFI::Families &families) { // printf("**** FONT REMOVED:%d\n", families.items.count()); if(itsSlowUpdates) storeSlowedMessage(families, MSG_DEL); else removeFonts(families.items, families.isSystem && !Misc::root()); } void CFontList::storeSlowedMessage(const Families &families, EMsgType type) { int folder=families.isSystem ? FontInst::FOLDER_SYS : FontInst::FOLDER_USER; bool playOld=false; for(int i=0; iconstMaxSlowed) playOld=true; if(playOld) actionSlowedUpdates(families.isSystem); FamilyCont::ConstIterator family(families.items.begin()), fend(families.items.end()); for(; family!=fend; ++family) { FamilyCont::ConstIterator f=itsSlowedMsgs[type][folder].find(*family); if(f!=itsSlowedMsgs[type][folder].end()) { StyleCont::ConstIterator style((*family).styles().begin()), send((*family).styles().end()); for(; style!=send; ++style) { StyleCont::ConstIterator st=(*f).styles().find(*style); if(st==(*f).styles().end()) (*f).add(*style); else (*st).addFiles((*style).files()); } } else itsSlowedMsgs[type][folder].insert(*family); } } void CFontList::actionSlowedUpdates(bool sys) { int folder=sys ? FontInst::FOLDER_SYS : FontInst::FOLDER_USER; for(int i=0; i modifiedFamilies; for(; family!=end; ++family) { if((*family).styles().count()>0) { CFamilyItem *famItem=findFamily((*family).name()); if(famItem) { int rowFrom=famItem->fonts().count(); if(famItem->addFonts((*family).styles(), sys)) { int rowTo=famItem->fonts().count(); if(rowTo!=rowFrom) { beginInsertRows(createIndex(famItem->rowNumber(), 0, famItem), rowFrom, rowTo); endInsertRows(); } modifiedFamilies.insert(famItem); } } else { famItem=new CFamilyItem(*this, *family, sys); itsFamilies.append(famItem); itsFamilyHash[famItem->name()]=famItem; modifiedFamilies.insert(famItem); } } } int famRowTo=itsFamilies.count(); if(famRowTo!=famRowFrom) { beginInsertRows(QModelIndex(), famRowFrom, famRowTo); endInsertRows(); } if(!modifiedFamilies.isEmpty()) { QSet::Iterator it(modifiedFamilies.begin()), end(modifiedFamilies.end()); for(; it!=end; ++it) (*it)->refresh(); } // if(emitLayout) // emit layoutChanged(); } void CFontList::removeFonts(const FamilyCont &families, bool sys) { // if(!itsSlowUpdates) // emit layoutAboutToBeChanged(); FamilyCont::ConstIterator family(families.begin()), end(families.end()); QSet modifiedFamilies; for(; family!=end; ++family) { if((*family).styles().count()>0) { CFamilyItem *famItem=findFamily((*family).name()); if(famItem) { StyleCont::ConstIterator it((*family).styles().begin()), end((*family).styles().end()); for(; it!=end; ++it) { CFontItem *fontItem=famItem->findFont((*it).value(), sys); if(fontItem) { int before=fontItem->files().count(); fontItem->removeFiles((*it).files()); if(fontItem->files().count()!=before) { if(fontItem->files().isEmpty()) { int row=-1; if(1!=famItem->fonts().count()) { row=fontItem->rowNumber(); beginRemoveRows(createIndex(famItem->rowNumber(), 0, famItem), row, row); } famItem->removeFont(fontItem, false); if(-1!=row) endRemoveRows(); } else fontItem->refresh(); } } } if(famItem->fonts().isEmpty()) { int row=famItem->rowNumber(); beginRemoveRows(QModelIndex(), row, row); itsFamilyHash.remove(famItem->name()); itsFamilies.removeAt(row); endRemoveRows(); } else modifiedFamilies.insert(famItem); } } } if(!modifiedFamilies.isEmpty()) { QSet::Iterator it(modifiedFamilies.begin()), end(modifiedFamilies.end()); for(; it!=end; ++it) (*it)->refresh(); } // if(!itsSlowUpdates) // emit layoutChanged(); } CFamilyItem * CFontList::findFamily(const QString &familyName) { CFamilyItemHash::Iterator it=itsFamilyHash.find(familyName); return it==itsFamilyHash.end() ? 0L : *it; } inline bool matchString(const QString &str, const QString &pattern) { return pattern.isEmpty() || -1!=str.indexOf(pattern, 0, Qt::CaseInsensitive); } CFontListSortFilterProxy::CFontListSortFilterProxy(QObject *parent, QAbstractItemModel *model) : QSortFilterProxyModel(parent), itsGroup(nullptr), itsFilterCriteria(CFontFilter::CRIT_FAMILY), itsFilterWs(0), itsFcQuery(nullptr) { setSourceModel(model); setSortCaseSensitivity(Qt::CaseInsensitive); setFilterKeyColumn(0); setDynamicSortFilter(false); itsTimer=new QTimer(this); connect(itsTimer, SIGNAL(timeout()), SLOT(timeout())); connect(model, SIGNAL(layoutChanged()), SLOT(invalidate())); itsTimer->setSingleShot(true); } QVariant CFontListSortFilterProxy::data(const QModelIndex &idx, int role) const { if (!idx.isValid()) return QVariant(); static const int constMaxFiles=20; QModelIndex index(mapToSource(idx)); CFontModelItem *mi=static_cast(index.internalPointer()); if(!mi) return QVariant(); switch(role) { case Qt::ToolTipRole: if(CFontFilter::CRIT_FILENAME==itsFilterCriteria || CFontFilter::CRIT_LOCATION==itsFilterCriteria || CFontFilter::CRIT_FONTCONFIG==itsFilterCriteria) { if(mi->isFamily()) { CFamilyItem *fam=static_cast(index.internalPointer()); CFontItemCont::ConstIterator it(fam->fonts().begin()), end(fam->fonts().end()); FileCont allFiles; QString tip(""+fam->name()+""); bool markMatch(CFontFilter::CRIT_FONTCONFIG==itsFilterCriteria); tip+="

"; for(; it!=end; ++it) allFiles+=(*it)->files(); //qSort(allFiles); FileCont::ConstIterator fit(allFiles.begin()), fend(allFiles.end()); for(int i=0; fit!=fend && ifile()) tip+=""; else tip+=""; if(allFiles.count()>constMaxFiles) tip+=""; tip+="
"+Misc::contractHome((*fit).path())+"
"+Misc::contractHome((*fit).path())+"
"+i18n("...plus %1 more", allFiles.count()-constMaxFiles)+"

"; return tip; } else { CFontItem *font=static_cast(index.internalPointer()); QString tip(""+font->name()+""); const FileCont &files(font->files()); bool markMatch(CFontFilter::CRIT_FONTCONFIG==itsFilterCriteria); tip+="

"; //qSort(files); FileCont::ConstIterator fit(files.begin()), fend(files.end()); for(int i=0; fit!=fend && ifile()) tip+=""; else tip+=""; if(files.count()>constMaxFiles) tip+=""; tip+="
"+Misc::contractHome((*fit).path())+"
"+Misc::contractHome((*fit).path() )+"
"+i18n("...plus %1 more", files.count()-constMaxFiles)+"

"; return tip; } } break; case Qt::FontRole: if(COL_FONT==index.column() && mi->isSystem()) { QFont font; font.setItalic(true); return font; } break; case Qt::ForegroundRole: if(COL_FONT==index.column() && ( (mi->isFont() && !(static_cast(index.internalPointer()))->isEnabled()) || (mi->isFamily() && CFamilyItem::DISABLED==(static_cast(index.internalPointer()))->status())) ) return KColorScheme(QPalette::Active).foreground(KColorScheme::NegativeText).color(); break; case Qt::DisplayRole: if(COL_FONT==index.column()) { if(mi->isFamily()) { CFamilyItem *fam=static_cast(index.internalPointer()); return i18n("%1 [%2]", fam->name(), fam->fontCount()); } else return (static_cast(index.internalPointer()))->style(); } break; case Qt::DecorationRole: if(mi->isFamily()) { CFamilyItem *fam=static_cast(index.internalPointer()); switch(index.column()) { case COL_STATUS: switch(fam->status()) { case CFamilyItem::PARTIAL: return SmallIcon("dialog-ok", 0, KIconLoader::DisabledState); case CFamilyItem::ENABLED: return SmallIcon("dialog-ok"); case CFamilyItem::DISABLED: return SmallIcon("dialog-cancel"); } break; default: break; } } else if(COL_STATUS==index.column()) return SmallIcon( (static_cast(index.internalPointer()))->isEnabled() ? "dialog-ok" : "dialog-cancel", 10); break; case Qt::SizeHintRole: if(mi->isFamily()) { const int s = KIconLoader::global()->currentSize(KIconLoader::Small); return QSize(s, s + 4); } default: break; } return QVariant(); } bool CFontListSortFilterProxy::acceptFont(CFontItem *fnt, bool checkFontText) const { if(itsGroup && (CGroupListItem::ALL!=itsGroup->type() || (!filterText().isEmpty() && checkFontText))) { bool fontMatch(!checkFontText); if(!fontMatch) switch(itsFilterCriteria) { case CFontFilter::CRIT_FONTCONFIG: fontMatch=itsFcQuery ? fnt->name()==itsFcQuery->font() // || fnt->files().contains(itsFcQuery->file()) : false; break; case CFontFilter::CRIT_STYLE: fontMatch=matchString(fnt->style(), itsFilterText); break; case CFontFilter::CRIT_FOUNDRY: { FileCont::ConstIterator it(fnt->files().begin()), end(fnt->files().end()); for(; it!=end && !fontMatch; ++it) fontMatch=0==(*it).foundry().compare(itsFilterText, Qt::CaseInsensitive); break; } case CFontFilter::CRIT_FILENAME: { FileCont::ConstIterator it(fnt->files().begin()), end(fnt->files().end()); for(; it!=end && !fontMatch; ++it) { QString file(Misc::getFile((*it).path())); int pos(Misc::isHidden(file) ? 1 : 0); if(pos==file.indexOf(itsFilterText, pos, Qt::CaseInsensitive)) fontMatch=true; } break; } case CFontFilter::CRIT_LOCATION: { FileCont::ConstIterator it(fnt->files().begin()), end(fnt->files().end()); for(; it!=end && !fontMatch; ++it) if(0==Misc::getDir((*it).path()).indexOf(itsFilterText, 0, Qt::CaseInsensitive)) fontMatch=true; break; } case CFontFilter::CRIT_FILETYPE: { FileCont::ConstIterator it(fnt->files().begin()), end(fnt->files().end()); QStringList::ConstIterator mimeEnd(itsFilterTypes.constEnd()); for(; it!=end && !fontMatch; ++it) { QStringList::ConstIterator mime(itsFilterTypes.constBegin()); for(; mime!=mimeEnd; ++mime) if(Misc::checkExt((*it).path(), *mime)) fontMatch=true; } break; } case CFontFilter::CRIT_WS: fontMatch=fnt->writingSystems()&itsFilterWs; break; default: break; } return fontMatch && itsGroup->hasFont(fnt); } return true; } bool CFontListSortFilterProxy::acceptFamily(CFamilyItem *fam) const { CFontItemCont::ConstIterator it(fam->fonts().begin()), end(fam->fonts().end()); bool familyMatch(CFontFilter::CRIT_FAMILY==itsFilterCriteria && matchString(fam->name(), itsFilterText)); for(; it!=end; ++it) if(acceptFont(*it, !familyMatch)) return true; return false; } bool CFontListSortFilterProxy::filterAcceptsRow(int sourceRow, const QModelIndex &sourceParent) const { QModelIndex index(sourceModel()->index(sourceRow, 0, sourceParent)); if(index.isValid()) { CFontModelItem *mi=static_cast(index.internalPointer()); if(mi->isFont()) { CFontItem *font=static_cast(index.internalPointer()); return acceptFont(font, !(CFontFilter::CRIT_FAMILY==itsFilterCriteria && matchString(font->family(), itsFilterText))); } else return acceptFamily(static_cast(index.internalPointer())); } return false; } bool CFontListSortFilterProxy::lessThan(const QModelIndex &left, const QModelIndex &right) const { if(left.isValid() && right.isValid()) { CFontModelItem *lmi=static_cast(left.internalPointer()), *rmi=static_cast(right.internalPointer()); if(lmi->isFont()isFont()) return true; if(lmi->isFont()) { CFontItem *lfi=static_cast(left.internalPointer()), *rfi=static_cast(right.internalPointer()); if(COL_STATUS==filterKeyColumn()) { if(lfi->isEnabled()isEnabled() || (lfi->isEnabled()==rfi->isEnabled() && lfi->styleInfo()styleInfo())) return true; } else if(lfi->styleInfo()styleInfo()) return true; } else { CFamilyItem *lfi=static_cast(left.internalPointer()), *rfi=static_cast(right.internalPointer()); if(COL_STATUS==filterKeyColumn()) { if(lfi->status()status() || (lfi->status()==rfi->status() && QString::localeAwareCompare(lfi->name(), rfi->name())<0)) return true; } else if(QString::localeAwareCompare(lfi->name(), rfi->name())<0) return true; } } return false; } void CFontListSortFilterProxy::setFilterGroup(CGroupListItem *grp) { if(grp!=itsGroup) { // bool wasNull=!itsGroup; itsGroup=grp; // if(!(wasNull && itsGroup && CGroupListItem::ALL==itsGroup->type())) clear(); } } void CFontListSortFilterProxy::setFilterText(const QString &text) { if(text!=itsFilterText) { // // If we are filtering on file location, then expand ~ to /home/user, etc. if (CFontFilter::CRIT_LOCATION==itsFilterCriteria && !text.isEmpty() && ('~'==text[0] || '$'==text[0])) if('~'==text[0]) itsFilterText=1==text.length() ? QDir::homePath() : QString(text).replace(0, 1, QDir::homePath()); else itsFilterText=replaceEnvVar(text); else itsFilterText=text; if(itsFilterText.isEmpty()) { itsTimer->stop(); timeout(); } else itsTimer->start(CFontFilter::CRIT_FONTCONFIG==itsFilterCriteria ? 750 : 400); } } void CFontListSortFilterProxy::setFilterCriteria(CFontFilter::ECriteria crit, qulonglong ws, const QStringList &ft) { if(crit!=itsFilterCriteria || ws!=itsFilterWs || ft!=itsFilterTypes) { itsFilterWs=ws; itsFilterCriteria=crit; itsFilterTypes=ft; if(CFontFilter::CRIT_LOCATION==itsFilterCriteria) setFilterText(itsFilterText); itsTimer->stop(); timeout(); } } void CFontListSortFilterProxy::timeout() { if(CFontFilter::CRIT_FONTCONFIG==itsFilterCriteria) { int commaPos=itsFilterText.indexOf(','); QString query(itsFilterText); if(-1!=commaPos) { QString style(query.mid(commaPos+1)); - query=query.left(commaPos); + query.truncate(commaPos); query=query.trimmed(); query+=":style="; style=style.trimmed(); query+=style; } else query=query.trimmed(); if(!itsFcQuery) { itsFcQuery=new CFcQuery(this); connect(itsFcQuery, SIGNAL(finished()), SLOT(fcResults())); } itsFcQuery->run(query); } else { clear(); emit refresh(); } } void CFontListSortFilterProxy::fcResults() { if(CFontFilter::CRIT_FONTCONFIG==itsFilterCriteria) { clear(); emit refresh(); } } CFontListView::CFontListView(QWidget *parent, CFontList *model) : QTreeView(parent), itsProxy(new CFontListSortFilterProxy(this, model)), itsModel(model), itsAllowDrops(false) { setModel(itsProxy); itsModel=model; header()->setStretchLastSection(false); resizeColumnToContents(COL_STATUS); header()->setResizeMode(COL_STATUS, QHeaderView::Fixed); header()->setResizeMode(COL_FONT, QHeaderView::Stretch); setSelectionMode(QAbstractItemView::ExtendedSelection); setSelectionBehavior(QAbstractItemView::SelectRows); setSortingEnabled(true); sortByColumn(COL_FONT, Qt::AscendingOrder); setAllColumnsShowFocus(true); setAlternatingRowColors(true); setAcceptDrops(true); setDropIndicatorShown(false); setDragEnabled(true); setDragDropMode(QAbstractItemView::DragDrop); header()->setClickable(true); header()->setSortIndicatorShown(true); connect(this, SIGNAL(collapsed(QModelIndex)), SLOT(itemCollapsed(QModelIndex))); connect(header(), SIGNAL(sectionClicked(int)), SLOT(setSortColumn(int))); connect(itsProxy, SIGNAL(refresh()), SIGNAL(refresh())); connect(itsModel, SIGNAL(listingPercent(int)), SLOT(listingPercent(int))); setWhatsThis(model->whatsThis()); header()->setWhatsThis(whatsThis()); itsMenu=new QMenu(this); itsDeleteAct=itsMenu->addAction(QIcon::fromTheme("edit-delete"), i18n("Delete"), this, SIGNAL(del())); itsMenu->addSeparator(); itsEnableAct=itsMenu->addAction(QIcon::fromTheme("enablefont"), i18n("Enable"), this, SIGNAL(enable())); itsDisableAct=itsMenu->addAction(QIcon::fromTheme("disablefont"), i18n("Disable"), this, SIGNAL(disable())); if(!Misc::app(KFI_VIEWER).isEmpty()) itsMenu->addSeparator(); itsPrintAct=Misc::app(KFI_VIEWER).isEmpty() ? nullptr : itsMenu->addAction(QIcon::fromTheme("document-print"), i18n("Print..."), this, SIGNAL(print())); itsViewAct=Misc::app(KFI_VIEWER).isEmpty() ? nullptr : itsMenu->addAction(QIcon::fromTheme("kfontview"), i18n("Open in Font Viewer"), this, SLOT(view())); itsMenu->addSeparator(); itsMenu->addAction(QIcon::fromTheme("view-refresh"), i18n("Reload"), model, SLOT(load())); } void CFontListView::getFonts(CJobRunner::ItemList &urls, QStringList &fontNames, QSet *fonts, bool selected, bool getEnabled, bool getDisabled) { QModelIndexList selectedItems(selected ? selectedIndexes() : allIndexes()); QSet usedFonts; QModelIndex index; foreach(index, selectedItems) if(index.isValid()) { QModelIndex realIndex(itsProxy->mapToSource(index)); if(realIndex.isValid()) { if((static_cast(realIndex.internalPointer()))->isFont()) { CFontItem *font=static_cast(realIndex.internalPointer()); addFont(font, urls, fontNames, fonts, usedFonts, getEnabled, getDisabled); } else { CFamilyItem *fam=static_cast(realIndex.internalPointer()); for(int ch=0; chfontCount(); ++ch) { QModelIndex child(itsProxy->mapToSource(index.child(ch, 0))); if(child.isValid() && (static_cast(child.internalPointer()))->isFont()) { CFontItem *font=static_cast(child.internalPointer()); addFont(font, urls, fontNames, fonts, usedFonts, getEnabled, getDisabled); } } } } } fontNames=CFontList::compact(fontNames); } QSet CFontListView::getFiles() { QModelIndexList items(allIndexes()); QModelIndex index; QSet files; foreach(index, items) if(index.isValid()) { QModelIndex realIndex(itsProxy->mapToSource(index)); if(realIndex.isValid()) if((static_cast(realIndex.internalPointer()))->isFont()) { CFontItem *font=static_cast(realIndex.internalPointer()); FileCont::ConstIterator it(font->files().begin()), end(font->files().end()); for(; it!=end; ++it) { QStringList assoc; files.insert((*it).path()); Misc::getAssociatedFiles((*it).path(), assoc); QStringList::ConstIterator ait(assoc.constBegin()), aend(assoc.constEnd()); for(; ait!=aend; ++ait) files.insert(*ait); } } } return files; } void CFontListView::getPrintableFonts(QSet &items, bool selected) { QModelIndexList selectedItems(selected ? selectedIndexes() : allIndexes()); QModelIndex index; foreach(index, selectedItems) { CFontItem *font=nullptr; if(index.isValid() && 0==index.column()) { QModelIndex realIndex(itsProxy->mapToSource(index)); if(realIndex.isValid()) { if((static_cast(realIndex.internalPointer()))->isFont()) font=static_cast(realIndex.internalPointer()); else { CFamilyItem *fam=static_cast(realIndex.internalPointer()); font=fam->regularFont(); } } } if(font && !font->isBitmap() && font->isEnabled()) items.insert(Misc::TFont(font->family(), font->styleInfo())); } } void CFontListView::setFilterGroup(CGroupListItem *grp) { CGroupListItem *oldGrp(itsProxy->filterGroup()); itsProxy->setFilterGroup(grp); itsAllowDrops=grp && !grp->isCustom(); if(!Misc::root()) { bool refreshStats(false); if(!grp || !oldGrp) refreshStats=true; else { // Check to see whether we have changed from listing all fonts, // listing just system or listing personal fonts. CGroupListItem::EType aType(CGroupListItem::CUSTOM==grp->type() || CGroupListItem::ALL==grp->type() || CGroupListItem::UNCLASSIFIED==grp->type() ? CGroupListItem::CUSTOM : grp->type()), bType(CGroupListItem::CUSTOM==oldGrp->type() || CGroupListItem::ALL==oldGrp->type() || CGroupListItem::UNCLASSIFIED==oldGrp->type() ? CGroupListItem::CUSTOM : oldGrp->type()); refreshStats=aType!=bType; } if(refreshStats) itsModel->refresh(!grp || !grp->isPersonal(), !grp || !grp->isSystem()); } // when switching groups, for some reason it is not always sorted. setSortingEnabled(true); } void CFontListView::listingPercent(int percent) { // when the font list is first loaded, for some reason it is not always sorted. // re-enabling sorting here seems to fix the issue - BUG 221610 if(100==percent) setSortingEnabled(true); } void CFontListView::refreshFilter() { itsProxy->clear(); } void CFontListView::filterText(const QString &text) { itsProxy->setFilterText(text); } void CFontListView::filterCriteria(int crit, qulonglong ws, const QStringList &ft) { itsProxy->setFilterCriteria((CFontFilter::ECriteria)crit, ws, ft); } void CFontListView::stats(int &enabled, int &disabled, int &partial) { enabled=disabled=partial=0; for(int i=0; irowCount(); ++i) { QModelIndex idx(itsProxy->index(i, 0, QModelIndex())); if(!idx.isValid()) break; QModelIndex sourceIdx(itsProxy->mapToSource(idx)); if(!sourceIdx.isValid()) break; if((static_cast(sourceIdx.internalPointer()))->isFamily()) switch((static_cast(sourceIdx.internalPointer()))->status()) { case CFamilyItem::ENABLED: enabled++; break; case CFamilyItem::DISABLED: disabled++; break; case CFamilyItem::PARTIAL: partial++; break; } } } void CFontListView::selectedStatus(bool &enabled, bool &disabled) { QModelIndexList selected(selectedIndexes()); QModelIndex index; enabled=disabled=false; foreach(index, selected) { QModelIndex realIndex(itsProxy->mapToSource(index)); if(realIndex.isValid()) { if((static_cast(realIndex.internalPointer()))->isFamily()) { switch((static_cast(realIndex.internalPointer()))->status()) { case CFamilyItem::ENABLED: enabled=true; break; case CFamilyItem::DISABLED: disabled=true; break; case CFamilyItem::PARTIAL: enabled=true; disabled=true; break; } } else { if((static_cast(realIndex.internalPointer()))->isEnabled()) enabled=true; else disabled=true; } } if(enabled && disabled) break; } } QModelIndexList CFontListView::allFonts() { QModelIndexList rv; int rowCount(itsProxy->rowCount()); for(int i=0; iindex(i, 0, QModelIndex())); int childRowCount(itsProxy->rowCount(idx)); for(int j=0; jindex(j, 0, idx)); if(child.isValid()) rv.append(itsProxy->mapToSource(child)); } } return rv; } void CFontListView::selectFirstFont() { if(0==selectedIndexes().count()) for(int i=0; iindex(0, i, QModelIndex())); if(idx.isValid()) selectionModel()->select(idx, QItemSelectionModel::Select); } } void CFontListView::setSortColumn(int col) { if(col!=itsProxy->filterKeyColumn()) { itsProxy->setFilterKeyColumn(col); itsProxy->clear(); } } void CFontListView::selectionChanged(const QItemSelection &selected, const QItemSelection &deselected) { QAbstractItemView::selectionChanged(selected, deselected); if(itsModel->slowUpdates()) return; emit itemsSelected(getSelectedItems()); } QModelIndexList CFontListView::getSelectedItems() { // // Go through current selection, and for any 'font' items that are selected, // ensure 'family' item is not... QModelIndexList selectedItems(selectedIndexes()), deselectList; QModelIndex index; QSet selectedFamilies; bool en(false), dis(false); foreach(index, selectedItems) if(index.isValid()) { QModelIndex realIndex(itsProxy->mapToSource(index)); if(realIndex.isValid()) { if((static_cast(realIndex.internalPointer()))->isFont()) { CFontItem *font=static_cast(realIndex.internalPointer()); if(font->isEnabled()) en=true; else dis=true; if(!selectedFamilies.contains(font->parent())) { selectedFamilies.insert(font->parent()); for(int i=0; imapFromSource( itsModel->createIndex(font->parent()->rowNumber(), i, font->parent()))); } } else { switch((static_cast(realIndex.internalPointer()))->status()) { case CFamilyItem::ENABLED: en=true; break; case CFamilyItem::DISABLED: dis=true; break; case CFamilyItem::PARTIAL: en=dis=true; break; } } } } if(deselectList.count()) foreach(index, deselectList) selectionModel()->select(index, QItemSelectionModel::Deselect); QModelIndexList sel; QSet pointers; selectedItems=selectedIndexes(); foreach(index, selectedItems) { QModelIndex idx(itsProxy->mapToSource(index)); if(!pointers.contains(idx.internalPointer())) { pointers.insert(idx.internalPointer()); sel.append(idx); } } return sel; } void CFontListView::itemCollapsed(const QModelIndex &idx) { if(idx.isValid()) { QModelIndex index(itsProxy->mapToSource(idx)); if(index.isValid() && (static_cast(index.internalPointer()))->isFamily()) { CFamilyItem *fam=static_cast(index.internalPointer()); CFontItemCont::ConstIterator it(fam->fonts().begin()), end(fam->fonts().end()); for(; it!=end; ++it) for(int i=0; iselect(itsProxy->mapFromSource(itsModel->createIndex((*it)->rowNumber(), i, *it)), QItemSelectionModel::Deselect); } } } static bool isScalable(const QString &str) { QByteArray cFile(QFile::encodeName(str)); return Misc::checkExt(cFile, "ttf") || Misc::checkExt(cFile, "otf") || Misc::checkExt(cFile, "ttc") || Misc::checkExt(cFile, "pfa") || Misc::checkExt(cFile, "pfb"); } void CFontListView::view() { // Number of fonts user has selected, before we ask if they really want to view them all... static const int constMaxBeforePrompt=10; QModelIndexList selectedItems(selectedIndexes()); QModelIndex index; QSet fonts; foreach(index, selectedItems) { QModelIndex realIndex(itsProxy->mapToSource(index)); if(realIndex.isValid()) { if((static_cast(realIndex.internalPointer()))->isFont()) { CFontItem *font(static_cast(realIndex.internalPointer())); fonts.insert(font); } else { CFontItem *font((static_cast(realIndex.internalPointer()))->regularFont()); if(font) fonts.insert(font); } } } if(fonts.count() && (fonts.count()::ConstIterator it(fonts.begin()), end(fonts.end()); QStringList args; for(; it!=end; ++it) { QString file; int index(0); if(!(*it)->isEnabled()) { // For a disabled font, we need to find the first scalable font entry in its file list... FileCont::ConstIterator fit((*it)->files().begin()), fend((*it)->files().end()); for(; fit!=fend; ++fit) if(isScalable((*fit).path())) { file=(*fit).path(); index=(*fit).index(); break; } if(file.isEmpty()) { file=(*it)->fileName(); index=(*it)->index(); } } args << FC::encode((*it)->family(), (*it)->styleInfo(), file, index).url(); } QProcess::startDetached(Misc::app(KFI_VIEWER), args); } } QModelIndexList CFontListView::allIndexes() { QModelIndexList rv; int rowCount(itsProxy->rowCount()); for(int i=0; iindex(i, 0, QModelIndex())); int childRowCount(itsProxy->rowCount(idx)); rv.append(idx); for(int j=0; jindex(j, 0, idx)); if(child.isValid()) rv.append(child); } } return rv; } void CFontListView::startDrag(Qt::DropActions supportedActions) { QModelIndexList indexes(selectedIndexes()); if (indexes.count()) { QMimeData *data = model()->mimeData(indexes); if (!data) return; QModelIndex index(itsProxy->mapToSource(indexes.first())); const char *icon="application-x-font-pcf"; if(index.isValid()) { CFontItem *font=(static_cast(index.internalPointer()))->isFont() ? static_cast(index.internalPointer()) : (static_cast(index.internalPointer()))->regularFont(); if(font && !font->isBitmap()) // if("application/x-font-type1"==font->mimetype()) // icon="application-x-font-type1"; // else icon="application-x-font-ttf"; } QPoint hotspot; QPixmap pix(DesktopIcon(icon, KIconLoader::SizeMedium)); hotspot.setX(0); // pix.width()/2); hotspot.setY(0); // pix.height()/2); QDrag *drag = new QDrag(this); drag->setPixmap(pix); drag->setMimeData(data); drag->setHotSpot(hotspot); drag->start(supportedActions); } } void CFontListView::dragEnterEvent(QDragEnterEvent *event) { if(itsAllowDrops && event->mimeData()->hasFormat("text/uri-list")) // "application/x-kde-urilist" ?? event->acceptProposedAction(); } void CFontListView::dropEvent(QDropEvent *event) { if(itsAllowDrops && event->mimeData()->hasFormat("text/uri-list")) { event->acceptProposedAction(); QList urls(event->mimeData()->urls()); QList::ConstIterator it(urls.begin()), end(urls.end()); QSet kurls; QMimeDatabase db; for (; it!=end; ++it) { QMimeType mime = db.mimeTypeForUrl(*it); foreach (const QString &fontMime, CFontList::fontMimeTypes) { if (mime.inherits(fontMime)) { kurls.insert(*it); break; } } } if(!kurls.isEmpty()) emit fontsDropped(kurls); } } void CFontListView::contextMenuEvent(QContextMenuEvent *ev) { bool valid(indexAt(ev->pos()).isValid()); itsDeleteAct->setEnabled(valid); bool en(false), dis(false); QModelIndexList selectedItems(selectedIndexes()); QModelIndex index; foreach(index, selectedItems) { QModelIndex realIndex(itsProxy->mapToSource(index)); if(realIndex.isValid()) { if((static_cast(realIndex.internalPointer()))->isFont()) { if((static_cast(realIndex.internalPointer())->isEnabled())) en=true; else dis=true; } else { switch((static_cast(realIndex.internalPointer()))->status()) { case CFamilyItem::ENABLED: en=true; break; case CFamilyItem::DISABLED: dis=true; break; case CFamilyItem::PARTIAL: en=dis=true; break; } } } if(en && dis) break; } itsEnableAct->setEnabled(dis); itsDisableAct->setEnabled(en); if(itsPrintAct) itsPrintAct->setEnabled(en|dis); if(itsViewAct) itsViewAct->setEnabled(en|dis); itsMenu->popup(ev->globalPos()); } bool CFontListView::viewportEvent(QEvent *event) { executeDelayedItemsLayout(); return QTreeView::viewportEvent(event); } } diff --git a/kcms/kfontinst/kcmfontinst/FontsPackage.cpp b/kcms/kfontinst/kcmfontinst/FontsPackage.cpp index 08291cd41..06b7d5ed8 100644 --- a/kcms/kfontinst/kcmfontinst/FontsPackage.cpp +++ b/kcms/kfontinst/kcmfontinst/FontsPackage.cpp @@ -1,97 +1,97 @@ /* * KFontInst - KDE Font Installer * * Copyright 2009 Craig Drummond * * ---- * * 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. * * This program 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 * General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; see the file COPYING. If not, write to * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, * Boston, MA 02110-1301, USA. */ #include #include #include #include "FontsPackage.h" #include "KfiConstants.h" #include "Misc.h" namespace KFI { namespace FontsPackage { QSet extract(const QString &fileName, QTemporaryDir **tempDir) { QSet urls; if(!tempDir) return urls; KZip zip(fileName); if(zip.open(QIODevice::ReadOnly)) { const KArchiveDirectory *zipDir=zip.directory(); if(zipDir) { QStringList fonts(zipDir->entries()); if(!fonts.isEmpty()) { QStringList::ConstIterator it(fonts.begin()), end(fonts.end()); for(; it!=end; ++it) { const KArchiveEntry *entry=zipDir->entry(*it); if(entry && entry->isFile()) { if(!(*tempDir)) { (*tempDir)=new QTemporaryDir(QDir::tempPath() + "/" KFI_TMP_DIR_PREFIX); (*tempDir)->setAutoRemove(true); } ((KArchiveFile *)entry)->copyTo((*tempDir)->path()); QString name(entry->name()); // // Cant install hidden fonts, therefore need to // unhide 1st! if(Misc::isHidden(name)) { ::rename(QFile::encodeName((*tempDir)->filePath(name)).data(), QFile::encodeName((*tempDir)->filePath(name.mid(1))).data()); - name=name.mid(1); + name.remove(0, 1); } urls.insert(QUrl((*tempDir)->filePath(name))); } } } } } return urls; } } } diff --git a/kcms/kfontinst/kcmfontinst/JobRunner.cpp b/kcms/kfontinst/kcmfontinst/JobRunner.cpp index 9c3e875ec..29082ad9d 100644 --- a/kcms/kfontinst/kcmfontinst/JobRunner.cpp +++ b/kcms/kfontinst/kcmfontinst/JobRunner.cpp @@ -1,809 +1,809 @@ /* * KFontInst - KDE Font Installer * * Copyright 2003-2007 Craig Drummond * * ---- * * 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. * * This program 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 * General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; see the file COPYING. If not, write to * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, * Boston, MA 02110-1301, USA. */ #include "JobRunner.h" #include "KfiConstants.h" #include "Misc.h" #include "Fc.h" #include "ActionLabel.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "config-fontinst.h" #define CFG_GROUP "Runner Dialog" #define CFG_DONT_SHOW_FINISHED_MSG "DontShowFinishedMsg" namespace KFI { Q_GLOBAL_STATIC(FontInstInterface, theInterface) FontInstInterface * CJobRunner::dbus() { return theInterface; } QString CJobRunner::folderName(bool sys) { if(!theInterface) return QString(); QDBusPendingReply reply=theInterface->folderName(sys); reply.waitForFinished(); return reply.isError() ? QString() : reply.argumentAt<0>(); } void CJobRunner::startDbusService() { if (!QDBusConnection::sessionBus().interface()->isServiceRegistered(OrgKdeFontinstInterface::staticInterfaceName())) { const QString fontinst = QStringLiteral(KFONTINST_LIB_EXEC_DIR"/fontinst"); qDebug() << "Service " << OrgKdeFontinstInterface::staticInterfaceName() << " not registered, starting" << fontinst; QProcess::startDetached(fontinst, QStringList()); } } static const int constDownloadFailed=-1; static const int constInterfaceCheck=5*1000; static void decode(const QUrl &url, Misc::TFont &font, bool &system) { font=FC::decode(url); QUrlQuery query(url); system = (query.hasQueryItem("sys") && query.queryItemValue("sys") == QLatin1String("true")); } QUrl CJobRunner::encode(const QString &family, quint32 style, bool system) { QUrl url(FC::encode(family, style)); url.addQueryItem("sys", system ? "true" : "false"); return url; } enum EPages { PAGE_PROGRESS, PAGE_SKIP, PAGE_ERROR, PAGE_CANCEL, PAGE_COMPLETE }; enum Response { RESP_CONTINUE, RESP_AUTO, RESP_CANCEL }; static void addIcon(QGridLayout *layout, QFrame *page, const char *iconName, int iconSize) { QLabel *icon=new QLabel(page); icon->setPixmap(QIcon::fromTheme(iconName).pixmap(iconSize)); icon->setSizePolicy(QSizePolicy::Maximum, QSizePolicy::Maximum); layout->addWidget(icon, 0, 0); } CJobRunner::CJobRunner(QWidget *parent, int xid) : QDialog(parent), itsIt(itsUrls.end()), itsEnd(itsIt), itsAutoSkip(false), itsCancelClicked(false), itsModified(false), itsTempDir(nullptr) { setModal(true); if(nullptr==parent && 0!=xid) XSetTransientForHint(QX11Info::display(), winId(), xid); itsButtonBox = new QDialogButtonBox; connect(itsButtonBox, &QDialogButtonBox::clicked, this, &CJobRunner::slotButtonClicked); itsSkipButton = new QPushButton(i18n("Skip")); itsButtonBox->addButton(itsSkipButton, QDialogButtonBox::ActionRole); itsSkipButton->hide(); itsAutoSkipButton = new QPushButton(i18n("AutoSkip")); itsButtonBox->addButton(itsAutoSkipButton, QDialogButtonBox::ActionRole); itsAutoSkipButton->hide(); itsStack = new QStackedWidget(this); QVBoxLayout *mainLayout = new QVBoxLayout; setLayout(mainLayout); mainLayout->addWidget(itsStack); mainLayout->addWidget(itsButtonBox); QStyleOption option; option.initFrom(this); int iconSize=style()->pixelMetric(QStyle::PM_MessageBoxIconSize, &option, this); QFrame *page = new QFrame(itsStack); QGridLayout *layout=new QGridLayout(page); itsStatusLabel=new QLabel(page); itsProgress=new QProgressBar(page); // itsStatusLabel->setWordWrap(true); layout->addWidget(itsActionLabel = new CActionLabel(this), 0, 0, 2, 1); layout->addWidget(itsStatusLabel, 0, 1); layout->addWidget(itsProgress, 1, 1); layout->addItem(new QSpacerItem(0, 0, QSizePolicy::Fixed, QSizePolicy::Expanding), 2, 0); itsStack->insertWidget(PAGE_PROGRESS, page); page=new QFrame(itsStack); layout=new QGridLayout(page); itsSkipLabel=new QLabel(page); itsSkipLabel->setWordWrap(true); addIcon(layout, page, "dialog-error", iconSize); layout->addWidget(itsSkipLabel, 0, 1); layout->addItem(new QSpacerItem(0, 0, QSizePolicy::Fixed, QSizePolicy::Expanding), 1, 0); itsStack->insertWidget(PAGE_SKIP, page); page=new QFrame(itsStack); layout=new QGridLayout(page); itsErrorLabel=new QLabel(page); itsErrorLabel->setWordWrap(true); addIcon(layout, page, "dialog-error", iconSize); layout->addWidget(itsErrorLabel, 0, 1); layout->addItem(new QSpacerItem(0, 0, QSizePolicy::Fixed, QSizePolicy::Expanding), 1, 0); itsStack->insertWidget(PAGE_ERROR, page); page=new QFrame(itsStack); layout=new QGridLayout(page); QLabel *cancelLabel=new QLabel(i18n("

Cancel?

Are you sure you wish to cancel?

"), page); cancelLabel->setWordWrap(true); addIcon(layout, page, "dialog-warning", iconSize); layout->addWidget(cancelLabel, 0, 1); layout->addItem(new QSpacerItem(0, 0, QSizePolicy::Fixed, QSizePolicy::Expanding), 1, 0); itsStack->insertWidget(PAGE_CANCEL, page); if(KSharedConfig::openConfig(KFI_UI_CFG_FILE)->group(CFG_GROUP).readEntry(CFG_DONT_SHOW_FINISHED_MSG, false)) itsDontShowFinishedMsg= nullptr; else { page=new QFrame(itsStack); layout=new QGridLayout(page); QLabel *finishedLabel=new QLabel(i18n("

Finished

" "

Please note that any open applications will need to be " "restarted in order for any changes to be noticed.

"), page); finishedLabel->setWordWrap(true); addIcon(layout, page, "dialog-information", iconSize); layout->addWidget(finishedLabel, 0, 1); layout->addItem(new QSpacerItem(0, 0, QSizePolicy::Fixed, QSizePolicy::Expanding), 1, 0); itsDontShowFinishedMsg = new QCheckBox(i18n("Do not show this message again"), page); itsDontShowFinishedMsg->setChecked(false); layout->addItem(new QSpacerItem(0, layout->spacing(), QSizePolicy::Fixed, QSizePolicy::Fixed), 2, 0); layout->addWidget(itsDontShowFinishedMsg, 3, 1); layout->addItem(new QSpacerItem(0, 0, QSizePolicy::Fixed, QSizePolicy::Expanding), 4, 0); itsStack->insertWidget(PAGE_COMPLETE, page); } QDBusServiceWatcher *watcher = new QDBusServiceWatcher(QLatin1String(OrgKdeFontinstInterface::staticInterfaceName()), QDBusConnection::sessionBus(), QDBusServiceWatcher::WatchForOwnerChange, this); connect(watcher, SIGNAL(serviceOwnerChanged(QString,QString,QString)), SLOT(dbusServiceOwnerChanged(QString,QString,QString))); connect(dbus(), SIGNAL(status(int,int)), SLOT(dbusStatus(int,int))); setMinimumSize(420, 160); } CJobRunner::~CJobRunner() { delete itsTempDir; } void CJobRunner::getAssociatedUrls(const QUrl &url, QList &list, bool afmAndPfm, QWidget *widget) { QString ext(url.path()); int dotPos(ext.lastIndexOf('.')); bool check(false); if(-1==dotPos) // Hmm, no extension - check anyway... check=true; else // Cool, got an extension - see if it is a Type1 font... { ext=ext.mid(dotPos+1); check=0==ext.compare("pfa", Qt::CaseInsensitive) || 0==ext.compare("pfb", Qt::CaseInsensitive); } if(check) { const char *afm[]={"afm", "AFM", "Afm", nullptr}, *pfm[]={"pfm", "PFM", "Pfm", nullptr}; bool gotAfm(false), localFile(url.isLocalFile()); int e; for(e=0; afm[e]; ++e) { QUrl statUrl(url); statUrl.setPath(Misc::changeExt(url.path(), afm[e])); bool urlExists = false; if (localFile) { urlExists = Misc::fExists(statUrl.toLocalFile()); } else { auto job = KIO::stat(statUrl); KJobWidgets::setWindow(job, widget); job->exec(); urlExists = !job->error(); } if (urlExists) { list.append(statUrl); gotAfm=true; break; } } if(afmAndPfm || !gotAfm) for(e=0; pfm[e]; ++e) { QUrl statUrl(url); statUrl.setPath(Misc::changeExt(url.path(), pfm[e])); bool urlExists = false; if (localFile) { urlExists = Misc::fExists(statUrl.toLocalFile()); } else { auto job = KIO::stat(statUrl); KJobWidgets::setWindow(job, widget); job->exec(); urlExists = !job->error(); } if (urlExists) { list.append(statUrl); break; } } } } static void addEnableActions(CJobRunner::ItemList &urls) { CJobRunner::ItemList modified; CJobRunner::ItemList::ConstIterator it(urls.constBegin()), end(urls.constEnd()); for(; it!=end; ++it) { if((*it).isDisabled) { CJobRunner::Item item(*it); item.fileName=QLatin1String("--"); modified.append(item); } modified.append(*it); } urls=modified; } int CJobRunner::exec(ECommand cmd, const ItemList &urls, bool destIsSystem) { itsAutoSkip=itsCancelClicked=itsModified=false; switch(cmd) { case CMD_INSTALL: setWindowTitle(i18n("Installing")); break; case CMD_DELETE: setWindowTitle(i18n("Uninstalling")); break; case CMD_ENABLE: setWindowTitle(i18n("Enabling")); break; case CMD_MOVE: setWindowTitle(i18n("Moving")); break; case CMD_UPDATE: setWindowTitle(i18n("Updating")); itsModified=true; break; case CMD_REMOVE_FILE: setWindowTitle(i18n("Removing")); break; default: case CMD_DISABLE: setWindowTitle(i18n("Disabling")); } itsDestIsSystem=destIsSystem; itsUrls=urls; if(CMD_INSTALL==cmd) std::sort(itsUrls.begin(), itsUrls.end()); // Sort list of fonts so that we have type1 fonts followed by their metrics... else if(CMD_MOVE==cmd) addEnableActions(itsUrls); itsIt=itsUrls.constBegin(); itsEnd=itsUrls.constEnd(); itsPrev=itsEnd; itsProgress->setValue(0); itsProgress->setRange(0, itsUrls.count()+1); itsProgress->show(); itsCmd=cmd; itsCurrentFile=QString(); itsStatusLabel->setText(QString()); setPage(PAGE_PROGRESS); QTimer::singleShot(0, this, SLOT(doNext())); QTimer::singleShot(constInterfaceCheck, this, SLOT(checkInterface())); itsActionLabel->startAnimation(); int rv=QDialog::exec(); if(itsTempDir) { delete itsTempDir; itsTempDir= nullptr; } return rv; } void CJobRunner::doNext() { if(itsIt==itsEnd/* || CMD_UPDATE==itsCmd*/) { if(itsModified) { // Force reconfig if command was already set to update... dbus()->reconfigure(getpid(), CMD_UPDATE==itsCmd); itsCmd=CMD_UPDATE; itsStatusLabel->setText(i18n("Updating font configuration. Please wait...")); itsProgress->setValue(itsProgress->maximum()); emit configuring(); } else { itsActionLabel->stopAnimation(); if(PAGE_ERROR!=itsStack->currentIndex()) reject(); } } else { Misc::TFont font; bool system; switch(itsCmd) { case CMD_INSTALL: { itsCurrentFile=fileName((*itsIt)); if(itsCurrentFile.isEmpty()) // Failed to download... dbusStatus(getpid(), constDownloadFailed); else { // Create AFM if this is a PFM, and the previous was not the AFM for this font... bool createAfm=Item::TYPE1_PFM==(*itsIt).type && (itsPrev==itsEnd || (*itsIt).fileName!=(*itsPrev).fileName || Item::TYPE1_AFM!=(*itsPrev).type); dbus()->install(itsCurrentFile, createAfm, itsDestIsSystem, getpid(), false); } break; } case CMD_DELETE: decode(*itsIt, font, system); dbus()->uninstall(font.family, font.styleInfo, system, getpid(), false); break; case CMD_ENABLE: decode(*itsIt, font, system); dbus()->enable(font.family, font.styleInfo, system, getpid(), false); break; case CMD_DISABLE: decode(*itsIt, font, system); dbus()->disable(font.family, font.styleInfo, system, getpid(), false); break; case CMD_MOVE: decode(*itsIt, font, system); // To 'Move' a disabled font, we first need to enable it. To accomplish this, JobRunner creates a 'fake' entry // with the filename "--" if((*itsIt).fileName==QLatin1String("--")) { setWindowTitle(i18n("Enabling")); dbus()->enable(font.family, font.styleInfo, system, getpid(), false); } else { if(itsPrev!=itsEnd && (*itsPrev).fileName==QLatin1String("--")) setWindowTitle(i18n("Moving")); dbus()->move(font.family, font.styleInfo, itsDestIsSystem, getpid(), false); } break; case CMD_REMOVE_FILE: decode(*itsIt, font, system); dbus()->removeFile(font.family, font.styleInfo, (*itsIt).fileName, system, getpid(), false); break; default: break; } itsStatusLabel->setText(CMD_INSTALL==itsCmd ? (*itsIt).url() : FC::createName(FC::decode(*itsIt))); itsProgress->setValue(itsProgress->value()+1); // Keep copy of this iterator - so that can check whether AFM should be created. itsPrev=itsIt; } } void CJobRunner::checkInterface() { if(itsIt==itsUrls.constBegin() && !FontInst::isStarted(dbus())) { setPage(PAGE_ERROR, i18n("Unable to start backend.")); itsActionLabel->stopAnimation(); itsIt=itsEnd; } } void CJobRunner::dbusServiceOwnerChanged(const QString &name, const QString &from, const QString &to) { if(to.isEmpty() && !from.isEmpty() && name==QLatin1String(OrgKdeFontinstInterface::staticInterfaceName()) && itsIt!=itsEnd) { setPage(PAGE_ERROR, i18n("Backend died, but has been restarted. Please try again.")); itsActionLabel->stopAnimation(); itsIt=itsEnd; } } void CJobRunner::dbusStatus(int pid, int status) { if(pid!=getpid()) return; if(CMD_UPDATE==itsCmd) { setPage(PAGE_COMPLETE); return; } itsLastDBusStatus=status; if(itsCancelClicked) { itsActionLabel->stopAnimation(); setPage(PAGE_CANCEL); return; /* if(RESP_CANCEL==itsResponse) itsIt=itsEnd; itsCancelClicked=false; setPage(PAGE_PROGRESS); itsActionLabel->startAnimation(); */ } // itsIt will equal itsEnd if user decided to cancel the current op if(itsIt==itsEnd) { doNext(); } else if (0==status) { itsModified=true; ++itsIt; doNext(); } else { bool cont(itsAutoSkip && itsUrls.count()>1); QString currentName((*itsIt).fileName); if(!cont) { itsActionLabel->stopAnimation(); if(FontInst::STATUS_SERVICE_DIED==status) { setPage(PAGE_ERROR, errorString(status)); itsIt=itsEnd; } else { ItemList::ConstIterator lastPartOfCurrent(itsIt), next(itsIt==itsEnd ? itsEnd : itsIt+1); // If we're installing a Type1 font, and its already installed - then we need to skip past AFM/PFM if(next!=itsEnd && Item::TYPE1_FONT==(*itsIt).type && (*next).fileName==currentName && (Item::TYPE1_AFM==(*next).type || Item::TYPE1_PFM==(*next).type)) { next++; if(next!=itsEnd && (*next).fileName==currentName && (Item::TYPE1_AFM==(*next).type || Item::TYPE1_PFM==(*next).type)) next++; } if(1==itsUrls.count() || next==itsEnd) setPage(PAGE_ERROR, errorString(status)); else { setPage(PAGE_SKIP, errorString(status)); return; } } } contineuToNext(cont); } } void CJobRunner::contineuToNext(bool cont) { itsActionLabel->startAnimation(); if(cont) { if(CMD_INSTALL==itsCmd && Item::TYPE1_FONT==(*itsIt).type) // Did we error on a pfa/pfb? if so, exclude the afm/pfm... { QString currentName((*itsIt).fileName); ++itsIt; // Skip afm/pfm if(itsIt!=itsEnd && (*itsIt).fileName==currentName && (Item::TYPE1_AFM==(*itsIt).type || Item::TYPE1_PFM==(*itsIt).type)) ++itsIt; // Skip pfm/afm if(itsIt!=itsEnd && (*itsIt).fileName==currentName && (Item::TYPE1_AFM==(*itsIt).type || Item::TYPE1_PFM==(*itsIt).type)) ++itsIt; } else ++itsIt; } else { itsUrls.empty(); itsIt=itsEnd=itsUrls.constEnd(); } doNext(); } void CJobRunner::slotButtonClicked(QAbstractButton *button) { switch(itsStack->currentIndex()) { case PAGE_PROGRESS: if(itsIt!=itsEnd) itsCancelClicked=true; break; case PAGE_SKIP: setPage(PAGE_PROGRESS); if (button == itsSkipButton) { contineuToNext(true); } else if (button == itsAutoSkipButton) { itsAutoSkip=true; contineuToNext(true); } else { contineuToNext(false); } break; case PAGE_CANCEL: if(button == itsButtonBox->button(QDialogButtonBox::Yes)) itsIt=itsEnd; itsCancelClicked=false; setPage(PAGE_PROGRESS); itsActionLabel->startAnimation(); // Now continue... dbusStatus(getpid(), itsLastDBusStatus); break; case PAGE_COMPLETE: if(itsDontShowFinishedMsg) { KConfigGroup grp(KSharedConfig::openConfig(KFI_UI_CFG_FILE)->group(CFG_GROUP)); grp.writeEntry(CFG_DONT_SHOW_FINISHED_MSG, itsDontShowFinishedMsg->isChecked()); } case PAGE_ERROR: QDialog::accept(); break; } } void CJobRunner::closeEvent(QCloseEvent *e) { if(PAGE_COMPLETE!=itsStack->currentIndex()) { e->ignore(); slotButtonClicked(PAGE_CANCEL==itsStack->currentIndex() ? itsButtonBox->button(QDialogButtonBox::No) : itsButtonBox->button(QDialogButtonBox::Cancel)); } } void CJobRunner::setPage(int page, const QString &msg) { itsStack->setCurrentIndex(page); switch(page) { case PAGE_PROGRESS: itsButtonBox->setStandardButtons(QDialogButtonBox::Cancel); itsSkipButton->hide(); itsAutoSkipButton->hide(); break; case PAGE_SKIP: itsSkipLabel->setText(i18n("

Error

")+QLatin1String("

")+msg+QLatin1String("

")); itsButtonBox->setStandardButtons(QDialogButtonBox::Cancel); itsSkipButton->show(); itsAutoSkipButton->show(); break; case PAGE_ERROR: itsErrorLabel->setText(i18n("

Error

")+QLatin1String("

")+msg+QLatin1String("

")); itsButtonBox->setStandardButtons(QDialogButtonBox::Cancel); itsSkipButton->hide(); itsAutoSkipButton->hide(); break; case PAGE_CANCEL: itsButtonBox->setStandardButtons(QDialogButtonBox::Yes|QDialogButtonBox::No); itsSkipButton->hide(); itsAutoSkipButton->hide(); break; case PAGE_COMPLETE: if(!itsDontShowFinishedMsg || itsDontShowFinishedMsg->isChecked()) { QDialog::accept(); } else { itsButtonBox->setStandardButtons(QDialogButtonBox::Close); itsSkipButton->hide(); itsAutoSkipButton->hide(); } break; } } QString CJobRunner::fileName(const QUrl &url) { if(url.isLocalFile()) return url.toLocalFile(); else { auto job = KIO::mostLocalUrl(url); job->exec(); QUrl local = job->mostLocalUrl(); if(local.isLocalFile()) return local.toLocalFile(); // Yipee! no need to download!! else { // Need to do actual download... if(!itsTempDir) { itsTempDir=new QTemporaryDir(QDir::tempPath() + "/fontinst"); itsTempDir->setAutoRemove(true); } QString tempName(itsTempDir->filePath(Misc::getFile(url.path()))); auto job = KIO::file_copy(url, QUrl::fromLocalFile(tempName), -1, KIO::Overwrite); if (job->exec()) return tempName; else return QString(); } } } QString CJobRunner::errorString(int value) const { Misc::TFont font(FC::decode(*itsIt)); QString urlStr; if(CMD_REMOVE_FILE==itsCmd) urlStr=(*itsIt).fileName; else if(font.family.isEmpty()) urlStr=(*itsIt).url(); else urlStr=FC::createName(font.family, font.styleInfo); switch(value) { case constDownloadFailed: return i18n("Failed to download %1", urlStr); case FontInst::STATUS_SERVICE_DIED: return i18n("System backend died. Please try again.
%1", urlStr); case FontInst::STATUS_BITMAPS_DISABLED: return i18n("%1 is a bitmap font, and these have been disabled on your system.", urlStr); case FontInst::STATUS_ALREADY_INSTALLED: return i18n("%1 contains the font %2, which is already installed on your system.", urlStr, FC::getName(itsCurrentFile)); case FontInst::STATUS_NOT_FONT_FILE: return i18n("%1 is not a font.", urlStr); case FontInst::STATUS_PARTIAL_DELETE: return i18n("Could not remove all files associated with %1", urlStr); case FontInst::STATUS_NO_SYS_CONNECTION: return i18n("Failed to start the system daemon.
%1", urlStr); case KIO::ERR_FILE_ALREADY_EXIST: { QString name(Misc::modifyName(Misc::getFile((*itsIt).fileName))), destFolder(Misc::getDestFolder(folderName(itsDestIsSystem), name)); return i18n("%1 already exists.", destFolder+name); } case KIO::ERR_DOES_NOT_EXIST: return i18n("%1 does not exist.", urlStr); case KIO::ERR_WRITE_ACCESS_DENIED: return i18n("Permission denied.
%1", urlStr); case KIO::ERR_UNSUPPORTED_ACTION: return i18n("Unsupported action.
%1", urlStr); case KIO::ERR_COULD_NOT_AUTHENTICATE: return i18n("Authentication failed.
%1", urlStr); default: return i18n("Unexpected error while processing: %1", urlStr); } } CJobRunner::Item::Item(const QUrl &u, const QString &n, bool dis) : QUrl(u), name(n), fileName(Misc::getFile(u.path())), isDisabled(dis) { type=Misc::checkExt(fileName, "pfa") || Misc::checkExt(fileName, "pfb") ? TYPE1_FONT : Misc::checkExt(fileName, "afm") ? TYPE1_AFM : Misc::checkExt(fileName, "pfm") ? TYPE1_PFM : OTHER_FONT; if(OTHER_FONT!=type) { int pos(fileName.lastIndexOf('.')); if(-1!=pos) - fileName=fileName.left(pos); + fileName.truncate(pos); } } CJobRunner::Item::Item(const QString &file, const QString &family, quint32 style, bool system) : QUrl(CJobRunner::encode(family, style, system)), fileName(file), type(OTHER_FONT) { } bool CJobRunner::Item::operator<(const Item &o) const { int nameComp(fileName.compare(o.fileName)); return nameComp<0 || (0==nameComp && type ** Copyright (C) 2002 by Karol Szwed ** This application is freely distributable under the GNU Public License. ** *****************************************************************************/ #include #include #include #include #include #include #undef Unsorted #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "krdb.h" #if HAVE_X11 #include #include #endif inline const char * gtkEnvVar(int version) { return 2==version ? "GTK2_RC_FILES" : "GTK_RC_FILES"; } inline const char * sysGtkrc(int version) { if(2==version) { if(access("/etc/opt/gnome/gtk-2.0", F_OK) == 0) return "/etc/opt/gnome/gtk-2.0/gtkrc"; else return "/etc/gtk-2.0/gtkrc"; } else { if(access("/etc/opt/gnome/gtk", F_OK) == 0) return "/etc/opt/gnome/gtk/gtkrc"; else return "/etc/gtk/gtkrc"; } } inline const char * userGtkrc(int version) { return 2==version ? "/.gtkrc-2.0" : "/.gtkrc"; } // ----------------------------------------------------------------------------- static QString writableGtkrc(int version) { QString gtkrc = QStandardPaths::writableLocation(QStandardPaths::GenericConfigLocation); QDir dir; dir.mkpath(gtkrc); gtkrc += 2==version?"/gtkrc-2.0":"/gtkrc"; return gtkrc; } // ----------------------------------------------------------------------------- static void applyGtkStyles(bool active, int version) { QString gtkkde = writableGtkrc(version); QByteArray gtkrc = getenv(gtkEnvVar(version)); QStringList list = QFile::decodeName(gtkrc).split( QLatin1Char(':')); QString userHomeGtkrc = QDir::homePath()+userGtkrc(version); if (!list.contains(userHomeGtkrc)) list.prepend(userHomeGtkrc); QLatin1String systemGtkrc = QLatin1String(sysGtkrc(version)); if (!list.contains(systemGtkrc)) list.prepend(systemGtkrc); list.removeAll(QLatin1String("")); list.removeAll(gtkkde); list.append(gtkkde); // Pass env. var to kdeinit. QString name = gtkEnvVar(version); - QString value = list.join(QLatin1String(":")); + QString value = list.join(QLatin1Char(':')); org::kde::KLauncher klauncher(QStringLiteral("org.kde.klauncher5"), QStringLiteral("/KLauncher"), QDBusConnection::sessionBus()); klauncher.setLaunchEnv(name, value); } // ----------------------------------------------------------------------------- static void applyQtColors( KSharedConfigPtr kglobalcfg, QSettings& settings, QPalette& newPal ) { QStringList actcg, inactcg, discg; /* export kde color settings */ int i; for (i = 0; i < QPalette::NColorRoles; i++) actcg << newPal.color(QPalette::Active, (QPalette::ColorRole) i).name(); for (i = 0; i < QPalette::NColorRoles; i++) inactcg << newPal.color(QPalette::Inactive, (QPalette::ColorRole) i).name(); for (i = 0; i < QPalette::NColorRoles; i++) discg << newPal.color(QPalette::Disabled, (QPalette::ColorRole) i).name(); settings.setValue(QStringLiteral("/qt/Palette/active"), actcg); settings.setValue(QStringLiteral("/qt/Palette/inactive"), inactcg); settings.setValue(QStringLiteral("/qt/Palette/disabled"), discg); // export kwin's colors to qtrc for kstyle to use KConfigGroup wmCfgGroup(kglobalcfg, "WM"); // active colors QColor clr = newPal.color( QPalette::Active, QPalette::Window ); clr = wmCfgGroup.readEntry("activeBackground", clr); settings.setValue(QStringLiteral("/qt/KWinPalette/activeBackground"), clr.name()); if (QPixmap::defaultDepth() > 8) clr = clr.darker(110); clr = wmCfgGroup.readEntry("activeBlend", clr); settings.setValue(QStringLiteral("/qt/KWinPalette/activeBlend"), clr.name()); clr = newPal.color( QPalette::Active, QPalette::HighlightedText ); clr = wmCfgGroup.readEntry("activeForeground", clr); settings.setValue(QStringLiteral("/qt/KWinPalette/activeForeground"), clr.name()); clr = newPal.color( QPalette::Active,QPalette::Window ); clr = wmCfgGroup.readEntry("frame", clr); settings.setValue(QStringLiteral("/qt/KWinPalette/frame"), clr.name()); clr = wmCfgGroup.readEntry("activeTitleBtnBg", clr); settings.setValue(QStringLiteral("/qt/KWinPalette/activeTitleBtnBg"), clr.name()); // inactive colors clr = newPal.color(QPalette::Inactive, QPalette::Window); clr = wmCfgGroup.readEntry("inactiveBackground", clr); settings.setValue(QStringLiteral("/qt/KWinPalette/inactiveBackground"), clr.name()); if (QPixmap::defaultDepth() > 8) clr = clr.darker(110); clr = wmCfgGroup.readEntry("inactiveBlend", clr); settings.setValue(QStringLiteral("/qt/KWinPalette/inactiveBlend"), clr.name()); clr = newPal.color(QPalette::Inactive, QPalette::Window).darker(); clr = wmCfgGroup.readEntry("inactiveForeground", clr); settings.setValue(QStringLiteral("/qt/KWinPalette/inactiveForeground"), clr.name()); clr = newPal.color(QPalette::Inactive, QPalette::Window); clr = wmCfgGroup.readEntry("inactiveFrame", clr); settings.setValue(QStringLiteral("/qt/KWinPalette/inactiveFrame"), clr.name()); clr = wmCfgGroup.readEntry("inactiveTitleBtnBg", clr); settings.setValue(QStringLiteral("/qt/KWinPalette/inactiveTitleBtnBg"), clr.name()); KConfigGroup kdeCfgGroup(kglobalcfg, "KDE"); settings.setValue(QStringLiteral("/qt/KDE/contrast"), kdeCfgGroup.readEntry("contrast", 7)); } // ----------------------------------------------------------------------------- static void applyQtSettings( KSharedConfigPtr kglobalcfg, QSettings& settings ) { /* export font settings */ // NOTE keep this in sync with kfontsettingsdata in plasma-integration (cf. also Bug 378262) QFont defaultFont(QStringLiteral("Noto Sans"), 10, -1); defaultFont.setStyleHint(QFont::SansSerif); const KConfigGroup configGroup(KSharedConfig::openConfig(), QStringLiteral("General")); const QString fontInfo = configGroup.readEntry(QStringLiteral("font"), QString()); if (!fontInfo.isEmpty()) { defaultFont.fromString(fontInfo); } settings.setValue(QStringLiteral("/qt/font"), defaultFont.toString()); /* export effects settings */ KConfigGroup kdeCfgGroup(kglobalcfg, "General"); bool effectsEnabled = kdeCfgGroup.readEntry("EffectsEnabled", false); bool fadeMenus = kdeCfgGroup.readEntry("EffectFadeMenu", false); bool fadeTooltips = kdeCfgGroup.readEntry("EffectFadeTooltip", false); bool animateCombobox = kdeCfgGroup.readEntry("EffectAnimateCombo", false); QStringList guieffects; if (effectsEnabled) { guieffects << QStringLiteral("general"); if (fadeMenus) guieffects << QStringLiteral("fademenu"); if (animateCombobox) guieffects << QStringLiteral("animatecombo"); if (fadeTooltips) guieffects << QStringLiteral("fadetooltip"); } else guieffects << QStringLiteral("none"); settings.setValue(QStringLiteral("/qt/GUIEffects"), guieffects); } // ----------------------------------------------------------------------------- static void addColorDef(QString& s, const char* n, const QColor& col) { QString tmp; tmp.sprintf("#define %s #%02x%02x%02x\n", n, col.red(), col.green(), col.blue()); s += tmp; } // ----------------------------------------------------------------------------- static void copyFile(QFile& tmp, QString const& filename, bool ) { QFile f( filename ); if ( f.open(QIODevice::ReadOnly) ) { QByteArray buf( 8192, ' ' ); while ( !f.atEnd() ) { int read = f.read( buf.data(), buf.size() ); if ( read > 0 ) tmp.write( buf.data(), read ); } } } // ----------------------------------------------------------------------------- static QString item( int i ) { return QString::number( i / 255.0, 'f', 3 ); } static QString color( const QColor& col ) { return QStringLiteral( "{ %1, %2, %3 }" ).arg( item( col.red() ) ).arg( item( col.green() ) ).arg( item( col.blue() ) ); } static void createGtkrc( bool exportColors, const QPalette& cg, bool exportGtkTheme, const QString& gtkTheme, int version ) { // lukas: why does it create in ~/.kde/share/config ??? // pfeiffer: so that we don't overwrite the user's gtkrc. // it is found via the GTK_RC_FILES environment variable. QSaveFile saveFile( writableGtkrc(version) ); if ( !saveFile.open(QIODevice::WriteOnly) ) return; QTextStream t ( &saveFile ); t.setCodec( QTextCodec::codecForLocale () ); t << i18n( "# created by KDE Plasma, %1\n" "#\n" "# If you do not want Plasma to override your GTK settings, select\n" "# Colors in the System Settings and disable the checkbox\n" "# \"Apply colors to non-Qt applications\"\n" "#\n" "#\n", QDateTime::currentDateTime().toString()); if ( 2==version ) { // we should maybe check for MacOS settings here t << endl; t << "gtk-alternative-button-order = 1" << endl; t << endl; } if (exportGtkTheme) { QString gtkStyle; if (gtkTheme.toLower() == QLatin1String("oxygen")) gtkStyle = QStringLiteral("oxygen-gtk"); else gtkStyle = gtkTheme; bool exist_gtkrc = false; QByteArray gtkrc = getenv(gtkEnvVar(version)); QStringList listGtkrc = QFile::decodeName(gtkrc).split(QLatin1Char(':')); if (listGtkrc.contains(saveFile.fileName())) listGtkrc.removeAll(saveFile.fileName()); listGtkrc.append(QDir::homePath() + userGtkrc(version)); listGtkrc.append(QDir::homePath() + "/.gtkrc-2.0-kde"); listGtkrc.append(QDir::homePath() + "/.gtkrc-2.0-kde4"); listGtkrc.removeAll(QLatin1String("")); listGtkrc.removeDuplicates(); for (int i = 0; i < listGtkrc.size(); ++i) { if ((exist_gtkrc = QFile::exists(listGtkrc.at(i)))) break; } if (!exist_gtkrc) { QString gtk2ThemeFilename; gtk2ThemeFilename = QStringLiteral("%1/.themes/%2/gtk-2.0/gtkrc").arg(QDir::homePath()).arg(gtkStyle); if (!QFile::exists(gtk2ThemeFilename)) { QStringList gtk2ThemePath; gtk2ThemeFilename.clear(); QByteArray xdgDataDirs = getenv("XDG_DATA_DIRS"); gtk2ThemePath.append(QDir::homePath() + "/.local"); gtk2ThemePath.append(QFile::decodeName(xdgDataDirs).split(QLatin1Char(':'))); gtk2ThemePath.removeDuplicates(); for (int i = 0; i < gtk2ThemePath.size(); ++i) { gtk2ThemeFilename = QStringLiteral("%1/themes/%2/gtk-2.0/gtkrc").arg(gtk2ThemePath.at(i)).arg(gtkStyle); if (QFile::exists(gtk2ThemeFilename)) break; else gtk2ThemeFilename.clear(); } } if (!gtk2ThemeFilename.isEmpty()) { t << "include \"" << gtk2ThemeFilename << "\"" << endl; t << endl; t << "gtk-theme-name=\"" << gtkStyle << "\"" << endl; t << endl; if (gtkStyle == QLatin1String("oxygen-gtk")) exportColors = false; } } } if (exportColors) { t << "style \"default\"" << endl; t << "{" << endl; t << " bg[NORMAL] = " << color( cg.color( QPalette::Active, QPalette::Window ) ) << endl; t << " bg[SELECTED] = " << color( cg.color(QPalette::Active, QPalette::Highlight) ) << endl; t << " bg[INSENSITIVE] = " << color( cg.color( QPalette::Active, QPalette::Window ) ) << endl; t << " bg[ACTIVE] = " << color( cg.color( QPalette::Active, QPalette::Mid ) ) << endl; t << " bg[PRELIGHT] = " << color( cg.color( QPalette::Active, QPalette::Window ) ) << endl; t << endl; t << " base[NORMAL] = " << color( cg.color( QPalette::Active, QPalette::Base ) ) << endl; t << " base[SELECTED] = " << color( cg.color(QPalette::Active, QPalette::Highlight) ) << endl; t << " base[INSENSITIVE] = " << color( cg.color( QPalette::Active, QPalette::Window ) ) << endl; t << " base[ACTIVE] = " << color( cg.color(QPalette::Active, QPalette::Highlight) ) << endl; t << " base[PRELIGHT] = " << color( cg.color(QPalette::Active, QPalette::Highlight) ) << endl; t << endl; t << " text[NORMAL] = " << color( cg.color(QPalette::Active, QPalette::Text) ) << endl; t << " text[SELECTED] = " << color( cg.color(QPalette::Active, QPalette::HighlightedText) ) << endl; t << " text[INSENSITIVE] = " << color( cg.color( QPalette::Active, QPalette::Mid ) ) << endl; t << " text[ACTIVE] = " << color( cg.color(QPalette::Active, QPalette::HighlightedText) ) << endl; t << " text[PRELIGHT] = " << color( cg.color(QPalette::Active, QPalette::HighlightedText) ) << endl; t << endl; t << " fg[NORMAL] = " << color ( cg.color( QPalette::Active, QPalette::WindowText ) ) << endl; t << " fg[SELECTED] = " << color( cg.color(QPalette::Active, QPalette::HighlightedText) ) << endl; t << " fg[INSENSITIVE] = " << color( cg.color( QPalette::Active, QPalette::Mid ) ) << endl; t << " fg[ACTIVE] = " << color( cg.color( QPalette::Active, QPalette::WindowText ) ) << endl; t << " fg[PRELIGHT] = " << color( cg.color( QPalette::Active, QPalette::WindowText ) ) << endl; t << "}" << endl; t << endl; t << "class \"*\" style \"default\"" << endl; t << endl; // tooltips don't have the standard background color t << "style \"ToolTip\"" << endl; t << "{" << endl; t << " bg[NORMAL] = " << color( cg.color( QPalette::ToolTipBase ) ) << endl; t << " base[NORMAL] = " << color( cg.color( QPalette::ToolTipBase ) ) << endl; t << " text[NORMAL] = " << color( cg.color( QPalette::ToolTipText ) ) << endl; t << " fg[NORMAL] = " << color( cg.color( QPalette::ToolTipText ) ) << endl; t << "}" << endl; t << endl; t << "widget \"gtk-tooltip\" style \"ToolTip\"" << endl; t << "widget \"gtk-tooltips\" style \"ToolTip\"" << endl; t << "widget \"gtk-tooltip*\" style \"ToolTip\"" << endl; t << endl; // highlight the current (mouse-hovered) menu-item // not every button, checkbox, etc. t << "style \"MenuItem\"" << endl; t << "{" << endl; t << " bg[PRELIGHT] = " << color( cg.color(QPalette::Highlight) ) << endl; t << " fg[PRELIGHT] = " << color( cg.color(QPalette::HighlightedText) ) << endl; t << "}" << endl; t << endl; t << "class \"*MenuItem\" style \"MenuItem\"" << endl; t << endl; } saveFile.commit(); } // --------------------------------------------------------------------- QString gtkColorsHelper(const QString &name, const QString &color) { return QStringLiteral("@define-color %1 %2;\n").arg(name, color); } void checkGtkCss() { QFile gtkCss(QStandardPaths::writableLocation(QStandardPaths::GenericConfigLocation) + "/gtk-3.0/gtk.css"); if (gtkCss.open(QIODevice::ReadWrite)) { QTextStream gtkStream(>kCss); bool hasImport = false; while (!gtkStream.atEnd()) { QString line = gtkStream.readLine(); if (line.contains("@import 'colors.css';")) { hasImport = true; } } if (!hasImport) { gtkStream << "@import 'colors.css';"; } } } void exportGtkColors(QList activeColorSchemes, QList inactiveColorSchemes, QList disabledColorSchemes, KConfigGroup groupWMTheme, QTextStream& colorsStream) { /* Normal (Non Backdrop, Non Insensitive) */ // General Colors colorsStream << gtkColorsHelper("theme_fg_color", activeColorSchemes[1].foreground(KColorScheme::NormalText).color().name()); colorsStream << gtkColorsHelper("theme_bg_color", activeColorSchemes[1].background(KColorScheme::NormalBackground).color().name()); colorsStream << gtkColorsHelper("theme_text_color", activeColorSchemes[0].foreground(KColorScheme::NormalText).color().name()); colorsStream << gtkColorsHelper("theme_base_color", activeColorSchemes[0].background(KColorScheme::NormalBackground).color().name()); colorsStream << gtkColorsHelper("theme_view_hover_decoration_color", activeColorSchemes[0].decoration(KColorScheme::HoverColor).color().name()); colorsStream << gtkColorsHelper("theme_hovering_selected_bg_color", activeColorSchemes[3].decoration(KColorScheme::HoverColor).color().name()); colorsStream << gtkColorsHelper("theme_selected_bg_color", activeColorSchemes[3].background(KColorScheme::NormalBackground).color().name()); colorsStream << gtkColorsHelper("theme_selected_fg_color", activeColorSchemes[3].foreground(KColorScheme::NormalText).color().name()); colorsStream << gtkColorsHelper("theme_view_active_decoration_color", activeColorSchemes[0].decoration(KColorScheme::HoverColor).color().name()); // Button Colors colorsStream << gtkColorsHelper("theme_button_background_normal", activeColorSchemes[2].background(KColorScheme::NormalBackground).color().name()); colorsStream << gtkColorsHelper("theme_button_decoration_hover", activeColorSchemes[2].decoration(KColorScheme::HoverColor).color().name()); colorsStream << gtkColorsHelper("theme_button_decoration_focus", activeColorSchemes[2].decoration(KColorScheme::FocusColor).color().name()); colorsStream << gtkColorsHelper("theme_button_foreground_normal", activeColorSchemes[2].foreground(KColorScheme::NormalText).color().name()); colorsStream << gtkColorsHelper("theme_button_foreground_active", activeColorSchemes[3].foreground(KColorScheme::NormalText).color().name()); // Misc Colors QColor windowForegroundColor = activeColorSchemes[1].foreground(KColorScheme::NormalText).color(); QColor windowBackgroundColor = activeColorSchemes[1].background(KColorScheme::NormalBackground).color(); QColor bordersColor = KColorUtils::mix(windowBackgroundColor,windowForegroundColor, 0.25); colorsStream << gtkColorsHelper("borders", bordersColor.name()); colorsStream << gtkColorsHelper("warning_color", activeColorSchemes[0].foreground(KColorScheme::NeutralText).color().name()); colorsStream << gtkColorsHelper("success_color", activeColorSchemes[0].foreground(KColorScheme::PositiveText).color().name()); colorsStream << gtkColorsHelper("error_color", activeColorSchemes[0].foreground(KColorScheme::NegativeText).color().name()); /* Backdrop (Inactive) */ // General colorsStream << gtkColorsHelper("theme_unfocused_fg_color",inactiveColorSchemes[1].foreground(KColorScheme::NormalText).color().name()); colorsStream << gtkColorsHelper("theme_unfocused_text_color", inactiveColorSchemes[0].foreground(KColorScheme::NormalText).color().name()); colorsStream << gtkColorsHelper("theme_unfocused_bg_color", inactiveColorSchemes[1].background(KColorScheme::NormalBackground).color().name()); colorsStream << gtkColorsHelper("theme_unfocused_base_color", inactiveColorSchemes[0].background(KColorScheme::NormalBackground).color().name()); colorsStream << gtkColorsHelper("theme_unfocused_selected_bg_color_alt", inactiveColorSchemes[3].background(KColorScheme::NormalBackground).color().name()); colorsStream << gtkColorsHelper("theme_unfocused_selected_bg_color", inactiveColorSchemes[3].background(KColorScheme::NormalBackground).color().name()); colorsStream << gtkColorsHelper("theme_unfocused_selected_fg_color", inactiveColorSchemes[3].foreground(KColorScheme::NormalText).color().name()); // Button colorsStream << gtkColorsHelper("theme_button_background_backdrop", inactiveColorSchemes[2].background(KColorScheme::NormalBackground).color().name()); colorsStream << gtkColorsHelper("theme_button_decoration_hover_backdrop", inactiveColorSchemes[2].decoration(KColorScheme::HoverColor).color().name()); colorsStream << gtkColorsHelper("theme_button_decoration_focus_backdrop", inactiveColorSchemes[2].decoration(KColorScheme::FocusColor).color().name()); colorsStream << gtkColorsHelper("theme_button_foreground_backdrop", inactiveColorSchemes[2].foreground(KColorScheme::NormalText).color().name()); colorsStream << gtkColorsHelper("theme_button_foreground_active_backdrop", inactiveColorSchemes[3].foreground(KColorScheme::NormalText).color().name()); // Misc Colors QColor inactiveWindowForegroundColor = inactiveColorSchemes[1].foreground(KColorScheme::NormalText).color(); QColor inactiveWindowBackgroundColor = inactiveColorSchemes[1].background(KColorScheme::NormalBackground).color(); QColor inactiveBordersColor = KColorUtils::mix(inactiveWindowBackgroundColor,inactiveWindowForegroundColor, 0.25); colorsStream << gtkColorsHelper("unfocused_borders", inactiveBordersColor.name()); colorsStream << gtkColorsHelper("warning_color_backdrop", inactiveColorSchemes[0].foreground(KColorScheme::NeutralText).color().name()); colorsStream << gtkColorsHelper("success_color_backdrop", inactiveColorSchemes[0].foreground(KColorScheme::PositiveText).color().name()); colorsStream << gtkColorsHelper("error_color_backdrop", inactiveColorSchemes[0].foreground(KColorScheme::NegativeText).color().name()); /* Insensitive (Disabled) */ // General colorsStream << gtkColorsHelper("insensitive_fg_color",disabledColorSchemes[1].foreground(KColorScheme::NormalText).color().name()); colorsStream << gtkColorsHelper("insensitive_base_fg_color", disabledColorSchemes[0].foreground(KColorScheme::NormalText).color().name()); colorsStream << gtkColorsHelper("insensitive_bg_color", disabledColorSchemes[1].background(KColorScheme::NormalBackground).color().name()); colorsStream << gtkColorsHelper("insensitive_base_color", disabledColorSchemes[0].background(KColorScheme::NormalBackground).color().name()); colorsStream << gtkColorsHelper("insensitive_selected_bg_color", disabledColorSchemes[3].background(KColorScheme::NormalBackground).color().name()); colorsStream << gtkColorsHelper("insensitive_selected_fg_color", disabledColorSchemes[3].foreground(KColorScheme::NormalText).color().name()); // Button colorsStream << gtkColorsHelper("theme_button_background_insensitive", disabledColorSchemes[2].background(KColorScheme::NormalBackground).color().name()); colorsStream << gtkColorsHelper("theme_button_decoration_hover_insensitive", disabledColorSchemes[2].decoration(KColorScheme::HoverColor).color().name()); colorsStream << gtkColorsHelper("theme_button_decoration_focus_insensitive", disabledColorSchemes[2].decoration(KColorScheme::FocusColor).color().name()); colorsStream << gtkColorsHelper("theme_button_foreground_insensitive", disabledColorSchemes[2].foreground(KColorScheme::NormalText).color().name()); colorsStream << gtkColorsHelper("theme_button_foreground_active_insensitive", disabledColorSchemes[3].foreground(KColorScheme::NormalText).color().name()); // Misc Colors QColor disabledWindowForegroundColor = disabledColorSchemes[1].foreground(KColorScheme::NormalText).color(); QColor disabledWindowBackgroundColor = disabledColorSchemes[1].background(KColorScheme::NormalBackground).color(); QColor disabledBordersColor = KColorUtils::mix(disabledWindowBackgroundColor,disabledWindowForegroundColor, 0.25); colorsStream << gtkColorsHelper("insensitive_borders", disabledBordersColor.name()); colorsStream << gtkColorsHelper("warning_color_insensitive", disabledColorSchemes[0].foreground(KColorScheme::NeutralText).color().name()); colorsStream << gtkColorsHelper("success_color_insensitive", disabledColorSchemes[0].foreground(KColorScheme::PositiveText).color().name()); colorsStream << gtkColorsHelper("error_color_insensitive", disabledColorSchemes[0].foreground(KColorScheme::NegativeText).color().name()); /* Insensitive Backdrop (Inactive Disabled) These pretty much have the same appearance as regular inactive colors, but they're seperate in case we decide to make them different in the future. */ // General colorsStream << gtkColorsHelper("insensitive_unfocused_fg_color",disabledColorSchemes[1].foreground(KColorScheme::NormalText).color().name()); colorsStream << gtkColorsHelper("theme_unfocused_view_text_color", disabledColorSchemes[0].foreground(KColorScheme::NormalText).color().name()); colorsStream << gtkColorsHelper("insensitive_unfocused_bg_color", disabledColorSchemes[1].background(KColorScheme::NormalBackground).color().name()); colorsStream << gtkColorsHelper("theme_unfocused_view_bg_color", disabledColorSchemes[0].background(KColorScheme::NormalBackground).color().name()); colorsStream << gtkColorsHelper("insensitive_unfocused_selected_bg_color", disabledColorSchemes[3].background(KColorScheme::NormalBackground).color().name()); colorsStream << gtkColorsHelper("insensitive_unfocused_selected_fg_color", disabledColorSchemes[3].foreground(KColorScheme::NormalText).color().name()); // Button colorsStream << gtkColorsHelper("theme_button_background_backdrop_insensitive", disabledColorSchemes[2].background(KColorScheme::NormalBackground).color().name()); colorsStream << gtkColorsHelper("theme_button_decoration_hover_backdrop_insensitive", disabledColorSchemes[2].decoration(KColorScheme::HoverColor).color().name()); colorsStream << gtkColorsHelper("theme_button_decoration_focus_backdrop_insensitive", disabledColorSchemes[2].decoration(KColorScheme::FocusColor).color().name()); colorsStream << gtkColorsHelper("theme_button_foreground_backdrop_insensitive", disabledColorSchemes[2].foreground(KColorScheme::NormalText).color().name()); colorsStream << gtkColorsHelper("theme_button_foreground_active_backdrop_insensitive", disabledColorSchemes[3].foreground(KColorScheme::NormalText).color().name()); // Misc Colors QColor unfocusedDisabledWindowForegroundColor = disabledColorSchemes[1].foreground(KColorScheme::NormalText).color(); QColor unfocusedDisabledWindowBackgroundColor = disabledColorSchemes[1].background(KColorScheme::NormalBackground).color(); QColor unfocusedDisabledBordersColor = KColorUtils::mix(unfocusedDisabledWindowBackgroundColor,unfocusedDisabledWindowForegroundColor, 0.25); colorsStream << gtkColorsHelper("unfocused_insensitive_borders", unfocusedDisabledBordersColor.name()); colorsStream << gtkColorsHelper("warning_color_insensitive_backdrop", disabledColorSchemes[0].foreground(KColorScheme::NeutralText).color().name()); colorsStream << gtkColorsHelper("success_color_insensitive_backdrop", disabledColorSchemes[0].foreground(KColorScheme::PositiveText).color().name()); colorsStream << gtkColorsHelper("error_color_insensitive_backdrop", disabledColorSchemes[0].foreground(KColorScheme::NegativeText).color().name()); /* Ignorant Colors (These colors do not care about backdrop or insensitive states) */ colorsStream << gtkColorsHelper("link_color", activeColorSchemes[0].foreground(KColorScheme::LinkText).color().name()); colorsStream << gtkColorsHelper("link_visited_color", activeColorSchemes[0].foreground(KColorScheme::VisitedText).color().name()); QColor tooltipForegroundColor = activeColorSchemes[4].foreground(KColorScheme::NormalText).color(); QColor tooltipBackgroundColor = activeColorSchemes[4].background(KColorScheme::NormalBackground).color(); QColor tooltipBorderColor = KColorUtils::mix(tooltipBackgroundColor, tooltipForegroundColor, 0.25); colorsStream << gtkColorsHelper("tooltip_text", tooltipForegroundColor.name()); colorsStream << gtkColorsHelper("tooltip_background", tooltipBackgroundColor.name()); colorsStream << gtkColorsHelper("tooltip_border", tooltipBorderColor.name()); colorsStream << gtkColorsHelper("content_view_bg", activeColorSchemes[0].background(KColorScheme::NormalBackground).color().name()); // Titlebar colors - colorsStream << gtkColorsHelper("theme_titlebar_background", "rgb(" + groupWMTheme.readEntry("activeBackground", "") + ")"); - colorsStream << gtkColorsHelper("theme_titlebar_foreground", "rgb(" + groupWMTheme.readEntry("activeForeground", "") + ")"); + colorsStream << gtkColorsHelper("theme_titlebar_background", "rgb(" + groupWMTheme.readEntry("activeBackground", "") + QLatin1Char(')')); + colorsStream << gtkColorsHelper("theme_titlebar_foreground", "rgb(" + groupWMTheme.readEntry("activeForeground", "") + QLatin1Char(')')); colorsStream << gtkColorsHelper("theme_titlebar_background_light", activeColorSchemes[1].background(KColorScheme::NormalBackground).color().name()); - colorsStream << gtkColorsHelper("theme_titlebar_foreground_backdrop", "rgb(" + groupWMTheme.readEntry("inactiveForeground", "") + ")"); - colorsStream << gtkColorsHelper("theme_titlebar_background_backdrop", "rgb(" + groupWMTheme.readEntry("inactiveBackground", "") + ")"); - colorsStream << gtkColorsHelper("theme_titlebar_foreground_insensitive", "rgb(" + groupWMTheme.readEntry("inactiveForeground", "") + ")"); - colorsStream << gtkColorsHelper("theme_titlebar_foreground_insensitive_backdrop", "rgb(" + groupWMTheme.readEntry("inactiveForeground", "") + ")"); + colorsStream << gtkColorsHelper("theme_titlebar_foreground_backdrop", "rgb(" + groupWMTheme.readEntry("inactiveForeground", "") + QLatin1Char(')')); + colorsStream << gtkColorsHelper("theme_titlebar_background_backdrop", "rgb(" + groupWMTheme.readEntry("inactiveBackground", "") + QLatin1Char(')')); + colorsStream << gtkColorsHelper("theme_titlebar_foreground_insensitive", "rgb(" + groupWMTheme.readEntry("inactiveForeground", "") + QLatin1Char(')')); + colorsStream << gtkColorsHelper("theme_titlebar_foreground_insensitive_backdrop", "rgb(" + groupWMTheme.readEntry("inactiveForeground", "") + QLatin1Char(')')); } void saveGtkColors(KSharedConfigPtr& config) { checkGtkCss(); QFile colorsCss(QStandardPaths::writableLocation(QStandardPaths::GenericConfigLocation) + "/gtk-3.0/colors.css"); if (colorsCss.open(QIODevice::WriteOnly | QIODevice::Truncate)) { QTextStream colorsStream(&colorsCss); /* 0 Active View 1 Active Window 2 Active Button 3 Active Selection 4 Active Tooltip 5 Active Complimentary */ QList activeColorSchemes{ KColorScheme(QPalette::Active, KColorScheme::View, config), KColorScheme(QPalette::Active, KColorScheme::Window, config), KColorScheme(QPalette::Active, KColorScheme::Button, config), KColorScheme(QPalette::Active, KColorScheme::Selection, config), KColorScheme(QPalette::Active, KColorScheme::Tooltip, config), KColorScheme(QPalette::Active, KColorScheme::Complementary, config) }; QList inactiveColorSchemes{ KColorScheme(QPalette::Inactive, KColorScheme::View, config), KColorScheme(QPalette::Inactive, KColorScheme::Window, config), KColorScheme(QPalette::Inactive, KColorScheme::Button, config), KColorScheme(QPalette::Inactive, KColorScheme::Selection, config), KColorScheme(QPalette::Inactive, KColorScheme::Tooltip, config), KColorScheme(QPalette::Inactive, KColorScheme::Complementary, config) }; QList disabledColorSchemes{ KColorScheme(QPalette::Disabled, KColorScheme::View, config), KColorScheme(QPalette::Disabled, KColorScheme::Window, config), KColorScheme(QPalette::Disabled, KColorScheme::Button, config), KColorScheme(QPalette::Disabled, KColorScheme::Selection, config), KColorScheme(QPalette::Disabled, KColorScheme::Tooltip, config), KColorScheme(QPalette::Disabled, KColorScheme::Complementary, config) }; KConfigGroup groupWMTheme(config, "WM"); exportGtkColors(activeColorSchemes, inactiveColorSchemes, disabledColorSchemes, groupWMTheme, colorsStream); } } void saveGtkColors() { checkGtkCss(); QFile colorsCss(QStandardPaths::writableLocation(QStandardPaths::GenericConfigLocation) + "/gtk-3.0/colors.css"); KConfigGroup groupWMTheme(KSharedConfig::openConfig(), "WM"); if (colorsCss.open(QIODevice::WriteOnly | QIODevice::Truncate)) { QTextStream colorsStream(&colorsCss); /* 0 Active View 1 Active Window 2 Active Button 3 Active Selection 4 Active Tooltip 5 Active Complimentary */ QList activeColorSchemes{ KColorScheme(QPalette::Active, KColorScheme::View), KColorScheme(QPalette::Active, KColorScheme::Window), KColorScheme(QPalette::Active, KColorScheme::Button), KColorScheme(QPalette::Active, KColorScheme::Selection), KColorScheme(QPalette::Active, KColorScheme::Tooltip), KColorScheme(QPalette::Active, KColorScheme::Complementary) }; QList inactiveColorSchemes{ KColorScheme(QPalette::Inactive, KColorScheme::View), KColorScheme(QPalette::Inactive, KColorScheme::Window), KColorScheme(QPalette::Inactive, KColorScheme::Button), KColorScheme(QPalette::Inactive, KColorScheme::Selection), KColorScheme(QPalette::Inactive, KColorScheme::Tooltip), KColorScheme(QPalette::Inactive, KColorScheme::Complementary) }; QList disabledColorSchemes{ KColorScheme(QPalette::Disabled, KColorScheme::View), KColorScheme(QPalette::Disabled, KColorScheme::Window), KColorScheme(QPalette::Disabled, KColorScheme::Button), KColorScheme(QPalette::Disabled, KColorScheme::Selection), KColorScheme(QPalette::Disabled, KColorScheme::Tooltip), KColorScheme(QPalette::Disabled, KColorScheme::Complementary) }; exportGtkColors(activeColorSchemes, inactiveColorSchemes, disabledColorSchemes, groupWMTheme, colorsStream); } } // ----------------------------------------------------------------------------- void runRdb( uint flags ) { // Obtain the application palette that is about to be set. bool exportColors = flags & KRdbExportColors; bool exportQtColors = flags & KRdbExportQtColors; bool exportQtSettings = flags & KRdbExportQtSettings; bool exportXftSettings = flags & KRdbExportXftSettings; bool exportGtkTheme = flags & KRdbExportGtkTheme; bool exportGtkColors = flags & KRdbExportGtkColors; KSharedConfigPtr kglobalcfg = KSharedConfig::openConfig( QStringLiteral("kdeglobals") ); KConfigGroup kglobals(kglobalcfg, "KDE"); QPalette newPal = KColorScheme::createApplicationPalette(kglobalcfg); QTemporaryFile tmpFile; if (!tmpFile.open()) { qDebug() << "Couldn't open temp file"; exit(0); } KConfigGroup generalCfgGroup(kglobalcfg, "General"); QString gtkTheme; if (kglobals.hasKey("widgetStyle")) gtkTheme = kglobals.readEntry("widgetStyle"); else gtkTheme = QStringLiteral("oxygen"); createGtkrc( exportColors, newPal, exportGtkTheme, gtkTheme, 1 ); createGtkrc( exportColors, newPal, exportGtkTheme, gtkTheme, 2 ); // Export colors to non-(KDE/Qt) apps (e.g. Motif, GTK+ apps) if (exportColors) { KConfigGroup g(KSharedConfig::openConfig(), "WM"); QString preproc; QColor backCol = newPal.color( QPalette::Active, QPalette::Window ); addColorDef(preproc, "FOREGROUND" , newPal.color( QPalette::Active, QPalette::WindowText ) ); addColorDef(preproc, "BACKGROUND" , backCol); addColorDef(preproc, "HIGHLIGHT" , backCol.lighter(100+(2*KColorScheme::contrast()+4)*16/1)); addColorDef(preproc, "LOWLIGHT" , backCol.darker(100+(2*KColorScheme::contrast()+4)*10)); addColorDef(preproc, "SELECT_BACKGROUND" , newPal.color( QPalette::Active, QPalette::Highlight)); addColorDef(preproc, "SELECT_FOREGROUND" , newPal.color( QPalette::Active, QPalette::HighlightedText)); addColorDef(preproc, "WINDOW_BACKGROUND" , newPal.color( QPalette::Active, QPalette::Base ) ); addColorDef(preproc, "WINDOW_FOREGROUND" , newPal.color( QPalette::Active, QPalette::Text ) ); addColorDef(preproc, "INACTIVE_BACKGROUND", g.readEntry("inactiveBackground", QColor(224, 223, 222))); addColorDef(preproc, "INACTIVE_FOREGROUND", g.readEntry("inactiveBackground", QColor(224, 223, 222))); addColorDef(preproc, "ACTIVE_BACKGROUND" , g.readEntry("activeBackground", QColor(48, 174, 232))); addColorDef(preproc, "ACTIVE_FOREGROUND" , g.readEntry("activeBackground", QColor(48, 174, 232))); //--------------------------------------------------------------- tmpFile.write( preproc.toLatin1(), preproc.length() ); QStringList list; const QStringList adPaths = QStandardPaths::locateAll(QStandardPaths::GenericDataLocation, QStringLiteral("kdisplay/app-defaults/"), QStandardPaths::LocateDirectory); for (QStringList::ConstIterator it = adPaths.constBegin(); it != adPaths.constEnd(); ++it) { QDir dSys( *it ); if ( dSys.exists() ) { dSys.setFilter( QDir::Files ); dSys.setSorting( QDir::Name ); dSys.setNameFilters(QStringList(QStringLiteral("*.ad"))); list += dSys.entryList(); } } for (QStringList::ConstIterator it = list.constBegin(); it != list.constEnd(); ++it) copyFile(tmpFile, QStandardPaths::locate(QStandardPaths::GenericDataLocation, "kdisplay/app-defaults/"+(*it)), true); } // Merge ~/.Xresources or fallback to ~/.Xdefaults QString homeDir = QDir::homePath(); QString xResources = homeDir + "/.Xresources"; // very primitive support for ~/.Xresources by appending it if ( QFile::exists( xResources ) ) copyFile(tmpFile, xResources, true); else copyFile(tmpFile, homeDir + "/.Xdefaults", true); // Export the Xcursor theme & size settings KConfigGroup mousecfg(KSharedConfig::openConfig( QStringLiteral("kcminputrc") ), "Mouse" ); QString theme = mousecfg.readEntry("cursorTheme", QString()); QString size = mousecfg.readEntry("cursorSize", QString()); QString contents; if (!theme.isNull()) contents = "Xcursor.theme: " + theme + '\n'; if (!size.isNull()) contents += "Xcursor.size: " + size + '\n'; if (exportXftSettings) { contents += QLatin1String("Xft.antialias: "); if(generalCfgGroup.readEntry("XftAntialias", true)) contents += QLatin1String("1\n"); else contents += QLatin1String("0\n"); QString hintStyle = generalCfgGroup.readEntry("XftHintStyle", "hintslight"); contents += QLatin1String("Xft.hinting: "); if(hintStyle.isEmpty()) contents += QLatin1String("-1\n"); else { if(hintStyle!=QLatin1String("hintnone")) contents += QLatin1String("1\n"); else contents += QLatin1String("0\n"); contents += "Xft.hintstyle: " + hintStyle + '\n'; } QString subPixel = generalCfgGroup.readEntry("XftSubPixel", "rgb"); if(!subPixel.isEmpty()) contents += "Xft.rgba: " + subPixel + '\n'; KConfig _cfgfonts( QStringLiteral("kcmfonts") ); KConfigGroup cfgfonts(&_cfgfonts, "General"); int dpi; //even though this sets up the X rdb, we want to use the value the //user has set to use when under wayland - as X apps will be scaled by the compositor if (KWindowSystem::isPlatformWayland()) { dpi = cfgfonts.readEntry( "forceFontDPIWayland", 0); if (dpi == 0) { //with wayland we want xwayland to run at 96 dpi (unless set otherwise) as we have wayland scaling on top dpi = 96; } } else { dpi = cfgfonts.readEntry( "forceFontDPI", 0); } if( dpi != 0 ) contents += "Xft.dpi: " + QString::number(dpi) + '\n'; else { KProcess proc; proc << QStringLiteral("xrdb") << QStringLiteral("-quiet") << QStringLiteral("-remove") << QStringLiteral("-nocpp"); proc.start(); if (proc.waitForStarted()) { proc.write( QByteArray( "Xft.dpi\n" ) ); proc.closeWriteChannel(); proc.waitForFinished(); } } } if (contents.length() > 0) tmpFile.write( contents.toLatin1(), contents.length() ); tmpFile.flush(); KProcess proc; #ifndef NDEBUG proc << QStringLiteral("xrdb") << QStringLiteral("-merge") << tmpFile.fileName(); #else proc << "xrdb" << "-quiet" << "-merge" << tmpFile.fileName(); #endif proc.execute(); applyGtkStyles(exportColors, 1); applyGtkStyles(exportColors, 2); /* Qt exports */ if ( exportQtColors || exportQtSettings ) { QSettings* settings = new QSettings(QStringLiteral("Trolltech")); if ( exportQtColors ) applyQtColors( kglobalcfg, *settings, newPal ); // For kcmcolors if ( exportQtSettings ) applyQtSettings( kglobalcfg, *settings ); // For kcmstyle delete settings; QApplication::flush(); #if HAVE_X11 if (qApp->platformName() == QLatin1String("xcb")) { // We let KIPC take care of ourselves, as we are in a KDE app with // QApp::setDesktopSettingsAware(false); // Instead of calling QApp::x11_apply_settings() directly, we instead // modify the timestamp which propagates the settings changes onto // Qt-only apps without adversely affecting ourselves. // Cheat and use the current timestamp, since we just saved to qtrc. QDateTime settingsstamp = QDateTime::currentDateTime(); static Atom qt_settings_timestamp = 0; if (!qt_settings_timestamp) { QString atomname(QStringLiteral("_QT_SETTINGS_TIMESTAMP_")); atomname += XDisplayName( nullptr ); // Use the $DISPLAY envvar. qt_settings_timestamp = XInternAtom( QX11Info::display(), atomname.toLatin1(), False); } QBuffer stamp; QDataStream s(&stamp.buffer(), QIODevice::WriteOnly); s << settingsstamp; XChangeProperty( QX11Info::display(), QX11Info::appRootWindow(), qt_settings_timestamp, qt_settings_timestamp, 8, PropModeReplace, (unsigned char*) stamp.buffer().data(), stamp.buffer().size() ); QApplication::flush(); } #endif } //Legacy support: //Try to sync kde4 settings with ours Kdelibs4Migration migration; //kf5 congig groups for general and icons KConfigGroup generalGroup(kglobalcfg, "General"); KConfigGroup iconsGroup(kglobalcfg, "Icons"); const QString colorSchemeName = generalGroup.readEntry("ColorScheme", QString()); //if no valid color scheme saved, something weird is going on, abort if (colorSchemeName.isEmpty()) { return; } QString colorSchemeSrcFile; if (colorSchemeName != QLatin1String("Default")) { //fix filename, copied from ColorsCM::saveScheme() QString colorSchemeFilename = colorSchemeName; colorSchemeFilename.remove('\''); // So Foo's does not become FooS QRegExp fixer(QStringLiteral("[\\W,.-]+(.?)")); int offset; while ((offset = fixer.indexIn(colorSchemeFilename)) >= 0) colorSchemeFilename.replace(offset, fixer.matchedLength(), fixer.cap(1).toUpper()); colorSchemeFilename.replace(0, 1, colorSchemeFilename.at(0).toUpper()); //clone the color scheme colorSchemeSrcFile = QStandardPaths::locate(QStandardPaths::GenericDataLocation, "color-schemes/" + colorSchemeFilename + ".colors"); const QString dest = migration.saveLocation("data", QStringLiteral("color-schemes")) + colorSchemeName + ".colors"; QFile::remove(dest); QFile::copy(colorSchemeSrcFile, dest); } //Apply the color scheme QString configFilePath = migration.saveLocation("config") + "kdeglobals"; if (configFilePath.isEmpty()) { return; } KConfig kde4config(configFilePath, KConfig::SimpleConfig); KConfigGroup kde4generalGroup(&kde4config, "General"); kde4generalGroup.writeEntry("ColorScheme", colorSchemeName); //fonts QString font = generalGroup.readEntry("font", QString()); if (!font.isEmpty()) { kde4generalGroup.writeEntry("font", font); } font = generalGroup.readEntry("desktopFont", QString()); if (!font.isEmpty()) { kde4generalGroup.writeEntry("desktopFont", font); } font = generalGroup.readEntry("menuFont", QString()); if (!font.isEmpty()) { kde4generalGroup.writeEntry("menuFont", font); } font = generalGroup.readEntry("smallestReadableFont", QString()); if (!font.isEmpty()) { kde4generalGroup.writeEntry("smallestReadableFont", font); } font = generalGroup.readEntry("taskbarFont", QString()); if (!font.isEmpty()) { kde4generalGroup.writeEntry("taskbarFont", font); } font = generalGroup.readEntry("toolBarFont", QString()); if (!font.isEmpty()) { kde4generalGroup.writeEntry("toolBarFont", font); } //TODO: does exist any way to check if a qt4 widget style is present from a qt5 app? //kde4generalGroup.writeEntry("widgetStyle", "qtcurve"); kde4generalGroup.sync(); KConfigGroup kde4IconGroup(&kde4config, "Icons"); QString iconTheme = iconsGroup.readEntry("Theme", QString()); if (!iconTheme.isEmpty()) { kde4IconGroup.writeEntry("Theme", iconTheme); } kde4IconGroup.sync(); if (!colorSchemeSrcFile.isEmpty()) { //copy all the groups in the color scheme in kdeglobals KSharedConfigPtr kde4ColorConfig = KSharedConfig::openConfig(colorSchemeSrcFile, KConfig::SimpleConfig); foreach (const QString &grp, kde4ColorConfig->groupList()) { KConfigGroup cg(kde4ColorConfig, grp); KConfigGroup cg2(&kde4config, grp); cg.copyTo(&cg2); } } //widgets settings KConfigGroup kglobals4(&kde4config, "KDE"); kglobals4.writeEntry("ShowIconsInMenuItems", kglobals.readEntry("ShowIconsInMenuItems", true)); kglobals4.writeEntry("ShowIconsOnPushButtons", kglobals.readEntry("ShowIconsOnPushButtons", true)); kglobals4.writeEntry("contrast", kglobals.readEntry("contrast", 4)); //FIXME: this should somehow check if the kde4 version of the style is installed kde4generalGroup.writeEntry("widgetStyle", kglobals.readEntry("widgetStyle", "breeze")); //toolbar style KConfigGroup toolbars4(&kde4config, "Toolbar style"); KConfigGroup toolbars5(kglobalcfg, "Toolbar style"); toolbars4.writeEntry("ToolButtonStyle", toolbars5.readEntry("ToolButtonStyle", "TextBesideIcon")); toolbars4.writeEntry("ToolButtonStyleOtherToolbars", toolbars5.readEntry("ToolButtonStyleOtherToolbars", "TextBesideIcon")); if (exportGtkColors) { saveGtkColors(); } } diff --git a/kcms/krdb/krdb_clearlibrarypath.cpp b/kcms/krdb/krdb_clearlibrarypath.cpp index 1a6fdb3eb..a42d8c5f5 100644 --- a/kcms/krdb/krdb_clearlibrarypath.cpp +++ b/kcms/krdb/krdb_clearlibrarypath.cpp @@ -1,75 +1,75 @@ /* This file is part of the KDE project Copyright (C) 2008 Matthias Kretz Copyright (C) 2011 David Faure This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation; either version 2.1 of the License, or (at your option) version 3, or any later version accepted by the membership of KDE e.V. (or its successor approved by the membership of KDE e.V.), Trolltech ASA (or its successors, if any) and the KDE Free Qt Foundation, which shall act as a proxy defined in Section 6 of version 3 of the license. 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 Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this library. If not, see . */ #include #include #include #include #include #include #include int main(int argc, char **argv) { QCoreApplication app(argc, argv); QSettings settings(QStringLiteral("Trolltech")); QString qversion = qVersion(); if (qversion.count('.') > 1) { qversion.truncate(qversion.lastIndexOf('.')); } if (qversion.contains('-')) { qversion.truncate(qversion.lastIndexOf('-')); } const QString &libPathKey = QStringLiteral("/qt/%1/libraryPath").arg(qversion); QStringList kdeAdded; KComponentData kcd("krdb libraryPath fix"); const QStringList &plugins = KGlobal::dirs()->resourceDirs("qtplugins"); foreach (const QString &_path, plugins) { QString path = QDir(_path).canonicalPath(); if (path.isEmpty() || kdeAdded.contains(path)) { continue; } kdeAdded.prepend(path); if (path.contains(QLatin1String("/lib64/"))) { path.replace(QLatin1String("/lib64/"), QLatin1String("/lib/")); if (!kdeAdded.contains(path)) { kdeAdded.prepend(path); } } } // Don't use toStringList! That's a different storage format QStringList libraryPath = settings.value(libPathKey, QString()) .toString().split(QLatin1Char(':'), QString::SkipEmptyParts); // Remove all KDE paths, not needed anymore, done by $QT_PLUGIN_PATH foreach (const QString &path, const_cast(kdeAdded)) { libraryPath.removeAll(path); } settings.remove(QStringLiteral("/qt/KDE/kdeAddedLibraryPaths")); - settings.setValue(libPathKey, libraryPath.join(QLatin1String(":"))); + settings.setValue(libPathKey, libraryPath.join(QLatin1Char(':'))); return 0; } diff --git a/kcms/solid_actions/DesktopFileGenerator.cpp b/kcms/solid_actions/DesktopFileGenerator.cpp index 75979f700..7e3cb8b19 100644 --- a/kcms/solid_actions/DesktopFileGenerator.cpp +++ b/kcms/solid_actions/DesktopFileGenerator.cpp @@ -1,81 +1,81 @@ /*************************************************************************** * Copyright (C) 2009 by Ben Cooksley * * * * 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. * * * * This program 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 General Public License for more details. * * * * You should have received a copy of the GNU General Public License * * along with this program; 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 "SolidActionData.h" int main( int argc, char *argv[] ) { QCoreApplication application(argc, argv); KLocalizedString::setApplicationDomain("kcm_solid_actions"); // About data KAboutData aboutData(QStringLiteral("solid-action-desktop-gen"), i18n("Solid Action Desktop File Generator"), QStringLiteral("0.4"), i18n("Tool to automatically generate Desktop Files from Solid DeviceInterface classes for translation"), KAboutLicense::GPL, i18n("(c) 2009, Ben Cooksley")); aboutData.addAuthor(i18n("Ben Cooksley"), i18n("Maintainer"), QStringLiteral("ben@eclipse.endoftheinternet.org")); KAboutData::setApplicationData(aboutData); QCommandLineParser parser; aboutData.setupCommandLine(&parser); parser.process(application); aboutData.processCommandLine(&parser); SolidActionData * availActions = SolidActionData::instance(); foreach( Solid::DeviceInterface::Type internalType, availActions->interfaceTypeList() ) { const QString typeName = Solid::DeviceInterface::typeToString( internalType ); KDesktopFile typeFile(QStandardPaths::GenericDataLocation, "solid/devices/solid-device-" + typeName + ".desktop" ); KConfigGroup tConfig = typeFile.desktopGroup(); tConfig.writeEntry( "Name", "Solid Device" ); tConfig.writeEntry( "X-KDE-ServiceTypes", "SolidDevice" ); tConfig.writeEntry( "Type", "Service" ); if( !tConfig.hasKey("X-KDE-Solid-Actions-Type") ) { tConfig.writeEntry( "X-KDE-Solid-Actions-Type", typeName ); } const QStringList typeValues = availActions->propertyInternalList( internalType ); - const QString actionText = typeValues.join(QLatin1String(";")).append(";"); + const QString actionText = typeValues.join(QLatin1Char(';')).append(";"); tConfig.writeEntry( "Actions", actionText ); qWarning() << "Desktop file created: " + typeFile.fileName(); foreach( const QString &tValue, typeValues ) { KConfigGroup vConfig = typeFile.actionGroup( tValue ); if( !vConfig.hasKey("Name") ) { vConfig.writeEntry( "Name", availActions->propertyName( internalType, tValue ) ); } vConfig.sync(); } tConfig.sync(); typeFile.sync(); } qWarning() << "Generation now completed"; return 0; }