diff --git a/sddmauthhelper.cpp b/sddmauthhelper.cpp index 3a18ae2..576cb30 100644 --- a/sddmauthhelper.cpp +++ b/sddmauthhelper.cpp @@ -1,237 +1,237 @@ /* Copyright 2013 by Reza Fatahilah Shah Copyright 2011, 2012 David Edmundson 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, see . */ #include "sddmauthhelper.h" +#include +#include #include #include -#include -#include -#include -#include #include +#include +#include -#include -#include -#include #include #include #include +#include +#include +#include static QSharedPointer openConfig(const QString &filePath) { QFile file(filePath); if(!file.exists()) { // If we are creating the config file, ensure it is world-readable: if // we don't do that, KConfig will create a file which is only readable // by root file.open(QIODevice::WriteOnly); file.close(); file.setPermissions(QFile::ReadOwner | QFile::WriteOwner | QFile::ReadGroup | QFile::ReadOther); } return QSharedPointer(new KConfig(file.fileName(), KConfig::SimpleConfig)); } ActionReply SddmAuthHelper::save(const QVariantMap &args) { ActionReply reply = ActionReply::HelperErrorReply(); QSharedPointer sddmConfig = openConfig(args[QStringLiteral("kde_settings.conf")].toString()); QSharedPointer sddmOldConfig = openConfig(args[QStringLiteral("sddm.conf")].toString()); QSharedPointer themeConfig; QString themeConfigFile = args[QStringLiteral("theme.conf.user")].toString(); if (!themeConfigFile.isEmpty()) { themeConfig = openConfig(themeConfigFile); } QMap::const_iterator iterator; for (iterator = args.constBegin() ; iterator != args.constEnd() ; ++iterator) { if (iterator.key() == QLatin1String("kde_settings.conf") || iterator.key() == QLatin1String("theme.conf.user")) continue; QStringList configFields = iterator.key().split(QLatin1Char('/')); if (configFields.size() != 3) { continue; } QSharedPointer config; QString fileName = configFields[0]; QString groupName = configFields[1]; QString keyName = configFields[2]; // if there is an identical keyName in "sddm.conf" we want to delete it so SDDM doesn't read from the old file // hierarchically SDDM prefers "etc/sddm.conf" to "/etc/sddm.conf.d/some_file.conf" if (fileName == QLatin1String("kde_settings.conf")) { sddmConfig->group(groupName).writeEntry(keyName, iterator.value()); sddmOldConfig->group(groupName).deleteEntry(keyName); } else if (fileName == QLatin1String("theme.conf.user") && !themeConfig.isNull()) { QFileInfo themeConfigFileInfo(themeConfigFile); QDir configRootDirectory = themeConfigFileInfo.absoluteDir(); if (keyName == QLatin1String("background")) { QFileInfo newBackgroundFileInfo(iterator.value().toString()); QString previousBackground = themeConfig->group(groupName).readEntry(keyName); bool backgroundChanged = newBackgroundFileInfo.fileName() != previousBackground; if (backgroundChanged) { if (!previousBackground.isEmpty()) { QString previousBackgroundPath = configRootDirectory.filePath(previousBackground); if (QFile::remove(previousBackgroundPath)) { qDebug() << "Removed previous background " << previousBackgroundPath; } } if (newBackgroundFileInfo.exists()) { QString newBackgroundPath = configRootDirectory.filePath(newBackgroundFileInfo.fileName()); qDebug() << "Copying background from " << newBackgroundFileInfo.absoluteFilePath() << " to " << newBackgroundPath; if (QFile::copy(newBackgroundFileInfo.absoluteFilePath(), newBackgroundPath)) { QFile::setPermissions(newBackgroundPath, QFile::ReadOwner | QFile::WriteOwner | QFile::ReadGroup | QFile::ReadOther); themeConfig->group(groupName).writeEntry(keyName, newBackgroundFileInfo.fileName()); } } else { themeConfig->group(groupName).deleteEntry(keyName); } } } else { themeConfig->group(groupName).writeEntry(keyName, iterator.value()); } } } sddmOldConfig->sync(); sddmConfig->sync(); if (!themeConfig.isNull()) themeConfig->sync(); return ActionReply::SuccessReply(); } ActionReply SddmAuthHelper::installtheme(const QVariantMap &args) { const QString filePath = args[QStringLiteral("filePath")].toString(); if (filePath.isEmpty()) { return ActionReply::HelperErrorReply(); } const QString themesBaseDir = QStandardPaths::locate(QStandardPaths::GenericDataLocation, QStringLiteral("sddm/themes"), QStandardPaths::LocateDirectory); QDir dir(themesBaseDir); if (!dir.exists()) { return ActionReply::HelperErrorReply(); } qDebug() << "Installing " << filePath << " into " << themesBaseDir; if (!QFile::exists(filePath)) { return ActionReply::HelperErrorReply(); } QMimeDatabase db; QMimeType mimeType = db.mimeTypeForFile(filePath); qWarning() << "Postinstallation: uncompress the file"; QScopedPointer archive; //there must be a better way to do this? If not, make a static bool KZip::supportsMimeType(const QMimeType &type); ? //or even a factory class in KArchive if (mimeType.inherits(QStringLiteral("application/zip"))) { archive.reset(new KZip(filePath)); } else if (mimeType.inherits(QStringLiteral("application/tar")) || mimeType.inherits(QStringLiteral("application/x-gzip")) || mimeType.inherits(QStringLiteral("application/x-bzip")) || mimeType.inherits(QStringLiteral("application/x-lzma")) || mimeType.inherits(QStringLiteral("application/x-xz")) || mimeType.inherits(QStringLiteral("application/x-bzip-compressed-tar")) || mimeType.inherits(QStringLiteral("application/x-compressed-tar"))) { archive.reset(new KTar(filePath)); } else { auto e = ActionReply::HelperErrorReply(); e.setErrorDescription(i18n("Invalid theme package")); return e; } if (!archive->open(QIODevice::ReadOnly)) { auto e = ActionReply::HelperErrorReply(); e.setErrorDescription(i18n("Could not open file")); return e; } auto directory = archive->directory(); QStringList installedPaths; //some basic validation //the top level should only have folders, and those folders should contain a valid metadata.desktop file //if we get anything else, abort everything before copying for(const QString &name: directory->entries()) { auto entry = directory->entry(name); if (!entry->isDirectory()) { auto e = ActionReply::HelperErrorReply(); e.setErrorDescription(i18n("Invalid theme package")); return e; } auto subDirectory = static_cast(entry); auto metadataFile = subDirectory->file(QStringLiteral("metadata.desktop")); if(!metadataFile || !metadataFile->data().contains("[SddmGreeterTheme]")) { auto e = ActionReply::HelperErrorReply(); e.setErrorDescription(i18n("Invalid theme package")); return e; } installedPaths.append(themesBaseDir + QLatin1Char('/') + name); } if (!directory->copyTo(themesBaseDir)) { auto e = ActionReply::HelperErrorReply(); e.setErrorDescription(i18n("Could not decompress archive")); return e; } auto rc = ActionReply::SuccessReply(); rc.addData(QStringLiteral("installedPaths"), installedPaths); return rc; } ActionReply SddmAuthHelper::uninstalltheme(const QVariantMap &args) { const QString themePath = args[QStringLiteral("filePath")].toString(); const QString themesBaseDir = QStandardPaths::locate(QStandardPaths::GenericDataLocation, QStringLiteral("sddm/themes"), QStandardPaths::LocateDirectory); QDir dir(themePath); if (!dir.exists()) { return ActionReply::HelperErrorReply(); } //validate the themePath is directly inside the themesBaseDir QDir baseDir(themesBaseDir); if(baseDir.absoluteFilePath(dir.dirName()) != dir.absolutePath()) { return ActionReply::HelperErrorReply(); } if (!dir.removeRecursively()) { return ActionReply::HelperErrorReply(); } return ActionReply::SuccessReply(); } KAUTH_HELPER_MAIN("org.kde.kcontrol.kcmsddm", SddmAuthHelper) #include "moc_sddmauthhelper.cpp" diff --git a/sddmthemeinstaller.cpp b/sddmthemeinstaller.cpp index 3bf99c2..3bd4b14 100644 --- a/sddmthemeinstaller.cpp +++ b/sddmthemeinstaller.cpp @@ -1,99 +1,98 @@ /* * Copyright (C) 2016 Marco Martin * Copyright (C) 2016 David Edmundson * * 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 -#include -#include -#include #include #include +#include +#include #include #include -#include int main(int argc, char **argv) { QCommandLineParser parser; QApplication app(argc, argv); //because GHNS doesn't do it's own error reporting on installation failing.. const QString description = i18n("SDDM theme installer"); const char version[] = "1.0"; app.setApplicationVersion(QString::fromLatin1(version)); parser.addVersionOption(); parser.addHelpOption(); parser.setApplicationDescription(description); parser.addOption(QCommandLineOption(QStringList() << QStringLiteral("i") << QStringLiteral("install"), i18n("Install a theme."))); parser.addOption(QCommandLineOption(QStringList() << QStringLiteral("u") << QStringLiteral("uninstall"), i18n("Uninstall a theme."))); parser.addPositionalArgument(QStringLiteral("themefile"), i18n("The theme to install, must be an existing archive file.")); parser.process(app); const QStringList args = parser.positionalArguments(); if (args.isEmpty()) { qWarning() << "No theme file specified."; return 0; } if (parser.isSet(QStringLiteral("install"))) { const QFileInfo themefile(args.first()); if (!themefile.exists()) { qWarning() << "Specified theme file does not exist"; return 0; } KAuth::Action action(QStringLiteral("org.kde.kcontrol.kcmsddm.installtheme")); action.setHelperId(QStringLiteral("org.kde.kcontrol.kcmsddm")); action.addArgument(QStringLiteral("filePath"), themefile.absoluteFilePath()); KAuth::ExecuteJob *job = action.execute(); bool rc = job->exec(); if (!rc) { QString errorString = job->errorString(); qWarning() << job->error() << errorString; KMessageBox::sorry(nullptr, errorString, i18n("Unable to install theme")); return -1; } KConfigGroup cg(KSharedConfig::openConfig(QStringLiteral("sddmthemeinstallerrc"), KConfig::SimpleConfig), "DownloadedThemes"); cg.writeEntry(themefile.absoluteFilePath(), job->data().value(QStringLiteral("installedPaths")).toStringList()); return 0; } if (parser.isSet(QStringLiteral("uninstall"))) { KConfigGroup cg(KSharedConfig::openConfig(QStringLiteral("sddmthemeinstallerrc"), KConfig::SimpleConfig), "DownloadedThemes"); const QStringList installed = cg.readEntry(args.first(), QStringList()); for (const QString &installedTheme: installed) { KAuth::Action action(QStringLiteral("org.kde.kcontrol.kcmsddm.uninstalltheme")); action.setHelperId(QStringLiteral("org.kde.kcontrol.kcmsddm")); action.addArgument(QStringLiteral("filePath"), installedTheme); KAuth::ExecuteJob *job = action.execute(); job->exec(); } return 0; } qWarning() << "either install or uninstall must be passed as an argument"; return -1; } diff --git a/src/advanceconfig.cpp b/src/advanceconfig.cpp index 34debee..42b512b 100644 --- a/src/advanceconfig.cpp +++ b/src/advanceconfig.cpp @@ -1,163 +1,162 @@ /* Copyright 2013 by Reza Fatahilah Shah 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, see . */ #include "advanceconfig.h" #include "ui_advanceconfig.h" +#include "config.h" +#include "cursortheme/thememodel.h" +#include "cursortheme/sortproxymodel.h" +#include "cursortheme/cursortheme.h" +#include "sessionmodel.h" +#include "usersmodel.h" +#include #include -#include #include #include -#include "usersmodel.h" -#include "sessionmodel.h" -#include "config.h" -#include "cursortheme/thememodel.h" -#include "cursortheme/sortproxymodel.h" -#include "cursortheme/cursortheme.h" - const int MIN_UID = 1000; const int MAX_UID = 60000; AdvanceConfig::AdvanceConfig(const KSharedConfigPtr &config, QWidget *parent) : QWidget(parent), mConfig(config) { configUi = new Ui::AdvanceConfig(); configUi->setupUi(this); load(); connect(configUi->userList, SIGNAL(activated(int)), SIGNAL(changed())); connect(configUi->sessionList, SIGNAL(activated(int)), SIGNAL(changed())); connect(configUi->haltCommand, SIGNAL(textChanged(QString)), SIGNAL(changed())); connect(configUi->rebootCommand, SIGNAL(textChanged(QString)), SIGNAL(changed())); connect(configUi->cursorList, SIGNAL(activated(int)), SIGNAL(changed())); connect(configUi->minimumUid, SIGNAL(textChanged(QString)), SIGNAL(changed())); connect(configUi->minimumUid, &QLineEdit::textChanged, this, &AdvanceConfig::slotUidRangeChanged); connect(configUi->maximumUid, SIGNAL(textChanged(QString)), SIGNAL(changed())); connect(configUi->maximumUid, &QLineEdit::textChanged, this, &AdvanceConfig::slotUidRangeChanged); // manually emit changed signal since QCheckBox::clicked will pass false to changed() when unchecked connect(configUi->autoLogin, &QCheckBox::clicked, this, [this] { emit changed(); }); connect(configUi->reloginAfterQuit, &QAbstractButton::clicked, this, [this] { emit changed(); }); } AdvanceConfig::~AdvanceConfig() { delete configUi; } void AdvanceConfig::load() { //Cursor themes CursorThemeModel *cursorModel = new CursorThemeModel(this); proxyCursorModel = new SortProxyModel(this); proxyCursorModel->setSourceModel(cursorModel); proxyCursorModel->setFilterCaseSensitivity(Qt::CaseSensitive); proxyCursorModel->sort(Qt::DisplayRole, Qt::AscendingOrder); configUi->cursorList->setModel(proxyCursorModel); QString currentCursor = mConfig->group("Theme").readEntry("CursorTheme", ""); QModelIndex cursorIndex = proxyCursorModel->findIndex(currentCursor); configUi->cursorList->setCurrentIndex(cursorIndex.row() < 0 ? 0 : cursorIndex.row()); //User list int minUid, maxUid; minUid = mConfig->group("Users").readEntry("MinimumUid", MIN_UID); maxUid = mConfig->group("Users").readEntry("MaximumUid", MAX_UID); userModel = new UsersModel(this); configUi->userList->setModel(userModel); userModel->populate( minUid, maxUid ); sessionModel = new SessionModel(this); configUi->sessionList->setModel(sessionModel); const QString currentUser = mConfig->group("Autologin").readEntry("User", ""); configUi->userList->setCurrentIndex(userModel->indexOf(currentUser)); const QString autologinSession = mConfig->group("Autologin").readEntry("Session", ""); configUi->sessionList->setCurrentIndex(sessionModel->indexOf(autologinSession)); configUi->autoLogin->setChecked(!currentUser.isEmpty()); configUi->reloginAfterQuit->setChecked(mConfig->group("Autologin").readEntry("Relogin", false)); QValidator *uidValidator = new QIntValidator(MIN_UID, MAX_UID, configUi->minimumUid); configUi->minimumUid->setValidator(uidValidator); configUi->minimumUid->setText(QString::number(minUid)); configUi->maximumUid->setValidator(uidValidator); configUi->maximumUid->setText(QString::number(maxUid)); //Commands configUi->haltCommand->setUrl(QUrl::fromLocalFile(mConfig->group("General").readEntry("HaltCommand"))); configUi->rebootCommand->setUrl(QUrl::fromLocalFile(mConfig->group("General").readEntry("RebootCommand"))); } QVariantMap AdvanceConfig::save() { QVariantMap args; qDebug() << "idx:" << configUi->cursorList->currentIndex(); QModelIndex cursorIndex = configUi->cursorList->model()->index(configUi->cursorList->currentIndex(),0); if (cursorIndex.isValid()) { const CursorTheme *cursorTheme = proxyCursorModel->theme(cursorIndex); if (cursorTheme) args[QStringLiteral("kde_settings.conf/Theme/CursorTheme")] = cursorTheme->name(); } args[QStringLiteral("kde_settings.conf/Autologin/User")] = ( configUi->autoLogin->isChecked() ) ? configUi->userList->currentText() : QString(); args[QStringLiteral("kde_settings.conf/Autologin/Session")] = ( configUi->autoLogin->isChecked() ) ? configUi->sessionList->currentData() : QString(); args[QStringLiteral("kde_settings.conf/Autologin/Relogin")] = configUi->reloginAfterQuit->isChecked(); //TODO session int minUid = configUi->minimumUid->text().toInt(); int maxUid = configUi->maximumUid->text().toInt(); if (isUidRangeValid(minUid, maxUid)) { args[QStringLiteral("kde_settings.conf/Users/MinimumUid")] = configUi->minimumUid->text(); args[QStringLiteral("kde_settings.conf/Users/MaximumUid")] = configUi->maximumUid->text(); } args[QStringLiteral("kde_settings.conf/General/HaltCommand")] = configUi->haltCommand->url().toLocalFile(); args[QStringLiteral("kde_settings.conf/General/RebootCommand")] = configUi->rebootCommand->url().toLocalFile(); return args; } void AdvanceConfig::slotUidRangeChanged() { int minUid = configUi->minimumUid->text().toInt(); int maxUid = configUi->maximumUid->text().toInt(); if (!isUidRangeValid(minUid, maxUid)) { return; } userModel->populate(minUid, maxUid); } bool AdvanceConfig::isUidRangeValid(int minUid, int maxUid) const { if (minUid < 0 || minUid > maxUid) return false; return true; } diff --git a/src/configwidgets/selectimagebutton.cpp b/src/configwidgets/selectimagebutton.cpp index ff8f562..ac6ccd7 100644 --- a/src/configwidgets/selectimagebutton.cpp +++ b/src/configwidgets/selectimagebutton.cpp @@ -1,99 +1,97 @@ /* * Button for selecting an image. * * Copyright (C) 2011 Martin Klapetek * Copyright (C) 2011, 2012, 2014 David Edmundson * * 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 St, Fifth Floor, Boston, MA 02110-1301 USA */ - #include "selectimagebutton.h" -#include - -#include #include #include #include +#include +#include #include #include SelectImageButton::SelectImageButton(QWidget *parent) : QToolButton(parent) { QMenu *menu = new QMenu(this); setPopupMode(QToolButton::InstantPopup); setIconSize(QSize(64,64)); menu->addAction(QIcon::fromTheme(QStringLiteral("document-open-folder")), i18n("Load from file..."), this, &SelectImageButton::onLoadImageFromFile); menu->addAction(QIcon::fromTheme(QStringLiteral("edit-clear")), i18n("Clear Image"), this, &SelectImageButton::onClearImage); setMenu(menu); onClearImage(); } SelectImageButton::~SelectImageButton() { } void SelectImageButton::setImagePath(const QString &imagePath) { m_imagePath = imagePath; QPixmap image(imagePath); if (! image.isNull()) { QIcon imageIcon; //scale oversized avatars to fit, but don't stretch smaller images imageIcon.addPixmap(image.scaled(iconSize().boundedTo(image.size()), Qt::KeepAspectRatio)); setIcon(imageIcon); } else { setIcon(QIcon::fromTheme(QStringLiteral("image-x-generic"))); } Q_EMIT imagePathChanged(m_imagePath); } QString SelectImageButton::imagePath() const { return m_imagePath; } void SelectImageButton::onLoadImageFromFile() { QPointer dialog = new QFileDialog(this, i18nc("@title:window", "Select Image")); dialog->setAcceptMode(QFileDialog::AcceptOpen); dialog->setFileMode(QFileDialog::ExistingFile); const QList supportedMimeTypes = QImageReader::supportedMimeTypes(); QStringList mimeTypeFilter; mimeTypeFilter.reserve(supportedMimeTypes.count()); for(const QByteArray &b: supportedMimeTypes) { mimeTypeFilter.append(QString::fromLatin1(b)); } dialog->setMimeTypeFilters(mimeTypeFilter); int rc = dialog->exec(); if (rc == QDialog::Accepted && dialog->selectedFiles().count() > 0) { setImagePath(dialog->selectedFiles().first()); } delete dialog; } void SelectImageButton::onClearImage() { setImagePath(QString()); } diff --git a/src/cursortheme/cursortheme.cpp b/src/cursortheme/cursortheme.cpp index 8ff9c31..f6fcea9 100644 --- a/src/cursortheme/cursortheme.cpp +++ b/src/cursortheme/cursortheme.cpp @@ -1,175 +1,172 @@ /* * Copyright © 2006-2007 Fredrik Höglund * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public * License version 2 or at your option version 3 as published * by the Free Software Foundation. * * 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 "cursortheme.h" +// #include #include -#include #include -#include #include +#include +#include #include -#include "cursortheme.h" - -// #include - #ifdef HAVE_XFIXES # include # include #endif - CursorTheme::CursorTheme(const QString &title, const QString &description) { setTitle(title); setDescription(description); setSample(QStringLiteral("left_ptr")); setIsHidden(false); setIsWritable(false); } QPixmap CursorTheme::icon() const { if (m_icon.isNull()) m_icon = createIcon(); return m_icon; } QImage CursorTheme::autoCropImage(const QImage &image) const { // Compute an autocrop rectangle for the image QRect r(image.rect().bottomRight(), image.rect().topLeft()); const quint32 *pixels = reinterpret_cast(image.bits()); for (int y = 0; y < image.height(); y++) { for (int x = 0; x < image.width(); x++) { if (*(pixels++)) { if (x < r.left()) r.setLeft(x); if (x > r.right()) r.setRight(x); if (y < r.top()) r.setTop(y); if (y > r.bottom()) r.setBottom(y); } } } // Normalize the rectangle return image.copy(r.normalized()); } QPixmap CursorTheme::loadPixmap(const QString &name, int size) const { QImage image = loadImage(name, size); if (image.isNull()) return QPixmap(); return QPixmap::fromImage(image); } static int nominalCursorSize(int iconSize) { for (int i = 512; i > 8; i /= 2) { if (i < iconSize) return i; if ((i * .75) < iconSize) return int(i * .75); } return 8; } QPixmap CursorTheme::createIcon() const { int iconSize = QApplication::style()->pixelMetric(QStyle::PM_LargeIconSize); int cursorSize = nominalCursorSize(iconSize); QSize size = QSize(iconSize, iconSize); QPixmap pixmap = createIcon(cursorSize); if (!pixmap.isNull()) { // Scale the pixmap if it's larger than the preferred icon size if (pixmap.width() > size.width() || pixmap.height() > size.height()) pixmap = pixmap.scaled(size, Qt::KeepAspectRatio, Qt::SmoothTransformation); } return pixmap; } QPixmap CursorTheme::createIcon(int size) const { QPixmap pixmap; QImage image = loadImage(sample(), size); if (image.isNull() && sample() != QLatin1String("left_ptr")) image = loadImage(QStringLiteral("left_ptr"), size); if (!image.isNull()) { pixmap = QPixmap::fromImage(image); } return pixmap; } void CursorTheme::setCursorName(qulonglong cursor, const QString &name) const { #ifdef HAVE_XFIXES if (haveXfixes()) { XFixesSetCursorName(QX11Info::display(), cursor, QFile::encodeName(name)); } #else Q_UNUSED(name); Q_UNUSED(cursor); #endif } bool CursorTheme::haveXfixes() { bool result = false; #ifdef HAVE_XFIXES if (!QX11Info::isPlatformX11()) { return result; } int event_base, error_base; if (XFixesQueryExtension(QX11Info::display(), &event_base, &error_base)) { int major, minor; XFixesQueryVersion(QX11Info::display(), &major, &minor); result = (major >= 2); } #endif return result; } diff --git a/src/cursortheme/cursortheme.h b/src/cursortheme/cursortheme.h index fc1fbc6..adff06d 100644 --- a/src/cursortheme/cursortheme.h +++ b/src/cursortheme/cursortheme.h @@ -1,145 +1,144 @@ /* * Copyright © 2006-2007 Fredrik Höglund * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public * License version 2 or at your option version 3 as published * by the Free Software Foundation. * * 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. */ - #ifndef CURSORTHEME_H #define CURSORTHEME_H -#include #include +#include /** * This is the abstract base class for all cursor themes stored in a * CursorThemeModel and previewed in a PreviewWidget. * * All cursor themes have a title, a description, an icon, and an internal * name, all of which, except for the internal name, CursorThemeModel * supplies to item views. * * A cursor theme may also have a path to the directory where the theme * is located in the filesystem. If isWritable() returns true, This directory * may be deleted in order to remove the theme at the users request. * * Subclasses must reimplement loadImage() and loadCursor(), which are * called by PreviewWidget to load cursors and cursor images. Subclasses may * optionally reimplement loadPixmap(), which in the default implementation * calls loadImage(), and converts the returned image to a pixmap. * Subclasses may also reimplement the protected function createIcon(), * which creates the icon pixmap that's supplied to item views. The default * implementation calls loadImage() to load the sample cursor, and creates * the icon from that. */ class CursorTheme { public: enum ItemDataRole { // Note: use printf "0x%08X\n" $(($RANDOM*$RANDOM)) // to define additional roles. DisplayDetailRole = 0x24A3DAF8 }; CursorTheme() {} CursorTheme(const QString &title, const QString &description = QString()); virtual ~CursorTheme() {} const QString title() const { return m_title; } const QString description() const { return m_description; } const QString sample() const { return m_sample; } const QString name() const { return m_name; } const QString path() const { return m_path; } /** @returns A list of the available sizes in this cursor theme, @warning This list may be empty if the engine doesn't support the recognition of the size. */ const QList availableSizes() const { return m_availableSizes; } bool isWritable() const { return m_writable; } bool isHidden() const { return m_hidden; } QPixmap icon() const; /// Hash value for the internal name uint hash() const { return m_hash; } /// Loads the cursor image @p name, with the nominal size @p size. /// The image should be autocropped to the smallest possible size. /// If the theme doesn't have the cursor @p name, it should return a null image. virtual QImage loadImage(const QString &name, int size = 0) const = 0; /// Convenience function. Default implementation calls /// QPixmap::fromImage(loadImage()); virtual QPixmap loadPixmap(const QString &name, int size = 0) const; /// Loads the cursor @p name, with the nominal size @p size. /// If the theme doesn't have the cursor @p name, it should return /// the default cursor from the active theme instead. virtual qulonglong loadCursor(const QString &name, int size = 0) const = 0; /** Creates the icon returned by @ref icon(). Don't use this function directly but use @ref icon() instead, because @ref icon() caches the icon. @returns A pixmap with a cursor (usually left_ptr) that can be used as icon for this theme. The size is adopted to standard icon sizes.*/ virtual QPixmap createIcon() const; /** @returns A pixmap with a cursor (usually left_ptr) that can be used as icon for this theme. */ virtual QPixmap createIcon(int size) const; static bool haveXfixes(); protected: void setTitle( const QString &title ) { m_title = title; } void setDescription( const QString &desc ) { m_description = desc; } void setSample( const QString &sample ) { m_sample = sample; } inline void setName( const QString &name ); void setPath( const QString &path ) { m_path = path; } void setAvailableSizes( const QList &availableSizes ) { m_availableSizes = availableSizes; } void setIcon( const QPixmap &icon ) { m_icon = icon; } void setIsWritable( bool val ) { m_writable = val; } void setIsHidden( bool val ) { m_hidden = val; } /// Convenience function for cropping an image. QImage autoCropImage( const QImage &image ) const; // Convenience function that uses Xfixes to tag a cursor with a name void setCursorName(qulonglong cursor, const QString &name) const; QString m_title; QString m_description; QString m_path; QList m_availableSizes; QString m_sample; mutable QPixmap m_icon; bool m_writable:1; bool m_hidden:1; private: QString m_name; uint m_hash; friend class CursorThemeModel; }; void CursorTheme::setName(const QString &name) { m_name = name; m_hash = qHash(name); } #endif // CURSORTHEME_H diff --git a/src/cursortheme/dummytheme.cpp b/src/cursortheme/dummytheme.cpp index da80e50..01626da 100644 --- a/src/cursortheme/dummytheme.cpp +++ b/src/cursortheme/dummytheme.cpp @@ -1,49 +1,48 @@ /* Copyright 2013 by Reza Fatahilah Shah 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, see . */ - #include "dummytheme.h" -#include #include +#include #include DummyTheme::DummyTheme() : CursorTheme(i18n("Default"), i18n("The default cursor theme in SDDM")) { } DummyTheme::~DummyTheme() { } QImage DummyTheme::loadImage(const QString &name, int) const { Q_UNUSED(name) return QImage(); } qulonglong DummyTheme::loadCursor(const QString &name, int) const { Q_UNUSED(name) return 0; } diff --git a/src/cursortheme/sortproxymodel.cpp b/src/cursortheme/sortproxymodel.cpp index b9d6309..52e163f 100644 --- a/src/cursortheme/sortproxymodel.cpp +++ b/src/cursortheme/sortproxymodel.cpp @@ -1,50 +1,49 @@ /* * Copyright © 2006-2007 Fredrik Höglund * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public * License version 2 or at your option version 3 as published * by the Free Software Foundation. * * 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 "sortproxymodel.h" #include "cursortheme.h" +#include "sortproxymodel.h" +#include int SortProxyModel::compare(const QModelIndex &left, const QModelIndex &right, int role) const { const QAbstractItemModel *model = sourceModel(); QString first = model->data(left, role).toString(); QString second = model->data(right, role).toString(); if (filterCaseSensitivity() == Qt::CaseSensitive) { first = first.toLower(); second = second.toLower(); } return QString::localeAwareCompare(first, second); } bool SortProxyModel::lessThan(const QModelIndex &left, const QModelIndex &right) const { const int result = compare(left, right, Qt::DisplayRole); if (result != 0) return (result < 0); else return compare(left, right, CursorTheme::DisplayDetailRole) < 0; } diff --git a/src/cursortheme/sortproxymodel.h b/src/cursortheme/sortproxymodel.h index f420c77..c1fa781 100644 --- a/src/cursortheme/sortproxymodel.h +++ b/src/cursortheme/sortproxymodel.h @@ -1,77 +1,77 @@ /* * Copyright © 2006-2007 Fredrik Höglund * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public * License version 2 or at your option version 3 as published * by the Free Software Foundation. * * 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. */ - #ifndef SORTPROXYMODEL_H #define SORTPROXYMODEL_H -#include #include "thememodel.h" +#include + /** * SortProxyModel is a sorting proxy model intended to be used in combination * with the ItemDelegate class. * * First it compares the Qt::DisplayRoles, and if they match it compares * the CursorTheme::DisplayDetailRoles. * * The model assumes both display roles are QStrings. */ class SortProxyModel : public QSortFilterProxyModel { public: explicit SortProxyModel(QObject *parent = nullptr) : QSortFilterProxyModel(parent) {} ~SortProxyModel() Q_DECL_OVERRIDE {} inline const CursorTheme *theme(const QModelIndex &index) const; inline QModelIndex findIndex(const QString &name) const; inline QModelIndex defaultIndex() const; inline void removeTheme(const QModelIndex &index); private: int compare(const QModelIndex &left, const QModelIndex &right, int role) const; protected: bool lessThan(const QModelIndex &left, const QModelIndex &right) const Q_DECL_OVERRIDE; }; const CursorTheme *SortProxyModel::theme(const QModelIndex &index) const { CursorThemeModel *model = static_cast(sourceModel()); return model->theme(mapToSource(index)); } QModelIndex SortProxyModel::findIndex(const QString &name) const { CursorThemeModel *model = static_cast(sourceModel()); return mapFromSource(model->findIndex(name)); } QModelIndex SortProxyModel::defaultIndex() const { CursorThemeModel *model = static_cast(sourceModel()); return mapFromSource(model->defaultIndex()); } void SortProxyModel::removeTheme(const QModelIndex &index) { CursorThemeModel *model = static_cast(sourceModel()); model->removeTheme(mapToSource(index)); } #endif // SORTPROXYMODEL_H diff --git a/src/cursortheme/thememodel.cpp b/src/cursortheme/thememodel.cpp index ce8b982..becc1bc 100644 --- a/src/cursortheme/thememodel.cpp +++ b/src/cursortheme/thememodel.cpp @@ -1,413 +1,410 @@ /* * Copyright © 2005-2007 Fredrik Höglund * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public * License version 2 as published by the Free Software Foundation. * * 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 "thememodel.h" +#include "xcursortheme.h" - -#include -#include -#include -#include #include +#include #include -#include "thememodel.h" -#include "xcursortheme.h" +#include +#include +#include #include #include // Check for older version #if !defined(XCURSOR_LIB_MAJOR) && defined(XCURSOR_MAJOR) # define XCURSOR_LIB_MAJOR XCURSOR_MAJOR # define XCURSOR_LIB_MINOR XCURSOR_MINOR #endif - - CursorThemeModel::CursorThemeModel(QObject *parent) : QAbstractTableModel(parent) { insertThemes(); } CursorThemeModel::~CursorThemeModel() { qDeleteAll(list); list.clear(); } void CursorThemeModel::refreshList() { beginResetModel(); qDeleteAll(list); list.clear(); endResetModel(); insertThemes(); } QVariant CursorThemeModel::headerData(int section, Qt::Orientation orientation, int role) const { // Only provide text for the headers if (role != Qt::DisplayRole) return QVariant(); // Horizontal header labels if (orientation == Qt::Horizontal) { switch (section) { case NameColumn: return i18n("Name"); case DescColumn: return i18n("Description"); default: return QVariant(); } } // Numbered vertical header lables return QString(section); } QVariant CursorThemeModel::data(const QModelIndex &index, int role) const { if (!index.isValid() || index.row() < 0 || index.row() >= list.count()) return QVariant(); const CursorTheme *theme = list.at(index.row()); // Text label if (role == Qt::DisplayRole) { switch (index.column()) { case NameColumn: return theme->title(); case DescColumn: return theme->description(); default: return QVariant(); } } // Description for the first name column if (role == CursorTheme::DisplayDetailRole && index.column() == NameColumn) return theme->description(); // Icon for the name column if (role == Qt::DecorationRole && index.column() == NameColumn) return theme->icon(); return QVariant(); } void CursorThemeModel::sort(int column, Qt::SortOrder order) { Q_UNUSED(column); Q_UNUSED(order); // Sorting of the model isn't implemented, as the KCM currently uses // a sorting proxy model. } const CursorTheme *CursorThemeModel::theme(const QModelIndex &index) { if (!index.isValid()) return nullptr; if (index.row() < 0 || index.row() >= list.count()) return nullptr; return list.at(index.row()); } QModelIndex CursorThemeModel::findIndex(const QString &name) { uint hash = qHash(name); for (int i = 0; i < list.count(); i++) { const CursorTheme *theme = list.at(i); if (theme->hash() == hash) return index(i, 0); } return QModelIndex(); } QModelIndex CursorThemeModel::defaultIndex() { return findIndex(defaultName); } const QStringList CursorThemeModel::searchPaths() { if (!baseDirs.isEmpty()) return baseDirs; #if XCURSOR_LIB_MAJOR == 1 && XCURSOR_LIB_MINOR < 1 // These are the default paths Xcursor will scan for cursor themes QString path("~/.icons:/usr/share/icons:/usr/share/pixmaps:/usr/X11R6/lib/X11/icons"); // If XCURSOR_PATH is set, use that instead of the default path char *xcursorPath = std::getenv("XCURSOR_PATH"); if (xcursorPath) path = xcursorPath; #else // Get the search path from Xcursor QString path = QString::fromLatin1(XcursorLibraryPath()); #endif // Separate the paths baseDirs = path.split(QLatin1Char(':'), QString::SkipEmptyParts); // Remove duplicates QMutableStringListIterator i(baseDirs); while (i.hasNext()) { const QString path = i.next(); QMutableStringListIterator j(i); while (j.hasNext()) if (j.next() == path) j.remove(); } // Expand all occurrences of ~/ to the home dir baseDirs.replaceInStrings(QRegExp(QLatin1String("^~\\/")), QDir::home().path() + QLatin1Char('/')); return baseDirs; } bool CursorThemeModel::hasTheme(const QString &name) const { const uint hash = qHash(name); foreach (const CursorTheme *theme, list) if (theme->hash() == hash) return true; return false; } bool CursorThemeModel::isCursorTheme(const QString &theme, const int depth) { // Prevent infinite recursion if (depth > 10) return false; // Search each icon theme directory for 'theme' foreach (const QString &baseDir, searchPaths()) { QDir dir(baseDir); if (!dir.exists() || !dir.cd(theme)) continue; // If there's a cursors subdir, we'll assume this is a cursor theme if (dir.exists(QStringLiteral("cursors"))) return true; // If the theme doesn't have an index.theme file, it can't inherit any themes. if (!dir.exists(QStringLiteral("index.theme"))) continue; // Open the index.theme file, so we can get the list of inherited themes KConfig config(dir.path() + QStringLiteral("/index.theme"), KConfig::NoGlobals); KConfigGroup cg(&config, "Icon Theme"); // Recurse through the list of inherited themes, to check if one of them // is a cursor theme. QStringList inherits = cg.readEntry("Inherits", QStringList()); foreach (const QString &inherit, inherits) { // Avoid possible DoS if (inherit == theme) continue; if (isCursorTheme(inherit, depth + 1)) return true; } } return false; } bool CursorThemeModel::handleDefault(const QDir &themeDir) { QFileInfo info(themeDir.path()); // If "default" is a symlink if (info.isSymLink()) { QFileInfo target(info.symLinkTarget()); if (target.exists() && (target.isDir() || target.isSymLink())) defaultName = target.fileName(); return true; } // If there's no cursors subdir, or if it's empty if (!themeDir.exists(QStringLiteral("cursors")) || QDir(themeDir.path() + QStringLiteral("/cursors")) .entryList(QDir::Files | QDir::NoDotAndDotDot ).isEmpty()) { if (themeDir.exists(QStringLiteral("index.theme"))) { XCursorTheme theme(themeDir); if (!theme.inherits().isEmpty()) defaultName = theme.inherits().at(0); } return true; } defaultName = QStringLiteral("default"); return false; } void CursorThemeModel::processThemeDir(const QDir &themeDir) { bool haveCursors = themeDir.exists(QStringLiteral("cursors")); // Special case handling of "default", since it's usually either a // symlink to another theme, or an empty theme that inherits another // theme. if (defaultName.isNull() && themeDir.dirName() == QLatin1String("default")) { if (handleDefault(themeDir)) return; } // If the directory doesn't have a cursors subdir and lacks an // index.theme file it can't be a cursor theme. if (!themeDir.exists(QStringLiteral("index.theme")) && !haveCursors) return; // Create a cursor theme object for the theme dir XCursorTheme *theme = new XCursorTheme(themeDir); // Skip this theme if it's hidden. if (theme->isHidden()) { delete theme; return; } // If there's no cursors subdirectory we'll do a recursive scan // to check if the theme inherits a theme with one. if (!haveCursors) { bool foundCursorTheme = false; foreach (const QString &name, theme->inherits()) if ((foundCursorTheme = isCursorTheme(name))) break; if (!foundCursorTheme) { delete theme; return; } } // Append the theme to the list beginInsertRows(QModelIndex(), list.size(), list.size()); list.append(theme); endInsertRows(); } void CursorThemeModel::insertThemes() { // Scan each base dir for Xcursor themes and add them to the list. foreach (const QString &baseDir, searchPaths()) { QDir dir(baseDir); if (!dir.exists()) continue; // Process each subdir in the directory foreach (const QString &name, dir.entryList(QDir::Dirs | QDir::NoDotAndDotDot)) { // Don't process the theme if a theme with the same name already exists // in the list. Xcursor will pick the first one it finds in that case, // and since we use the same search order, the one Xcursor picks should // be the one already in the list. if (hasTheme(name) || !dir.cd(name)) continue; processThemeDir(dir); dir.cdUp(); // Return to the base dir } } // The theme Xcursor will end up using if no theme is configured if (defaultName.isNull() || !hasTheme(defaultName)) defaultName = QStringLiteral("KDE_Classic"); } bool CursorThemeModel::addTheme(const QDir &dir) { XCursorTheme *theme = new XCursorTheme(dir); // Don't add the theme to the list if it's hidden if (theme->isHidden()) { delete theme; return false; } // ### If the theme is hidden, the user will probably find it strange that it // doesn't appear in the list view. There also won't be a way for the user // to delete the theme using the KCM. Perhaps a warning about this should // be issued, and the user be given a chance to undo the installation. // If an item with the same name already exists in the list, // we'll remove it before inserting the new one. for (int i = 0; i < list.count(); i++) { if (list.at(i)->hash() == theme->hash()) { removeTheme(index(i, 0)); break; } } // Append the theme to the list beginInsertRows(QModelIndex(), rowCount(), rowCount()); list.append(theme); endInsertRows(); return true; } void CursorThemeModel::removeTheme(const QModelIndex &index) { if (!index.isValid()) return; beginRemoveRows(QModelIndex(), index.row(), index.row()); delete list.takeAt(index.row()); endRemoveRows(); } diff --git a/src/cursortheme/thememodel.h b/src/cursortheme/thememodel.h index 007064a..7654bf8 100644 --- a/src/cursortheme/thememodel.h +++ b/src/cursortheme/thememodel.h @@ -1,114 +1,113 @@ /* * Copyright © 2005-2007 Fredrik Höglund * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public * License version 2 or at your option version 3 as published * by the Free Software Foundation. * * 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. */ - #ifndef THEMEMODEL_H #define THEMEMODEL_H #include #include class QDir; class CursorTheme; // The two TableView/TreeView columns provided by the model enum Columns { NameColumn = 0, DescColumn }; /** * The CursorThemeModel class provides a model for all locally installed * Xcursor themes, and the KDE/Qt legacy bitmap theme. * * This class automatically scans the locations in the file system from * which Xcursor loads cursors, and creates an internal list of all * available cursor themes. * * The model provides this theme list to item views in the form of a list * of rows with two columns; the first column has the theme's descriptive * name and its sample cursor as its icon, and the second column contains * the theme's description. * * Additional Xcursor themes can be added to a model after it's been * created, by calling addTheme(), which takes QDir as a parameter, * with the themes location. The intention is for this function to be * called when a new Xcursor theme has been installed, after the model * was instantiated. * * The KDE legacy theme is a read-only entry, with the descriptive name * "KDE Classic", and the internal name "#kde_legacy#". * * Calling defaultIndex() will return the index of the theme Xcursor * will use if the user hasn't explicitly configured a cursor theme. */ class CursorThemeModel : public QAbstractTableModel { Q_OBJECT public: explicit CursorThemeModel(QObject *parent = nullptr); ~CursorThemeModel() Q_DECL_OVERRIDE; inline int columnCount(const QModelIndex &parent = QModelIndex()) const Q_DECL_OVERRIDE; inline int rowCount(const QModelIndex &parent = QModelIndex()) const Q_DECL_OVERRIDE; QVariant headerData(int section, Qt::Orientation orientation, int role) const Q_DECL_OVERRIDE; QVariant data(const QModelIndex &index, int role) const Q_DECL_OVERRIDE; void sort(int column, Qt::SortOrder order = Qt::AscendingOrder) Q_DECL_OVERRIDE; /// Returns the CursorTheme at @p index. const CursorTheme *theme(const QModelIndex &index); /// Returns the index for the CursorTheme with the internal name @p name, /// or an invalid index if no matching theme can be found. QModelIndex findIndex(const QString &name); /// Returns the index for the default theme. QModelIndex defaultIndex(); /// Adds the theme in @p dir, and returns @a true if successful or @a false otherwise. bool addTheme(const QDir &dir); void removeTheme(const QModelIndex &index); /// Returns the list of base dirs Xcursor looks for themes in. const QStringList searchPaths(); /// Refresh the list of themes by checking what's on disk. void refreshList(); private: bool handleDefault(const QDir &dir); void processThemeDir(const QDir &dir); void insertThemes(); bool hasTheme(const QString &theme) const; bool isCursorTheme(const QString &theme, const int depth = 0); private: QList list; QStringList baseDirs; QString defaultName; }; int CursorThemeModel::rowCount(const QModelIndex &) const { return list.count(); } int CursorThemeModel::columnCount(const QModelIndex &) const { return 2; } #endif // THEMEMODEL_H diff --git a/src/cursortheme/xcursortheme.cpp b/src/cursortheme/xcursortheme.cpp index 2b3e25c..195c576 100644 --- a/src/cursortheme/xcursortheme.cpp +++ b/src/cursortheme/xcursortheme.cpp @@ -1,232 +1,231 @@ /* * Copyright © 2006-2007 Fredrik Höglund * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public * License version 2 or at your option version 3 as published * by the Free Software Foundation. * * 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 +#include -#include #include #include +#include -#include -#include -#include -#include -#include #include #include #include "xcursortheme.h" - // Static variable holding alternative names for some cursors QHash XCursorTheme::alternatives; XCursorTheme::XCursorTheme(const QDir &themeDir) : CursorTheme(themeDir.dirName()) { // Directory information setName(themeDir.dirName()); setPath(themeDir.path()); setIsWritable(QFileInfo(themeDir.path()).isWritable()); // ### perhaps this shouldn't be cached if (themeDir.exists(QStringLiteral("index.theme"))) parseIndexFile(); QString cursorFile = path() + QStringLiteral("/cursors/left_ptr"); QList sizeList; XcursorImages *images = XcursorFilenameLoadAllImages(qPrintable(cursorFile)); if (images) { for (int i = 0; i < images->nimage; ++i) { if (!sizeList.contains(images->images[i]->size)) sizeList.append(images->images[i]->size); }; XcursorImagesDestroy(images); std::sort(sizeList.begin(), sizeList.end()); m_availableSizes = sizeList; }; if (!sizeList.isEmpty()) { QString sizeListString = QString::number(sizeList.takeFirst()); while (!sizeList.isEmpty()) { sizeListString.append(QStringLiteral(", ")); sizeListString.append(QString::number(sizeList.takeFirst())); }; QString tempString = i18nc( "@info The argument is the list of available sizes (in pixel). Example: " "'Available sizes: 24' or 'Available sizes: 24, 36, 48'", "(Available sizes: %1)", sizeListString); if (m_description.isEmpty()) m_description = tempString; else m_description = m_description + QLatin1Char(' ') + tempString; }; } void XCursorTheme::parseIndexFile() { KConfig config(path() + QStringLiteral("/index.theme"), KConfig::NoGlobals); KConfigGroup cg(&config, "Icon Theme"); m_title = cg.readEntry("Name", m_title); m_description = cg.readEntry("Comment", m_description); m_sample = cg.readEntry("Example", m_sample); m_hidden = cg.readEntry("Hidden", false); m_inherits = cg.readEntry("Inherits", QStringList()); } QString XCursorTheme::findAlternative(const QString &name) const { if (alternatives.isEmpty()) { alternatives.reserve(18); // Qt uses non-standard names for some core cursors. // If Xcursor fails to load the cursor, Qt creates it with the correct name using the // core protcol instead (which in turn calls Xcursor). We emulate that process here. // Note that there's a core cursor called cross, but it's not the one Qt expects. alternatives.insert(QStringLiteral("cross"), QStringLiteral("crosshair")); alternatives.insert(QStringLiteral("up_arrow"), QStringLiteral("center_ptr")); alternatives.insert(QStringLiteral("wait"), QStringLiteral("watch")); alternatives.insert(QStringLiteral("ibeam"), QStringLiteral("xterm")); alternatives.insert(QStringLiteral("size_all"), QStringLiteral("fleur")); alternatives.insert(QStringLiteral("pointing_hand"), QStringLiteral("hand2")); // Precomputed MD5 hashes for the hardcoded bitmap cursors in Qt and KDE. // Note that the MD5 hash for left_ptr_watch is for the KDE version of that cursor. alternatives.insert(QStringLiteral("size_ver"), QStringLiteral("00008160000006810000408080010102")); alternatives.insert(QStringLiteral("size_hor"), QStringLiteral("028006030e0e7ebffc7f7070c0600140")); alternatives.insert(QStringLiteral("size_bdiag"), QStringLiteral("fcf1c3c7cd4491d801f1e1c78f100000")); alternatives.insert(QStringLiteral("size_fdiag"), QStringLiteral("c7088f0f3e6c8088236ef8e1e3e70000")); alternatives.insert(QStringLiteral("whats_this"), QStringLiteral("d9ce0ab605698f320427677b458ad60b")); alternatives.insert(QStringLiteral("split_h"), QStringLiteral("14fef782d02440884392942c11205230")); alternatives.insert(QStringLiteral("split_v"), QStringLiteral("2870a09082c103050810ffdffffe0204")); alternatives.insert(QStringLiteral("forbidden"), QStringLiteral("03b6e0fcb3499374a867c041f52298f0")); alternatives.insert(QStringLiteral("left_ptr_watch"), QStringLiteral("3ecb610c1bf2410f44200f48c40d3599")); alternatives.insert(QStringLiteral("hand2"), QStringLiteral("e29285e634086352946a0e7090d73106")); alternatives.insert(QStringLiteral("openhand"), QStringLiteral("9141b49c8149039304290b508d208c40")); alternatives.insert(QStringLiteral("closedhand"), QStringLiteral("05e88622050804100c20044008402080")); } return alternatives.value(name, QString()); } XcursorImage *XCursorTheme::xcLoadImage(const QString &image, int size) const { const QByteArray cursorName = QFile::encodeName(image); const QByteArray themeName = QFile::encodeName(name()); return XcursorLibraryLoadImage(cursorName.constData(), themeName.constData(), size); } XcursorImages *XCursorTheme::xcLoadImages(const QString &image, int size) const { const QByteArray cursorName = QFile::encodeName(image); const QByteArray themeName = QFile::encodeName(name()); return XcursorLibraryLoadImages(cursorName.constData(), themeName.constData(), size); } int XCursorTheme::autodetectCursorSize() const { /* This code is basically borrowed from display.c of the XCursor library We can't use "int XcursorGetDefaultSize(Display *dpy)" because if previously the cursor size was set to a custom value, it would return this custom value. */ int size = 0; int dpi = 0; Display *dpy = QX11Info::display(); // Fallback on wayland if (!dpy) return 24; // The string "v" is owned and will be destroyed by Xlib char *v = XGetDefault(dpy, "Xft", "dpi"); if (v) dpi = atoi(v); if (dpi) size = dpi * 16 / 72; if (size == 0) { int dim; if (DisplayHeight(dpy, DefaultScreen(dpy)) < DisplayWidth(dpy, DefaultScreen(dpy))) { dim = DisplayHeight(dpy, DefaultScreen(dpy)); } else { dim = DisplayWidth(dpy, DefaultScreen(dpy)); }; size = dim / 48; } return size; } qulonglong XCursorTheme::loadCursor(const QString &name, int size) const { if (size <= 0) size = autodetectCursorSize(); // Load the cursor images XcursorImages *images = xcLoadImages(name, size); if (!images) images = xcLoadImages(findAlternative(name), size); if (!images) return None; // Create the cursor Cursor handle = XcursorImagesLoadCursor(QX11Info::display(), images); XcursorImagesDestroy(images); setCursorName(handle, name); return handle; } QImage XCursorTheme::loadImage(const QString &name, int size) const { if (size <= 0) size = autodetectCursorSize(); // Load the image XcursorImage *xcimage = xcLoadImage(name, size); if (!xcimage) xcimage = xcLoadImage(findAlternative(name), size); if (!xcimage) { return QImage(); } // Convert the XcursorImage to a QImage, and auto-crop it QImage image((uchar *)xcimage->pixels, xcimage->width, xcimage->height, QImage::Format_ARGB32_Premultiplied ); image = autoCropImage(image); XcursorImageDestroy(xcimage); return image; } diff --git a/src/cursortheme/xcursortheme.h b/src/cursortheme/xcursortheme.h index 8c7d760..63f7000 100644 --- a/src/cursortheme/xcursortheme.h +++ b/src/cursortheme/xcursortheme.h @@ -1,71 +1,70 @@ /* * Copyright © 2006-2007 Fredrik Höglund * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public * License version 2 or at your option version 3 as published by * the Free Software Foundation. * * 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. */ - #ifndef XCURSORTHEME_H #define XCURSORTHEME_H -#include - #include "cursortheme.h" +#include + class QDir; struct _XcursorImage; struct _XcursorImages; typedef _XcursorImage XcursorImage; typedef _XcursorImages XcursorImages; /** * The XCursorTheme class is a CursorTheme implementation for Xcursor themes. */ class XCursorTheme : public CursorTheme { public: /** * Initializes itself from the @p dir information, and parses the * index.theme file if the dir has one. */ XCursorTheme(const QDir &dir); ~XCursorTheme() Q_DECL_OVERRIDE {} const QStringList inherits() const { return m_inherits; } QImage loadImage(const QString &name, int size = 0) const Q_DECL_OVERRIDE; qulonglong loadCursor(const QString &name, int size = 0) const Q_DECL_OVERRIDE; protected: XCursorTheme(const QString &title, const QString &desc) : CursorTheme(title, desc) {} void setInherits(const QStringList &val) { m_inherits = val; } private: XcursorImage *xcLoadImage(const QString &name, int size) const; XcursorImages *xcLoadImages(const QString &name, int size) const; void parseIndexFile(); QString findAlternative(const QString &name) const; /** Returns the size that the XCursor library would use if no cursor size is given. This depends mainly on Xft.dpi. */ int autodetectCursorSize() const; QStringList m_inherits; static QHash alternatives; }; #endif // XCURSORTHEME_H diff --git a/src/sddmkcm.cpp b/src/sddmkcm.cpp index 87f4363..0dc41ea 100644 --- a/src/sddmkcm.cpp +++ b/src/sddmkcm.cpp @@ -1,135 +1,130 @@ /* Copyright 2013 by Reza Fatahilah Shah 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, see . */ +#include "advanceconfig.h" +#include "config.h" #include "sddmkcm.h" +#include "themeconfig.h" #include +#include #include #include #include -#include - -#include - #include - +#include +#include #include -#include - -#include "config.h" -#include "themeconfig.h" -#include "advanceconfig.h" - +#include #include #include -#include K_PLUGIN_FACTORY(SddmKcmFactory, registerPlugin();) SddmKcm::SddmKcm(QWidget *parent, const QVariantList &args) : KCModule(parent, args) { KAboutData* aboutData = new KAboutData(QStringLiteral("kcmsddm"), i18n("SDDM KDE Config"), QLatin1String(PROJECT_VERSION)); aboutData->setShortDescription(i18n("Login screen using the SDDM")); aboutData->setLicense(KAboutLicense::GPL_V2); aboutData->setHomepage(QStringLiteral("https://projects.kde.org/projects/kde/workspace/sddm-kcm")); aboutData->addAuthor(QStringLiteral("Reza Fatahilah Shah"), i18n("Author"), QStringLiteral("rshah0385@kireihana.com")); aboutData->addAuthor(QStringLiteral("David Edmundson"), i18n("Author"), QStringLiteral("davidedmundson@kde.org")); setAboutData(aboutData); setNeedsAuthorization(true); mSddmConfig = KSharedConfig::openConfig(QStringLiteral(SDDM_CONFIG_FILE), KConfig::CascadeConfig); mSddmOldConfig = KSharedConfig::openConfig(QStringLiteral("sddm.conf"), KConfig::CascadeConfig); // This does not listen for new config files in the directory. QStringList configFiles = QDir(QLatin1String(SDDM_CONFIG_DIR)).entryList(QDir::Files | QDir::NoDotAndDotDot, QDir::LocaleAware), systemConfigFiles = QDir(QLatin1String(SDDM_SYSTEM_CONFIG_DIR)).entryList(QDir::Files | QDir::NoDotAndDotDot, QDir::LocaleAware); // QStringBuilder keeps dangling references, so force return of QString (QTBUG-47066) std::transform(systemConfigFiles.begin(), systemConfigFiles.end(), systemConfigFiles.begin(), [](const QString &filename) -> QString { return QStringLiteral(SDDM_SYSTEM_CONFIG_DIR "/") + filename; }); std::transform(configFiles.begin(), configFiles.end(), configFiles.begin(), [](const QString &filename) -> QString { return QStringLiteral(SDDM_CONFIG_DIR "/") + filename; }); mSddmConfig->addConfigSources(systemConfigFiles + configFiles); mSddmOldConfig->addConfigSources(systemConfigFiles + configFiles); prepareUi(); } SddmKcm::~SddmKcm() { } void SddmKcm::save() { QVariantMap args; args[QStringLiteral("kde_settings.conf")] = QString {QLatin1String(SDDM_CONFIG_DIR "/") + QStringLiteral("kde_settings.conf")}; args[QStringLiteral("sddm.conf")] = QLatin1String(SDDM_CONFIG_FILE); if (!mThemeConfig->themeConfigPath().isEmpty()) { args[QStringLiteral("theme.conf.user")] = QString(mThemeConfig->themeConfigPath() + QLatin1String(".user")); } qDebug() << "Ovr:" << args[QStringLiteral("theme.conf.user")].toString(); args.unite(mThemeConfig->save()); args.unite(mAdvanceConfig->save()); KAuth::Action saveAction = authAction(); saveAction.setHelperId(QStringLiteral("org.kde.kcontrol.kcmsddm")); saveAction.setArguments(args); auto job = saveAction.execute(); job->exec(); if (job->error()){ qDebug() << "Save Failed"; qDebug() << job->errorString(); qDebug() << job->errorText(); } else { changed(false); qDebug() << "Option saved"; } } void SddmKcm::prepareUi() { QHBoxLayout* layout = new QHBoxLayout(this); QTabWidget* tabHolder = new QTabWidget(this); layout->addWidget(tabHolder); mThemeConfig = new ThemeConfig(mSddmConfig, this); connect(mThemeConfig, SIGNAL(changed(bool)), SIGNAL(changed(bool))); tabHolder->addTab(mThemeConfig, i18n("Theme")); mAdvanceConfig = new AdvanceConfig(mSddmConfig, this); connect(mAdvanceConfig, SIGNAL(changed(bool)), SIGNAL(changed(bool))); tabHolder->addTab(mAdvanceConfig, i18n("Advanced")); } #include "sddmkcm.moc" diff --git a/src/sessionmodel.cpp b/src/sessionmodel.cpp index 0bbe217..efa7ef6 100644 --- a/src/sessionmodel.cpp +++ b/src/sessionmodel.cpp @@ -1,156 +1,154 @@ /*************************************************************************** * Copyright (c) 2013 Abdurrahman AVCI * * 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 "config.h" #include "sessionmodel.h" +#include + #include #include #include #include -#include - #include - class Session { public: QString file; QString name; QString exec; QString comment; }; typedef std::shared_ptr SessionPtr; class SessionModelPrivate { public: int lastIndex { 0 }; QList sessions; }; SessionModel::SessionModel(QObject *parent) : QAbstractListModel(parent), d(new SessionModelPrivate()) { loadDir(QStringLiteral(XSESSIONS_DIR), SessionTypeX); loadDir(QStringLiteral(WAYLAND_SESSIONS_DIR), SessionTypeWayland); } SessionModel::~SessionModel() { delete d; } void SessionModel::loadDir(const QString &path, SessionType type) { QDir dir(path); dir.setNameFilters(QStringList() << QStringLiteral("*.desktop")); dir.setFilter(QDir::Files); // read session foreach(const QString &session, dir.entryList()) { QFile inputFile(dir.absoluteFilePath(session)); if (!inputFile.open(QIODevice::ReadOnly)) continue; SessionPtr si { new Session { session.chopped(strlen(".desktop")), QString(), QString(), QString() } }; bool isHidden = false; QString current_section; QTextStream in(&inputFile); while (!in.atEnd()) { QString line = in.readLine(); if (line.startsWith(QLatin1String("["))) { // The section name ends before the last ] before the start of a comment int end = line.lastIndexOf(QLatin1Char(']'), line.indexOf(QLatin1Char('#'))); if (end != -1) current_section = line.mid(1, end - 1); } if (current_section != QLatin1String("Desktop Entry")) continue; // We are only interested in the "Desktop Entry" section if (line.startsWith(QLatin1String("Name="))) { si->name = line.mid(5); if (type == SessionTypeWayland) { //we want to exactly match the SDDM prompt which is formatted in this way si->name = i18nc("%1 is the name of a session", "%1 (Wayland)", si->name); } } if (line.startsWith(QLatin1String("Exec="))) si->exec = line.mid(5); if (line.startsWith(QLatin1String("Comment="))) si->comment = line.mid(8); if (line.startsWith(QLatin1String("Hidden="))) isHidden = line.mid(7).toLower() == QLatin1String("true"); } if (!isHidden) { // add to sessions list d->sessions.push_back(si); } // close file inputFile.close(); } } QHash SessionModel::roleNames() const { // set role names QHash roleNames; roleNames[FileRole] = "file"; roleNames[NameRole] = "name"; roleNames[ExecRole] = "exec"; roleNames[CommentRole] = "comment"; return roleNames; } int SessionModel::rowCount(const QModelIndex &parent) const { Q_UNUSED(parent); return d->sessions.length(); } QVariant SessionModel::data(const QModelIndex &index, int role) const { if (index.row() < 0 || index.row() >= d->sessions.count()) return QVariant(); // get session SessionPtr session = d->sessions[index.row()]; // return correct value if (role == FileRole) return session->file; else if (role == NameRole) return session->name; else if (role == ExecRole) return session->exec; else if (role == CommentRole) return session->comment; // return empty value return QVariant(); } int SessionModel::indexOf(const QString &sessionId) const { for (int i = 0; i < d->sessions.length(); i++) { if (d->sessions[i]->file == sessionId) { return i; } } return 0; } diff --git a/src/sessionmodel.h b/src/sessionmodel.h index e307d37..9256a76 100644 --- a/src/sessionmodel.h +++ b/src/sessionmodel.h @@ -1,63 +1,61 @@ /*************************************************************************** * Copyright (c) 2013 Abdurrahman AVCI * * 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 SDDM_SESSIONMODEL_H #define SDDM_SESSIONMODEL_H #include - #include class SessionModelPrivate; class SessionModel : public QAbstractListModel { Q_OBJECT Q_DISABLE_COPY(SessionModel) enum SessionType { SessionTypeX, SessionTypeWayland }; public: enum SessionRole { NameRole = Qt::DisplayRole, FileRole = Qt::UserRole, ExecRole, CommentRole }; explicit SessionModel(QObject *parent = nullptr); ~SessionModel() Q_DECL_OVERRIDE; void loadDir(const QString &path, SessionType type); QHash roleNames() const override; int rowCount(const QModelIndex &parent = QModelIndex()) const override; QVariant data(const QModelIndex &index, int role = Qt::DisplayRole) const override; int indexOf(const QString& sessionId) const; private: SessionModelPrivate *d { nullptr }; }; #endif // SDDM_SESSIONMODEL_H diff --git a/src/themeconfig.cpp b/src/themeconfig.cpp index d50cc01..83772ab 100644 --- a/src/themeconfig.cpp +++ b/src/themeconfig.cpp @@ -1,241 +1,240 @@ /* Copyright 2013 by Reza Fatahilah Shah 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, see . */ +#include "config.h" #include "themeconfig.h" #include "ui_themeconfig.h" #include "themesmodel.h" #include "themesdelegate.h" +#include #include +#include +#include #include #include #include -#include #include -#include -#include -#include -#include -#include #include #include #include - -#include "config.h" +#include +#include +#include ThemeConfig::ThemeConfig(const KSharedConfigPtr &config, QWidget *parent) : QWidget(parent), mConfig(config) { configUi = new Ui::ThemeConfig(); configUi->setupUi(this); configUi->messageWidget->setVisible(false); ThemesModel *model = new ThemesModel(this); configUi->themesListView->setModel(model); ThemesDelegate *delegate = new ThemesDelegate(configUi->themesListView); delegate->setPreviewSize(QSize(128,128)); configUi->themesListView->setItemDelegate(delegate); model->populate(); connect(this, &ThemeConfig::themesChanged, model, &ThemesModel::populate); connect(configUi->themesListView, &QAbstractItemView::activated, this, &ThemeConfig::themeSelected); connect(configUi->themesListView, &QAbstractItemView::clicked, this, &ThemeConfig::themeSelected); connect(configUi->selectBackgroundButton, &SelectImageButton::imagePathChanged, this, &ThemeConfig::backgroundChanged); connect(configUi->getNewButton, &QPushButton::clicked, this, &ThemeConfig::getNewStuffClicked); connect(configUi->installFromFileButton, &QPushButton::clicked, this, &ThemeConfig::installFromFileClicked); connect(configUi->removeThemeButton, &QPushButton::clicked, this, &ThemeConfig::removeThemeClicked); prepareInitialTheme(); } ThemeConfig::~ThemeConfig() { delete configUi; } QVariantMap ThemeConfig::save() { QModelIndex index = configUi->themesListView->currentIndex(); if (!index.isValid()) { return QVariantMap(); } QVariantMap args; args[QStringLiteral("kde_settings.conf/Theme/Current")] = index.data(ThemesModel::IdRole); if (!mThemeConfigPath.isEmpty()) { if (!mBackgroundPath.isEmpty()) { args[QStringLiteral("theme.conf.user/General/background")] = mBackgroundPath; args[QStringLiteral("theme.conf.user/General/type")] = QStringLiteral("image"); } else { args[QStringLiteral("theme.conf.user/General/type")] = QStringLiteral("color"); } } return args; } QString ThemeConfig::themeConfigPath() const { return mThemeConfigPath; } void ThemeConfig::prepareInitialTheme() { const QString initialTheme = mConfig->group("Theme").readEntry("Current"); QModelIndex index = findThemeIndex(initialTheme); if (!index.isValid() && configUi->themesListView->model()->rowCount() > 0) { //if we can't find the currently configured theme from the config, just select the first theme in the UI index = configUi->themesListView->model()->index(0,0); } configUi->themesListView->setCurrentIndex(index); themeSelected(index); } QModelIndex ThemeConfig::findThemeIndex(const QString &id) const { QAbstractItemModel* model = configUi->themesListView->model(); for (int i=0; i < model->rowCount(); i++) { QModelIndex index = model->index(i, 0); if (index.data(ThemesModel::IdRole).toString() == id) { return index; } } return QModelIndex(); } void ThemeConfig::themeSelected(const QModelIndex &index) { if (!configUi->quickWidget->source().isValid()) { const QString mainQmlPath = QStandardPaths::locate(QStandardPaths::GenericDataLocation, QStringLiteral("sddm-kcm/main.qml")); configUi->quickWidget->setSource(QUrl::fromLocalFile(mainQmlPath)); } QString themePath = index.data(ThemesModel::PathRole).toString(); QString previewFilename = themePath + index.data(ThemesModel::PreviewRole).toString(); configUi->quickWidget->rootObject()->setProperty("themeName", index.data().toString()); configUi->quickWidget->rootObject()->setProperty("previewPath", previewFilename); configUi->quickWidget->rootObject()->setProperty("authorName", index.data(ThemesModel::AuthorRole).toString()); configUi->quickWidget->rootObject()->setProperty("description", index.data(ThemesModel::DescriptionRole).toString()); configUi->quickWidget->rootObject()->setProperty("license", index.data(ThemesModel::LicenseRole).toString()); configUi->quickWidget->rootObject()->setProperty("email", index.data(ThemesModel::EmailRole).toString()); configUi->quickWidget->rootObject()->setProperty("website", index.data(ThemesModel::WebsiteRole).toString()); configUi->quickWidget->rootObject()->setProperty("copyright", index.data(ThemesModel::CopyrightRole).toString()); configUi->quickWidget->rootObject()->setProperty("version", index.data(ThemesModel::VersionRole).toString()); //Check if we need to display configuration group prepareConfigurationUi(index); emit changed(true); } void ThemeConfig::backgroundChanged(const QString &imagePath) { mBackgroundPath = imagePath; emit changed(true); } void ThemeConfig::prepareConfigurationUi(const QModelIndex &index) { const QString themePath = index.data(ThemesModel::PathRole).toString(); const QString configPath = themePath + index.data(ThemesModel::ConfigFileRole).toString(); mThemeConfigPath = configPath; QFile configFile(configPath); if (configFile.exists()) { KSharedConfigPtr themeConfig = KSharedConfig::openConfig(configFile.fileName() + QStringLiteral(".user"), KConfig::CascadeConfig); themeConfig->addConfigSources({configFile.fileName()}); configUi->customizeBox->setVisible(true); configUi->selectBackgroundButton->setImagePath(themePath + themeConfig->group("General").readEntry("background")); } else { configUi->customizeBox->setVisible(false); } } void ThemeConfig::dump() { //dump sddm conf KConfigGroup config = mConfig->group("General"); qDebug() << "Current theme:" << config.readEntry("CurrentTheme"); } void ThemeConfig::getNewStuffClicked() { QPointer dialog(new KNS3::DownloadDialog(QStringLiteral("sddmtheme.knsrc"), this)); dialog->setWindowTitle(i18n("Download New SDDM Themes")); if (dialog->exec()) { emit themesChanged(); } delete dialog.data(); } void ThemeConfig::installFromFileClicked() { QPointer dialog(new QFileDialog(this)); dialog->exec(); QStringList files = dialog->selectedFiles(); if (files.count() == 1) { QString file = files.first(); KAuth::Action saveAction(QStringLiteral("org.kde.kcontrol.kcmsddm.installtheme")); saveAction.setHelperId(QStringLiteral("org.kde.kcontrol.kcmsddm")); saveAction.addArgument(QStringLiteral("filePath"), file); auto job = saveAction.execute(); if (!job->exec()) { configUi->messageWidget->setMessageType(KMessageWidget::Warning); configUi->messageWidget->setText(job->errorString()); configUi->messageWidget->animatedShow(); } else { emit themesChanged(); } } delete dialog.data(); } void ThemeConfig::removeThemeClicked() { if (!configUi->themesListView->currentIndex().isValid()) { return; } const QString path = configUi->themesListView->currentIndex().data(ThemesModel::PathRole).toString(); KAuth::Action saveAction(QStringLiteral("org.kde.kcontrol.kcmsddm.uninstalltheme")); saveAction.setHelperId(QStringLiteral("org.kde.kcontrol.kcmsddm")); saveAction.addArgument(QStringLiteral("filePath"), path); auto job = saveAction.execute(); if (!job->exec()) { configUi->messageWidget->setMessageType(KMessageWidget::Warning); configUi->messageWidget->setText(job->errorString()); configUi->messageWidget->animatedShow(); } else { emit themesChanged(); } } diff --git a/src/thememetadata.cpp b/src/thememetadata.cpp index 666e770..909eae3 100644 --- a/src/thememetadata.cpp +++ b/src/thememetadata.cpp @@ -1,163 +1,163 @@ /* Copyright 2013 by Reza Fatahilah Shah Copyright 2014 by David Edmundson 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, see . */ #include "thememetadata.h" #include #include -#include #include +#include class ThemeMetadataPrivate : public QSharedData { public: QString themeid; QString name; QString description; QString author; QString email; QString version; QString website; QString license; QString themeapi; QString mainscript; QString screenshot; QString copyright; QString path; QString configfile; }; ThemeMetadata::ThemeMetadata(const QString &id, const QString &path) : d(new ThemeMetadataPrivate) { d->path = path + QLatin1Char('/'); read(d->path + QStringLiteral("metadata.desktop")); d->themeid = id; } ThemeMetadata::ThemeMetadata(const ThemeMetadata &other) : d(other.d) { } ThemeMetadata& ThemeMetadata::operator=( const ThemeMetadata &other ) { if ( this != &other ) d = other.d; return *this; } ThemeMetadata::~ThemeMetadata() { } void ThemeMetadata::read(const QString &filename) { if (filename.isEmpty()) { return; } QSharedPointer configFile = QSharedPointer(new KConfig(filename, KConfig::SimpleConfig)); KConfigGroup config = configFile->group("SddmGreeterTheme"); //d->themeid = config.readEntry("Theme-Id"); d->name = config.readEntry("Name"); d->description = config.readEntry("Description"); d->author = config.readEntry("Author"); d->email = config.readEntry("Email"); d->version = config.readEntry("Version"); d->website = config.readEntry("Website"); d->license = config.readEntry("License"); d->themeapi = config.readEntry("Theme-API"); d->mainscript = config.readEntry("MainScript"); d->screenshot = config.readEntry("Screenshot"); d->copyright = config.readEntry("Copyright"); d->configfile = config.readEntry("ConfigFile"); } QString ThemeMetadata::path() const { return d->path; } QString ThemeMetadata::themeid() const { return d->themeid; } QString ThemeMetadata::name() const { return d->name; } QString ThemeMetadata::description() const { return d->description; } QString ThemeMetadata::author() const { return d->author; } QString ThemeMetadata::version() const { return d->version; } QString ThemeMetadata::email() const { return d->email; } QString ThemeMetadata::website() const { return d->website; } QString ThemeMetadata::license() const { return d->license; } QString ThemeMetadata::themeapi() const { return d->themeapi; } QString ThemeMetadata::mainscript() const { return d->mainscript; } QString ThemeMetadata::screenshot() const { return d->screenshot; } QString ThemeMetadata::copyright() const { return d->copyright; } QString ThemeMetadata::configfile() const { return d->configfile; } diff --git a/src/thememetadata.h b/src/thememetadata.h index 51cb28a..bba3fd7 100644 --- a/src/thememetadata.h +++ b/src/thememetadata.h @@ -1,56 +1,56 @@ /* Copyright 2013 by Reza Fatahilah Shah Copyright 2014 by David Edmundson 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, see . */ #ifndef THEMEMETADATA_H #define THEMEMETADATA_H -#include #include +#include class ThemeMetadataPrivate; class ThemeMetadata { public: explicit ThemeMetadata(const QString &id, const QString &path = QString()); ThemeMetadata(const ThemeMetadata &other); ThemeMetadata& operator=(const ThemeMetadata& other); ~ThemeMetadata(); QString path() const; QString name() const; QString description() const; QString author() const; QString email() const; QString version() const; QString website() const; QString license() const; QString themeapi() const; QString screenshot() const; QString mainscript() const; QString copyright() const; QString themeid() const; QString configfile() const; private: void read(const QString &filename); private: QSharedDataPointer d; }; #endif //THEMEMETADATA_H diff --git a/src/themesdelegate.cpp b/src/themesdelegate.cpp index 7353090..46f0973 100644 --- a/src/themesdelegate.cpp +++ b/src/themesdelegate.cpp @@ -1,158 +1,157 @@ /* Copyright (c) 2007 Paolo Capriotti Copyright (c) 2010 Dario Andres Rodriguez Copyright 2013 by Reza Fatahilah Shah 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, see . */ #include "themesdelegate.h" +#include "themesmodel.h" #include +#include +#include #include #include #include -#include -#include #include -#include "themesmodel.h" - static const int BLUR_PAD = 6; ThemesDelegate::ThemesDelegate(QObject *parent) : QAbstractItemDelegate(parent) { m_maxHeight = SCREENSHOT_SIZE/1.6 + BLUR_INCREMENT; m_maxWidth = SCREENSHOT_SIZE + BLUR_INCREMENT; } void ThemesDelegate::setPreviewSize(const QSize &size) { mSize = size; } void ThemesDelegate::paint(QPainter *painter, const QStyleOptionViewItem &option, const QModelIndex &index) const { const QAbstractItemModel *model = index.model(); QString previewFilename = model->data(index, ThemesModel::PathRole).toString(); previewFilename += model->data(index, ThemesModel::PreviewRole).toString(); const QString title = model->data(index, Qt::DisplayRole).toString(); const QString author = model->data(index, ThemesModel::AuthorRole).toString(); const QString website = model->data(index, ThemesModel::WebsiteRole).toString(); QPixmap originalPix(previewFilename); QColor color = option.palette.color(QPalette::Base); if (originalPix.isNull()) { // paint a placeholder pixmap originalPix = QPixmap(m_maxWidth, m_maxHeight); originalPix.fill(color); QPainter p_pix(&originalPix); p_pix.drawText(originalPix.rect(), Qt::AlignCenter | Qt::TextWordWrap, i18n("No preview available")); } const QPixmap pix = originalPix.scaled(QSize(ThemesDelegate::SCREENSHOT_SIZE, ThemesDelegate::SCREENSHOT_SIZE/1.6), Qt::KeepAspectRatio, Qt::SmoothTransformation); // Highlight selected item QStyleOptionViewItem opt(option); opt.showDecorationSelected = true; QStyle *style = opt.widget ? opt.widget->style() : QApplication::style(); style->drawPrimitive(QStyle::PE_PanelItemViewItem, &opt, painter, opt.widget); // Draw wallpaper thumbnail QImage blur(pix.size() + QSize(BLUR_INCREMENT + BLUR_PAD, BLUR_INCREMENT + BLUR_PAD), QImage::Format_ARGB32); QRect blurRect = QRect(QPoint((blur.width() - pix.width()) / 2, (blur.height() - pix.height()) / 2), pix.size()); blur.fill(Qt::transparent); QPainter p(&blur); bool darkBaseColor = qGray(color.rgb()) < 192; p.fillRect(blurRect, darkBaseColor ? Qt::white : Qt::black); p.end(); // apply blur with a radius of 2 as thumbnail shadow // Plasma::PaintUtils::shadowBlur(blur, 2, darkBaseColor ? Qt::white : Qt::black); // calculate point const int bx = (option.rect.width() - blur.width()) / 2; const int by = MARGIN + qMax(0, m_maxHeight - blur.height()); QRect shadowRect = QRect(option.rect.topLeft(), blur.size()).translated(bx, by); // draw the blur painter->drawImage(shadowRect.topLeft(), blur); // draw the actual thumbnail painter->drawPixmap(QRect(shadowRect.topLeft() + QPoint((shadowRect.width() - pix.width()) / 2, (shadowRect.height() - pix.height()) / 2), pix.size()), pix); //Use a QTextDocument to layout the text QTextDocument document; QString html = QStringLiteral("%1").arg(title); //Set the text color according to the item state QPalette::ColorGroup cg = QPalette::Active; if (!(option.state & QStyle::State_Enabled)) { cg = QPalette::Disabled; } else if (!(option.state & QStyle::State_Active)) { cg = QPalette::Inactive; } if (option.state & QStyle::State_Selected) { color = QApplication::palette().brush(cg, QPalette::HighlightedText).color(); } else { color = QApplication::palette().brush(cg, QPalette::Text).color(); } html = QStringLiteral("
%2
").arg(color.name()).arg(html); document.setHtml(html); //Calculate positioning int x = option.rect.left() + MARGIN; //Enable word-wrap document.setTextWidth(m_maxWidth); //Center text on the row int y = option.rect.top() + m_maxHeight + MARGIN; //qMax(0 ,(int)((option.rect.height() - document.size().height()) / 2)); //Draw text painter->save(); painter->translate(x, y); document.drawContents(painter, QRect(QPoint(0, 0), option.rect.size() - QSize(0, m_maxHeight + MARGIN))); painter->restore(); } QSize ThemesDelegate::sizeHint(const QStyleOptionViewItem &option, const QModelIndex &index) const { Q_UNUSED(option) const QAbstractItemModel *model = index.model(); const QString title = model->data(index, Qt::DisplayRole).toString(); const QString author = model->data(index, ThemesModel::AuthorRole).toString(); const QString website = model->data(index, ThemesModel::WebsiteRole).toString(); //Generate a sample complete entry (with the real title) to calculate sizes QTextDocument document; const QString html = QStringLiteral("%1
").arg(title); document.setHtml(html); document.setTextWidth(m_maxWidth); QSize s(m_maxWidth + MARGIN * 2, m_maxHeight + MARGIN + (int)(document.size().height())); return s; } diff --git a/src/themesmodel.cpp b/src/themesmodel.cpp index 2c6cef4..968d328 100644 --- a/src/themesmodel.cpp +++ b/src/themesmodel.cpp @@ -1,138 +1,135 @@ /* Copyright 2013 by Reza Fatahilah Shah 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, see . */ - - +#include "config.h" +#include "thememetadata.h" #include "themesmodel.h" #include +#include #include #include #include #include #include -#include - -#include "config.h" -#include "thememetadata.h" ThemesModel::ThemesModel(QObject *parent) : QAbstractListModel(parent) { } ThemesModel::~ThemesModel() { } int ThemesModel::rowCount(const QModelIndex &parent) const { Q_UNUSED(parent); return mThemeList.size(); } QVariant ThemesModel::data(const QModelIndex &index, int role) const { const ThemeMetadata metadata = mThemeList[index.row()]; switch(role) { case Qt::DisplayRole: return metadata.name(); case ThemesModel::IdRole: return metadata.themeid(); case ThemesModel::AuthorRole: return metadata.author(); case ThemesModel::DescriptionRole: return metadata.description(); case ThemesModel::LicenseRole: return metadata.license(); case ThemesModel::EmailRole: return metadata.email(); case ThemesModel::WebsiteRole: return metadata.website(); case ThemesModel::CopyrightRole: return metadata.copyright(); case ThemesModel::VersionRole: return metadata.version(); case ThemesModel::ThemeApiRole: return metadata.themeapi(); case ThemesModel::PreviewRole: return metadata.screenshot(); case ThemesModel::PathRole: return metadata.path(); case ThemesModel::ConfigFileRole: return metadata.configfile(); } return QVariant(); } void ThemesModel::populate() { if (!mThemeList.isEmpty()) { beginResetModel(); mThemeList.clear(); endResetModel(); } QString themesBaseDir = KSharedConfig::openConfig(QStringLiteral(SDDM_CONFIG_FILE), KConfig::SimpleConfig)->group("Theme").readEntry("ThemeDir"); if (themesBaseDir.isEmpty()) { themesBaseDir = QStandardPaths::locate(QStandardPaths::GenericDataLocation, QStringLiteral("sddm/themes"), QStandardPaths::LocateDirectory); } QDir dir(themesBaseDir); if (!dir.exists()) { return; } foreach (const QString &theme, dir.entryList(QDir::AllDirs | QDir::Readable)) { QString path = themesBaseDir + QLatin1Char('/') + theme; if (QFile::exists(path + QStringLiteral("/metadata.desktop") )) { add(theme, path); } } } void ThemesModel::add(const QString &id, const QString &path) { beginInsertRows(QModelIndex(), mThemeList.count(), mThemeList.count()); mThemeList.append( ThemeMetadata(id, path) ); endInsertRows(); } void ThemesModel::dump(const QString &id, const QString &path) { Q_UNUSED(id) ThemeMetadata metadata(path); qDebug() << "Theme Path:" << metadata.path(); qDebug() << "Name: " << metadata.name(); qDebug() << "Version: " << metadata.version(); qDebug() << "Author: " << metadata.author(); qDebug() << "Description: " << metadata.description(); qDebug() << "Email: " << metadata.email(); qDebug() << "License: " << metadata.license(); qDebug() << "Copyright: " << metadata.copyright(); qDebug() << "Screenshot: " << metadata.screenshot(); } diff --git a/src/usersmodel.cpp b/src/usersmodel.cpp index e2d6855..0e32ca3 100644 --- a/src/usersmodel.cpp +++ b/src/usersmodel.cpp @@ -1,99 +1,98 @@ /* Copyright 2013 by Reza Fatahilah Shah 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, see . */ - #include "usersmodel.h" -#include #include +#include UsersModel::UsersModel(QObject *parent) : QAbstractListModel(parent) { } UsersModel::~UsersModel() { } int UsersModel::rowCount(const QModelIndex &parent) const { Q_UNUSED(parent) return mUserList.size(); } QVariant UsersModel::data(const QModelIndex &index, int role) const { auto row = index.row(); if (row < 0 || row >= mUserList.count()) { return QVariant(); } const KUser &user = mUserList[row]; switch(role) { case Qt::DisplayRole: return user.loginName(); } return QVariant(); } void UsersModel::add(const KUser &user) { beginInsertRows(QModelIndex(), mUserList.count(), mUserList.count()); mUserList.append( KUser(user) ); endInsertRows(); } void UsersModel::populate(const uint minimumUid, const uint maximumUid) { mUserList.clear(); QList< KUser > userList = KUser::allUsers(); KUser user; foreach( user, userList ) { K_UID uuid = user.userId().nativeId(); // invalid user if (uuid == (uid_t) -1) { continue; } if (uuid >= minimumUid && uuid <= maximumUid) { add(user); } /*qDebug() << user.loginName() << ",uid" << uuid; qDebug() << " home:" << user.homeDir(); qDebug() << " isSuperUser:" << user.isSuperUser() << ",isValid:" << user.isValid(); qDebug() << " faceIconPath:" << user.faceIconPath();*/ } } int UsersModel::indexOf(const QString &user) { if (user.isEmpty()) return 0; // find user index for (int i = 0; i < mUserList.size(); ++i) if (mUserList.at(i).loginName() == user) return i; // user not found return 0; }