//=========================================== // Lumina-DE source code // Copyright (c) 2014-2015, Ken Moore // Available under the 3-clause BSD license // See the LICENSE file for full details //=========================================== #include "LuminaThemes.h" #include "LUtils.h" #include "LuminaOS.h" #include #include #include #include #include #include #include "LuminaXDG.h" #include //Stuff necesary for Qt Cursor Reloads //#include "qxcbcursor.h" //needed to prod Qt to refresh the mouse cursor theme //#include QStringList LTHEME::availableSystemThemes(){ //returns: [name::::path] for each item QDir dir(LOS::LuminaShare()+"../lthemeengine/desktop_qss"); QStringList list = dir.entryList(QStringList() <<"*.qss", QDir::Files, QDir::Name); for(int i=0; i::::] list[i] = list[i].section(".qss",0,0)+"::::"+dir.absoluteFilePath(list[i]); } return list; } QStringList LTHEME::availableSystemStyles(){ //returns: [name::::path] for each item QDir dir(LOS::LuminaShare()+"../lthemeengine/qss"); QStringList list = dir.entryList(QStringList() <<"*.qss", QDir::Files, QDir::Name); for(int i=0; i::::] list[i] = list[i].section(".qss",0,0)+"::::"+dir.absoluteFilePath(list[i]); } return list; } QStringList LTHEME::availableLocalThemes(){ //returns: [name::::path] for each item QDir dir( QString(getenv("XDG_CONFIG_HOME"))+"/lthemeengine/desktop_qss"); QStringList list = dir.entryList(QStringList() <<"*.qss", QDir::Files, QDir::Name); for(int i=0; i::::] list[i] = list[i].section(".qss",0,0)+"::::"+dir.absoluteFilePath(list[i]); } return list; } QStringList LTHEME::availableLocalStyles(){ //returns: [name::::path] for each item QDir dir( QString(getenv("XDG_CONFIG_HOME"))+"/lthemeengine/qss"); QStringList list = dir.entryList(QStringList() <<"*.qss", QDir::Files, QDir::Name); for(int i=0; i::::] list[i] = list[i].section(".qss",0,0)+"::::"+dir.absoluteFilePath(list[i]); } return list; } QStringList LTHEME::availableSystemColors(){ //returns: [name::::path] for each item //returns: [name::::path] for each item QDir dir(LOS::LuminaShare()+"../lthemeengine/colors"); QStringList list = dir.entryList(QStringList() <<"*.conf", QDir::Files, QDir::Name); for(int i=0; i::::] list[i] = list[i].section(".conf",0,0)+"::::"+dir.absoluteFilePath(list[i]); } return list; } QStringList LTHEME::availableLocalColors(){ //returns: [name::::path] for each item QDir dir(QString(getenv("XDG_CONFIG_HOME"))+"/lthemeengine/colors"); QStringList list = dir.entryList(QStringList() <<"*.conf", QDir::Files, QDir::Name); for(int i=0; i::::] list[i] = list[i].section(".conf",0,0)+"::::"+dir.absoluteFilePath(list[i]); } return list; } QStringList LTHEME::availableSystemIcons(){ //returns: [name] for each item 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 highest QSettings engineset("lthemeengine","lthemeengine"); engineset.setValue("Interface/stylesheets",paths); engineset.sync(); return true; } //Return the complete stylesheet for a given theme/colors QString LTHEME::assembleStyleSheet(QString themepath, QString colorpath, QString font, QString fontsize){ QString stylesheet = LUtils::readFile(themepath).join("\n"); QStringList colors = LUtils::readFile(colorpath); //qDebug() << "Found Theme:" << themepath << stylesheet; //qDebug() << "Found Colors:" << colorpath << colors; QStringList systhemes = availableSystemThemes(); QStringList locthemes = availableLocalThemes(); //Now do any inheritance between themes int start = stylesheet.indexOf("INHERITS="); while(start>=0){ QString line = stylesheet.mid(start, stylesheet.indexOf("\n",start)-start); //only get this line QString inherit = line.section("=",1,1); QString rStyle; //replacement stylesheet if(!locthemes.filter(inherit+"::::").isEmpty()){ rStyle = LUtils::readFile(locthemes.filter(inherit+"::::").first().section("::::",1,1)).join("\n"); }else if(!systhemes.filter(inherit+"::::").isEmpty()){ rStyle = LUtils::readFile(systhemes.filter(inherit+"::::").first().section("::::",1,1)).join("\n"); } stylesheet.replace(line, rStyle); //Now look for the next one start = stylesheet.indexOf("INHERITS="); } //Now perform the color replacements for(int i=0; i=0; i--){ if(info[i].startsWith(var+"=")){ return info[i].section("=",1,100).simplified(); } } //If it gets here, no setting found for that variable return ""; } // ========================= // LuminaThemeStyle // ========================= /*LuminaThemeStyle::LuminaThemeStyle() : QProxyStyle(){ this->update(); } LuminaThemeStyle::~LuminaThemeStyle(){ } //Function to update the style (for use by the theme engine) void LuminaThemeStyle::update(){ darkfont = true; //make this dynamic later }*/ //Subclassed functions //void LuminaThemeStyle::drawItemText(QPainter *painter, const QRect &rect, int alignment, const QPalette &palette, bool enabled, const QString &text, QPalette::ColorRole textRole) const{ /*QFont cfont = painter->font(); cfont.setHintingPreference(QFont::PreferFullHinting); QFont outfont = cfont; outfont.setStretch(101); outfont.setLetterSpacing(QFont::PercentageSpacing, 99); //Paint the background outline if(darkfont){ painter->setPen(QPen(Qt::white)); } else{ painter->setPen(QPen(Qt::black)); } painter->setFont(outfont); //QRect outline = QRect(rect.left()+2, rect.top()+2, rect.right()+2, rect.bottom()+2); painter->drawText(rect, text); //Paint the text itself (Make this respect the "enabled" flag later) painter->setFont(cfont); if(darkfont){ painter->setPen(QPen(Qt::black)); } else{ painter->setPen(QPen(Qt::white)); } painter->drawText(rect, text);*/ /*QFont font = painter->font(); QFont cfont = font; //save for later if(font.pixelSize()>0){ font.setPixelSize( font.pixelSize()-4); } else{ font.setPointSize(font.pointSize()-1); } painter->setFont(font); //Create the path QPainterPath path; //path.setFillRule(Qt::WindingFill); path.addText(rect.left(), rect.center().y()+(painter->fontMetrics().xHeight()/2), painter->font(), text); //Now set the border/fill colors QPen pen; pen.setWidth(2); if(darkfont){ pen.setColor(Qt::white); painter->fillPath(path,Qt::black); }else{ pen.setColor(Qt::black); painter->fillPath(path,Qt::white); } painter->setPen(pen); painter->drawPath(path); painter->setFont(cfont); //reset back to original font*/ //} //================== // THEME ENGINE CLASS //================== LuminaThemeEngine::LuminaThemeEngine(QApplication *app){ application=app; //save this pointer for later //style = new LuminaThemeStyle(); //Set the application-wide style //application->setStyle( style ); lastcheck = QDateTime::currentDateTime(); // // Now load the theme stylesheet QStringList current = LTHEME::currentSettings(); theme = current[0]; colors=current[1]; icons=current[2]; font=current[3]; fontsize=current[4]; cursors = LTHEME::currentCursor(); if(application->applicationFilePath().section("/",-1)=="lumina-desktop"){ application->setStyleSheet( LTHEME::assembleStyleSheet(theme, colors, font, fontsize) ); }else{ //Non-Desktop binary - only use alternate Qt methods (skip stylesheets) QFont tmp = application->font(); tmp.setStyleStrategy(QFont::PreferOutline); tmp.setFamily(font); tmp.setHintingPreference(QFont::PreferFullHinting); if(fontsize.endsWith("pt")){ tmp.setPointSize(fontsize.section("pt",0,0).toInt()); } else if(fontsize.endsWith("px")){ tmp.setPixelSize(fontsize.section("px",0,0).toInt()); } application->setFont(tmp); } QIcon::setThemeName(icons); //make sure this sets set within this environment syncTimer = new QTimer(this); syncTimer->setSingleShot(true); syncTimer->setInterval(500); //wait 1/2 second before re-loading the files if(cursors.isEmpty()){ LTHEME::setCursorTheme("default"); //X11 fallback (always installed?) cursors = "default"; } //setenv("XCURSOR_THEME", cursors.toLocal8Bit(),1); watcher = new QFileSystemWatcher(this); watcher->addPath( QString(getenv("XDG_CONFIG_HOME"))+"/lumina-desktop/envsettings.conf" ); watcher->addPath( QString(getenv("XDG_CONFIG_HOME"))+"/lumina-desktop/themesettings.cfg" ); watcher->addPaths( QStringList() << theme << colors << QDir::homePath()+"/.icons/default/index.theme" ); //also watch these files for changes connect(watcher, SIGNAL(fileChanged(QString)), this, SLOT(watcherChange(QString)) ); connect(syncTimer, SIGNAL(timeout()), this, SLOT(reloadFiles()) ); } LuminaThemeEngine::~LuminaThemeEngine(){ } void LuminaThemeEngine::refresh(){ QTimer::singleShot(100,this, SLOT(reloadFiles()) ); } void LuminaThemeEngine::watcherChange(QString file){ if(syncTimer->isActive()){ syncTimer->stop(); } syncTimer->start(); if(!watcher->files().contains(file)){ watcher->addPath(file); } } void LuminaThemeEngine::reloadFiles(){ //Check the Theme file/settings if(lastcheck < QFileInfo(QString(getenv("XDG_CONFIG_HOME"))+"/lumina-desktop/themesettings.cfg").lastModified().addSecs(1) ){ QStringList current = LTHEME::currentSettings(); if(application->applicationFilePath().section("/",-1)=="lumina-desktop"){ application->setStyleSheet( LTHEME::assembleStyleSheet(current[0], current[1], current[3], current[4]) ); } if(icons!=current[2]){ QIcon::setThemeName(current[2]); //make sure this sets set within this environment emit updateIcons(); } //save the settings for comparison later theme = current[0]; colors=current[1]; icons=current[2]; if(font!=current[3] || fontsize!=current[4]){ font=current[3]; fontsize=current[4]; QFont tmp = application->font(); tmp.setStyleStrategy(QFont::PreferAntialias); tmp.setFamily(font); if(fontsize.endsWith("pt")){ tmp.setPointSize(fontsize.section("pt",0,0).toInt()); } else if(fontsize.endsWith("px")){ tmp.setPixelSize(fontsize.section("px",0,0).toInt()); } application->setFont(tmp); } } //Check the Cursor file/settings if(lastcheck < QFileInfo(QDir::homePath()+"/.icons/default/index.theme").lastModified()){ QString ccurs = LTHEME::currentCursor(); if(cursors != ccurs){ emit updateCursors(); //Might be something we can do automatically here as well - since we have the QApplication handy // - Note: setting/unsetting an override cursor does not update the current cursor bitmap // Qt created a background database/hash/mapping of the theme pixmaps on startup // So Qt itself needs to be prodded to update that mapping /*QXcbCursor::cursorThemePropertyChanged( \ new QXcbVirtualDesktop(QX11Info::connection(), application->screen()->handle(), QX11Info::appScreen()), ccurs.toData(), QVariant("Inherits"), NULL);*/ //QCursorData::cleanup(); //QCursorData::initialize(); //setenv("XCURSOR_THEME", ccurs.toLocal8Bit(),1); } cursors = ccurs; } //Environment Changes if( lastcheck < QFileInfo(QString(getenv("XDG_CONFIG_HOME"))+"/lumina-desktop/envsettings.conf").lastModified()){ LTHEME::LoadCustomEnvSettings(); emit EnvChanged(); } lastcheck = QDateTime::currentDateTime(); //Now update the watched files to ensure nothing is missed watcher->removePaths( QStringList() << theme << colors << QDir::homePath()+"/.icons/default/index.theme" << QString(getenv("XDG_CONFIG_HOME"))+"/lumina-desktop/envsettings.conf"); watcher->addPaths( QStringList() << theme << colors << QDir::homePath()+"/.icons/default/index.theme" << QString(getenv("XDG_CONFIG_HOME"))+"/lumina-desktop/envsettings.conf"); }