diff options
author | Ken Moore <ken@ixsystems.com> | 2017-09-15 12:25:54 -0400 |
---|---|---|
committer | Ken Moore <ken@ixsystems.com> | 2017-09-15 12:25:54 -0400 |
commit | 5b1831a22408a22d9c3bc03a82942a4bf092f311 (patch) | |
tree | ab34171d8d1c905ea589b915e8209cfee0b5844e | |
parent | Make sure the "leave" dialog is themeable, and theme it within the DarkGlass ... (diff) | |
download | lumina-5b1831a22408a22d9c3bc03a82942a4bf092f311.tar.gz lumina-5b1831a22408a22d9c3bc03a82942a4bf092f311.tar.bz2 lumina-5b1831a22408a22d9c3bc03a82942a4bf092f311.zip |
Add mouse cursor theme management to lthemeengine.
Also fix up a number of other little bugs in various pages.
9 files changed, 375 insertions, 7 deletions
diff --git a/src-qt5/core/lumina-theme-engine/src/lthemeengine/cursorthemepage.cpp b/src-qt5/core/lumina-theme-engine/src/lthemeengine/cursorthemepage.cpp new file mode 100644 index 00000000..1d84164f --- /dev/null +++ b/src-qt5/core/lumina-theme-engine/src/lthemeengine/cursorthemepage.cpp @@ -0,0 +1,139 @@ +#include <QSettings> +#include <QFileInfo> +#include <QFileInfoList> +#include <QDir> +#include <QTreeWidgetItem> +#include <QImageReader> +#include <QLocale> +#include "lthemeengine.h" +#include "cursorthemepage.h" +#include "ui_cursorthemepage.h" + +CursorThemePage::CursorThemePage(QWidget *parent) : TabPage(parent), m_ui(new Ui::CursorThemePage){ + m_ui->setupUi(this); + loadThemes(); + readSettings(); +} + +CursorThemePage::~CursorThemePage(){ + delete m_ui; +} + +void CursorThemePage::writeSettings(){ + QSettings settings(lthemeengine::configFile(), QSettings::IniFormat); + QTreeWidgetItem *item = m_ui->treeWidget->currentItem(); + if(item){ + settings.setValue("Appearance/cursor_theme", item->data(3, Qt::UserRole)); + lthemeengine::setCursorTheme(item->data(3, Qt::UserRole).toString() ); + } +} + +void CursorThemePage::readSettings(){ + QSettings settings(lthemeengine::configFile(), QSettings::IniFormat); + QString name = settings.value("Appearance/cursor_theme").toString(); + if(name.isEmpty()){ return; } + for(int i = 0; i < m_ui->treeWidget->topLevelItemCount(); ++i){ + QTreeWidgetItem *item = m_ui->treeWidget->topLevelItem(i); + if(item->data(3, Qt::UserRole).toString() == name){ + m_ui->treeWidget->setCurrentItem(item); + break; + } + } +} + +void CursorThemePage::loadThemes(){ + /*QFileInfoList themeFileList; + foreach(QString path, lthemeengine::iconPaths()){ + QDir dir(path); + dir.setFilter(QDir::Dirs | QDir::NoDotDot | QDir::NoDot); + foreach (QFileInfo info, dir.entryInfoList()){ + QDir themeDir(info.absoluteFilePath()); + themeDir.setFilter(QDir::Files); + themeFileList << themeDir.entryInfoList(QStringList() << "index.theme"); + } + }*/ + /*foreach(QFileInfo info, themeFileList){ + loadTheme(info.canonicalFilePath()); + }*/ + QStringList themes = lthemeengine::availableSystemCursors(); + for(int i=0; i<themes.length(); i++){ loadTheme(themes[i]); } +} + +void CursorThemePage::loadTheme(const QString &path){ + //QSettings config(path, QSettings::IniFormat); + //config.setIniCodec("UTF-8"); + //config.beginGroup("Icon Theme"); + //QStringList dirs = config.value("Directories").toStringList(); + //if(dirs.isEmpty() || config.value("Hidden", false).toBool()){ return; } + QString name, comment; + name = comment = path.section("/",-1); + /*QString lang = QLocale::system().name(); + name = config.value(QString("Name[%1]").arg(lang)).toString(); + comment = config.value(QString("Comment[%1]").arg(lang)).toString(); + if(lang.contains("_")){ lang = lang.split("_").first(); } + if(name.isEmpty()){ name = config.value(QString("Name[%1]").arg(lang)).toString(); } + if(comment.isEmpty()){ comment = config.value(QString("Comment[%1]").arg(lang)).toString(); } + if(name.isEmpty()){ name = config.value("Name").toString(); } + if(comment.isEmpty()){ comment = config.value("Comment").toString(); } + config.endGroup();*/ + QIcon icon1;// = findIcon(path, 24, "document-save"); + QIcon icon2;//= findIcon(path, 24, "document-print"); + QIcon icon3;// = findIcon(path, 24, "media-playback-stop"); + QTreeWidgetItem *item = new QTreeWidgetItem(); + item->setIcon(0, icon1); + item->setIcon(1, icon2); + item->setIcon(2, icon3); + item->setText(3, name); + item->setData(3, Qt::UserRole, path.section("/",-1)); + item->setToolTip(3, comment); + item->setSizeHint(0, QSize(24,24)); + m_ui->treeWidget->addTopLevelItem(item); + m_ui->treeWidget->resizeColumnToContents(0); + m_ui->treeWidget->resizeColumnToContents(1); + m_ui->treeWidget->resizeColumnToContents(2); + m_ui->treeWidget->resizeColumnToContents(3); +} + +QIcon CursorThemePage::findIcon(const QString &themePath, int size, const QString &name){ + QSettings config(themePath, QSettings::IniFormat); + config.beginGroup("Icon Theme"); + QStringList dirs = config.value("Directories").toStringList(); + QStringList parents = config.value("Inherits").toStringList(); + bool haveInherits = config.contains("Inherits"); + config.endGroup(); + foreach (QString dir, dirs){ + config.beginGroup(dir); + if(config.value("Size").toInt() == size){ + QDir iconDir = QFileInfo(themePath).path() + "/" + dir; + iconDir.setFilter(QDir::Files); + iconDir.setNameFilters(QStringList () << name + ".*"); + if(iconDir.entryInfoList().isEmpty()) + continue; + return QIcon(iconDir.entryInfoList().first().absoluteFilePath()); + } + config.endGroup(); + } + foreach (QString dir, dirs){ + config.beginGroup(dir); + if(abs(config.value("Size").toInt() - size) < 4){ + QDir iconDir = QFileInfo(themePath).path() + "/" + dir; + iconDir.setFilter(QDir::Files); + iconDir.setNameFilters(QStringList () << name + ".*"); + if(iconDir.entryInfoList().isEmpty()) + continue; + return QIcon(iconDir.entryInfoList().first().absoluteFilePath()); + } + config.endGroup(); + } + if (!haveInherits){ return QIcon(); } + parents.append("hicolor"); //add fallback themes + parents.append("gnome"); + parents.removeDuplicates(); + foreach (QString parent, parents){ + QString parentThemePath = QDir(QFileInfo(themePath).path() + "/../" + parent).canonicalPath() + "/index.theme"; + if(!QFile::exists(parentThemePath) || parentThemePath == themePath){ continue; } + QIcon icon = findIcon(parentThemePath, size, name); + if(!icon.isNull()){ return icon; } + } + return QIcon(); +} diff --git a/src-qt5/core/lumina-theme-engine/src/lthemeengine/cursorthemepage.h b/src-qt5/core/lumina-theme-engine/src/lthemeengine/cursorthemepage.h new file mode 100644 index 00000000..e221520b --- /dev/null +++ b/src-qt5/core/lumina-theme-engine/src/lthemeengine/cursorthemepage.h @@ -0,0 +1,29 @@ +#ifndef CURSORTHEMEPAGE_H +#define CURSORTHEMEPAGE_H + +#include <QIcon> +#include "tabpage.h" + +namespace Ui { +class CursorThemePage; +} + +class CursorThemePage : public TabPage +{ + Q_OBJECT + +public: + explicit CursorThemePage(QWidget *parent = 0); + ~CursorThemePage(); + + void writeSettings(); + +private: + void readSettings(); + void loadThemes(); + void loadTheme(const QString &path); + QIcon findIcon(const QString &themePath, int size, const QString &name); + Ui::CursorThemePage *m_ui; +}; + +#endif // ICONTHEMEPAGE_H diff --git a/src-qt5/core/lumina-theme-engine/src/lthemeengine/cursorthemepage.ui b/src-qt5/core/lumina-theme-engine/src/lthemeengine/cursorthemepage.ui new file mode 100644 index 00000000..3e0a5da1 --- /dev/null +++ b/src-qt5/core/lumina-theme-engine/src/lthemeengine/cursorthemepage.ui @@ -0,0 +1,51 @@ +<?xml version="1.0" encoding="UTF-8"?> +<ui version="4.0"> + <class>CursorThemePage</class> + <widget class="QWidget" name="CursorThemePage"> + <property name="geometry"> + <rect> + <x>0</x> + <y>0</y> + <width>400</width> + <height>300</height> + </rect> + </property> + <property name="windowTitle"> + <string notr="true">Form</string> + </property> + <layout class="QVBoxLayout" name="verticalLayout"> + <item> + <widget class="QTreeWidget" name="treeWidget"> + <property name="columnCount"> + <number>4</number> + </property> + <attribute name="headerVisible"> + <bool>false</bool> + </attribute> + <column> + <property name="text"> + <string notr="true">1</string> + </property> + </column> + <column> + <property name="text"> + <string notr="true">2</string> + </property> + </column> + <column> + <property name="text"> + <string notr="true">3</string> + </property> + </column> + <column> + <property name="text"> + <string notr="true">4</string> + </property> + </column> + </widget> + </item> + </layout> + </widget> + <resources/> + <connections/> +</ui> diff --git a/src-qt5/core/lumina-theme-engine/src/lthemeengine/iconthemepage.cpp b/src-qt5/core/lumina-theme-engine/src/lthemeengine/iconthemepage.cpp index 1a09ac0d..7ed54f02 100644 --- a/src-qt5/core/lumina-theme-engine/src/lthemeengine/iconthemepage.cpp +++ b/src-qt5/core/lumina-theme-engine/src/lthemeengine/iconthemepage.cpp @@ -72,7 +72,7 @@ void IconThemePage::loadTheme(const QString &path){ config.endGroup(); QIcon icon1 = findIcon(path, 24, "document-save"); QIcon icon2 = findIcon(path, 24, "document-print"); - QIcon icon3 = findIcon(path, 24, "media-playback-stop"); + QIcon icon3 = findIcon(path, 24, "document-edit"); QTreeWidgetItem *item = new QTreeWidgetItem(); item->setIcon(0, icon1); item->setIcon(1, icon2); @@ -97,7 +97,7 @@ QIcon IconThemePage::findIcon(const QString &themePath, int size, const QString config.endGroup(); foreach (QString dir, dirs){ config.beginGroup(dir); - if(config.value("Size").toInt() == size){ + if(config.value("Size").toInt() == size || config.value("Type").toString().toLower()=="scalable"){ QDir iconDir = QFileInfo(themePath).path() + "/" + dir; iconDir.setFilter(QDir::Files); iconDir.setNameFilters(QStringList () << name + ".*"); diff --git a/src-qt5/core/lumina-theme-engine/src/lthemeengine/lthemeengine.cpp b/src-qt5/core/lumina-theme-engine/src/lthemeengine/lthemeengine.cpp index 701745c4..cb491aa6 100644 --- a/src-qt5/core/lumina-theme-engine/src/lthemeengine/lthemeengine.cpp +++ b/src-qt5/core/lumina-theme-engine/src/lthemeengine/lthemeengine.cpp @@ -8,6 +8,19 @@ #include <QDebug> +QStringList lthemeengine::readFile(QString filepath){ + QStringList out; + QFile file(filepath); + if(file.open(QIODevice::Text | QIODevice::ReadOnly)){ + QTextStream in(&file); + while(!in.atEnd()){ + out << in.readLine(); + } + file.close(); + } + return out; +} + QString lthemeengine::configPath(){ return QDir::homePath() + "/.config/lthemeengine/"; } @@ -91,3 +104,96 @@ QString lthemeengine::systemLanguageID(){ #endif return QLocale::system().name(); } + +QStringList lthemeengine::availableSystemCursors(){ //returns: [name] for each item + //Find all the directories which could contain themes + QStringList paths; + paths << QDir::homePath()+"/.icons"; + QStringList xdd = QString(getenv("XDG_DATA_HOME")).split(":"); + xdd << QString(getenv("XDG_DATA_DIRS")).split(":"); + for(int i=0; i<xdd.length(); i++){ + if(QFile::exists(xdd[i]+"/icons")){ + paths << xdd[i]+"/icons"; + } + } + //Now get all the icon themes in these directories + QStringList themes, tmpthemes; + QDir dir; + for(int i=0; i<paths.length(); i++){ + if(dir.cd(paths[i])){ + tmpthemes = dir.entryList(QDir::Dirs | QDir::NoDotAndDotDot, QDir::Name); + for(int j=0; j<tmpthemes.length(); j++){ + if(tmpthemes[j].startsWith("default")){ continue; } + if( QFile::exists(dir.absoluteFilePath(tmpthemes[j]+"/cursors")) ){ themes << tmpthemes[j]; } + } + } + } + //Clean up the list and return + themes.removeDuplicates(); + themes.sort(); + return themes; +} + +//Return the currently-selected Cursor theme +QString lthemeengine::currentCursor(){ + //qDebug() << "Reading Current Cursor Theme:"; + QStringList info = readFile(QDir::homePath()+"/.icons/default/index.theme"); + if(info.isEmpty()){ return ""; } + QString cursor; + bool insection = false; + for(int i=0; i<info.length(); i++){ + if(info[i]=="[Icon Theme]"){ insection = true; continue;} + else if(insection && info[i].startsWith("Inherits=")){ + cursor = info[i].section("=",1,1).simplified(); + break; + } + } + //qDebug() << " - found theme:" << cursor; + return cursor; +} + +//Change the current Cursor Theme +bool lthemeengine::setCursorTheme(QString cursorname){ +//qDebug() << "Set Cursor Theme:" << cursorname; + if(cursorname=="default"){ + //special case - this will cause a recursive inheritance loop - just remove the file instead + if(QFile::exists(QDir::homePath()+"/.icons/default/index.theme")){ + return QFile::remove(QDir::homePath()+"/.icons/default/index.theme"); + } + return true; //already does not exist + } + QStringList info = readFile(QDir::homePath()+"/.icons/default/index.theme"); + bool insection = false; + bool changed = false; + QString newval = "Inherits="+cursorname; + for(int i=0; i<info.length() && !changed; i++){ + if(info[i]=="[Icon Theme]"){ + insection = true; + }else if( info[i].startsWith("[") && insection){ + //Section does not have the setting - add it + info.insert(i, newval); + changed =true; + }else if( info[i].startsWith("[") ){ + insection = false; + }else if(insection && info[i].startsWith("Inherits=")){ + info[i] = newval; //replace the current setting + changed = true; + } + } //end loop over file contents + if(!changed){ //Could not change the file contents for some reason + if(insection){ info << newval; } //end of file while in the section + else{ info << "[Icon Theme]" << newval; } //entire section missing from file + } + //Now save the file + QFile file(QDir::homePath()+"/.icons/default/index.theme"); + bool ok = false; + if( file.open(QIODevice::WriteOnly | QIODevice::Truncate) ){ + QTextStream out(&file); + out << info.join("\n"); + if(!info.last().isEmpty()){ out << "\n"; } //always end with a new line + file.close(); + ok = true; + } + //qDebug() << "Done saving the cursor:" << info; + return ok; +} diff --git a/src-qt5/core/lumina-theme-engine/src/lthemeengine/lthemeengine.h b/src-qt5/core/lumina-theme-engine/src/lthemeengine/lthemeengine.h index 06dd3e09..c1b6eac6 100644 --- a/src-qt5/core/lumina-theme-engine/src/lthemeengine/lthemeengine.h +++ b/src-qt5/core/lumina-theme-engine/src/lthemeengine/lthemeengine.h @@ -16,6 +16,7 @@ class lthemeengine { public: + static QStringList readFile(QString path); static QString configPath(); static QString configFile(); static QStringList iconPaths(); @@ -26,6 +27,11 @@ public: static QString userColorSchemePath(); static QStringList sharedColorSchemePath(); static QString systemLanguageID(); + //Cursor Theme Management + static QStringList availableSystemCursors(); + static QString currentCursor(); + static bool setCursorTheme(QString); + private: lthemeengine() {} diff --git a/src-qt5/core/lumina-theme-engine/src/lthemeengine/lthemeengine.pro b/src-qt5/core/lumina-theme-engine/src/lthemeengine/lthemeengine.pro index 721b8888..cc949ff9 100644 --- a/src-qt5/core/lumina-theme-engine/src/lthemeengine/lthemeengine.pro +++ b/src-qt5/core/lumina-theme-engine/src/lthemeengine/lthemeengine.pro @@ -16,7 +16,8 @@ SOURCES += \ interfacepage.cpp \ fontconfigdialog.cpp \ qsspage.cpp \ - qsseditordialog.cpp + qsseditordialog.cpp \ + cursorthemepage.cpp FORMS += \ mainwindow.ui \ @@ -28,7 +29,8 @@ FORMS += \ fontconfigdialog.ui \ previewform.ui \ qsspage.ui \ - qsseditordialog.ui + qsseditordialog.ui \ + cursorthemepage.ui HEADERS += \ mainwindow.h \ @@ -41,7 +43,8 @@ HEADERS += \ interfacepage.h \ fontconfigdialog.h \ qsspage.h \ - qsseditordialog.h + qsseditordialog.h \ + cursorthemepage.h DEFINES += USE_WIDGETS diff --git a/src-qt5/core/lumina-theme-engine/src/lthemeengine/mainwindow.cpp b/src-qt5/core/lumina-theme-engine/src/lthemeengine/mainwindow.cpp index d0f1560f..d4544c7b 100644 --- a/src-qt5/core/lumina-theme-engine/src/lthemeengine/mainwindow.cpp +++ b/src-qt5/core/lumina-theme-engine/src/lthemeengine/mainwindow.cpp @@ -8,6 +8,7 @@ #include "appearancepage.h" #include "fontspage.h" #include "iconthemepage.h" +#include "cursorthemepage.h" #include "interfacepage.h" #include "qsspage.h" #include "ui_mainwindow.h" @@ -27,6 +28,7 @@ MainWindow::MainWindow(QWidget *parent) : QWidget(parent), m_ui(new Ui::MainWind bgroup->addButton(m_ui->tool_page_icons, m_ui->stackedWidget->addWidget(new IconThemePage(this))); bgroup->addButton(m_ui->tool_page_styles, m_ui->stackedWidget->addWidget(new QSSPage(this, false))); bgroup->addButton(m_ui->tool_page_deskstyles, m_ui->stackedWidget->addWidget(new QSSPage(this, true))); + bgroup->addButton(m_ui->tool_page_cursors, m_ui->stackedWidget->addWidget(new CursorThemePage(this)) ); connect(bgroup, SIGNAL(buttonClicked(int)), m_ui->stackedWidget, SLOT(setCurrentIndex(int)) ); QTimer::singleShot(10, m_ui->tool_page_general, SLOT(toggle())); diff --git a/src-qt5/core/lumina-theme-engine/src/lthemeengine/mainwindow.ui b/src-qt5/core/lumina-theme-engine/src/lthemeengine/mainwindow.ui index bfaf3eaf..dd676c37 100644 --- a/src-qt5/core/lumina-theme-engine/src/lthemeengine/mainwindow.ui +++ b/src-qt5/core/lumina-theme-engine/src/lthemeengine/mainwindow.ui @@ -6,8 +6,8 @@ <rect> <x>0</x> <y>0</y> - <width>657</width> - <height>500</height> + <width>672</width> + <height>723</height> </rect> </property> <property name="windowTitle"> @@ -159,6 +159,38 @@ </widget> </item> <item> + <widget class="QToolButton" name="tool_page_cursors"> + <property name="sizePolicy"> + <sizepolicy hsizetype="Preferred" vsizetype="Preferred"> + <horstretch>0</horstretch> + <verstretch>0</verstretch> + </sizepolicy> + </property> + <property name="text"> + <string>Cursors</string> + </property> + <property name="icon"> + <iconset theme="preferences-desktop-mouse"> + <normaloff>.</normaloff>.</iconset> + </property> + <property name="iconSize"> + <size> + <width>32</width> + <height>32</height> + </size> + </property> + <property name="checkable"> + <bool>true</bool> + </property> + <property name="toolButtonStyle"> + <enum>Qt::ToolButtonTextUnderIcon</enum> + </property> + <property name="autoRaise"> + <bool>true</bool> + </property> + </widget> + </item> + <item> <widget class="QToolButton" name="tool_page_styles"> <property name="sizePolicy"> <sizepolicy hsizetype="Preferred" vsizetype="Preferred"> |