//=========================================== // Lumina-desktop source code // Copyright (c) 2017, Ken Moore // Available under the 3-clause BSD license // See the LICENSE file for full details //=========================================== #include "LIconCache.h" #include #include #include #include #include LIconCache::LIconCache(QObject *parent) : QObject(parent){ connect(this, SIGNAL(InternalIconLoaded(QString, QDateTime, QByteArray*)), this, SLOT(IconLoaded(QString, QDateTime, QByteArray*)) ); } LIconCache::~LIconCache(){ } LIconCache* LIconCache::instance(){ static LIconCache cache; return &cache; } // === PUBLIC === //Icon Checks bool LIconCache::exists(QString icon){ if(icon.isEmpty()){ return false; } if(HASH.contains(icon)){ return true; } //already else if(!icon.startsWith("/")){ //relative path to file (from icon theme?) QString path = findFile(icon); if(!path.isEmpty() && QFile::exists(path)){ return true; } }else{ //absolute path to file return QFile::exists(icon); } return false; } bool LIconCache::isLoaded(QString icon){ if(icon.isEmpty()){ return false; } if(HASH.contains(icon)){ return !HASH[icon].icon.isNull(); } return false; } QString LIconCache::findFile(QString icon){ if(icon.isEmpty()){ return ""; } //Get the currently-set theme QString cTheme = QIcon::themeName(); if(cTheme.isEmpty()){ QIcon::setThemeName("material-design-light"); cTheme = "material-design-light"; } //Make sure the current search paths correspond to this theme if( QDir::searchPaths("icontheme").filter("/"+cTheme+"/").isEmpty() ){ //Need to reset search paths: setup the "icontheme" "material-design-light" and "fallback" sets // - Get all the base icon directories QStringList paths; paths << QDir::homePath()+"/.icons/"; //ordered by priority - local user dirs first QStringList xdd = QString(getenv("XDG_DATA_HOME")).split(":"); xdd << QString(getenv("XDG_DATA_DIRS")).split(":"); for(int i=0; i Theme2 -> Theme3 -> Fallback) //fall << LOS::AppPrefix()+"share/pixmaps"; //always use this as well as a final fallback QDir::setSearchPaths("icontheme", theme); QDir::setSearchPaths("default", oxy); QDir::setSearchPaths("fallback", fall); //qDebug() << "Setting Icon Search Paths:" << "\nicontheme:" << theme << "\nmaterial-design-light:" << oxy << "\nfallback:" << fall; } //Find the icon in the search paths QIcon ico; QStringList srch; srch << "icontheme" << "default" << "fallback"; for(int i=0; isetIcon( iconFromTheme(icon)); return ; } //See if the icon has already been loaded into the HASH bool needload = !HASH.contains(icon); if(!needload){ if(!noThumb && !HASH[icon].thumbnail.isNull()){ button->setIcon( HASH[icon].thumbnail ); return; } else if(!HASH[icon].icon.isNull()){ button->setIcon( HASH[icon].icon ); return; } } //Need to load the icon icon_data idata; if(HASH.contains(icon)){ idata = HASH.value(icon); } else { idata = createData(icon); } idata.pendingButtons << QPointer(button); //save this button for later HASH.insert(icon, idata); if(needload){ startReadFile(icon, idata.fullpath); } } void LIconCache::loadIcon(QAction *action, QString icon, bool noThumb){ if(icon.isEmpty()){ return; } if(isThemeIcon(icon)){ action->setIcon( iconFromTheme(icon)); return ; } //See if the icon has already been loaded into the HASH bool needload = !HASH.contains(icon); if(!needload){ if(!noThumb && !HASH[icon].thumbnail.isNull()){ action->setIcon( HASH[icon].thumbnail ); return; } else if(!HASH[icon].icon.isNull()){ action->setIcon( HASH[icon].icon ); return; } } //Need to load the icon icon_data idata; if(HASH.contains(icon)){ idata = HASH.value(icon); } else { idata = createData(icon); } idata.pendingActions << QPointer(action); //save this button for later HASH.insert(icon, idata); if(needload){ startReadFile(icon, idata.fullpath); } } void LIconCache::loadIcon(QLabel *label, QString icon, bool noThumb){ if(icon.isEmpty()){ return; } if(isThemeIcon(icon)){ label->setPixmap( iconFromTheme(icon).pixmap(label->sizeHint()) ); return ; } //See if the icon has already been loaded into the HASH bool needload = !HASH.contains(icon); if(!needload){ if(!noThumb && !HASH[icon].thumbnail.isNull()){ label->setPixmap( HASH[icon].thumbnail.pixmap(label->sizeHint()) ); return; } else if(!HASH[icon].icon.isNull()){ label->setPixmap( HASH[icon].icon.pixmap(label->sizeHint()) ); return; } } //Need to load the icon icon_data idata; if(HASH.contains(icon)){ idata = HASH.value(icon); } else { idata = createData(icon); if(idata.fullpath.isEmpty()){ return; } //nothing to do } idata.pendingLabels << QPointer(label); //save this QLabel for later HASH.insert(icon, idata); if(needload){ startReadFile(icon, idata.fullpath); } } void LIconCache::clearIconTheme(){ //use when the icon theme changes to refresh all requested icons QStringList keys = HASH.keys(); for(int i=0; i 0){ out << D.absolutePath(); } for(int i=0; i 0){ out << img; } } return out; } QStringList LIconCache::getIconThemeDepChain(QString theme, QStringList paths){ QStringList results; for(int i=0; isetIcon(idat.icon); } } idat.pendingButtons.clear(); for(int i=0; isetPixmap(idat.icon.pixmap(idat.pendingLabels[i]->sizeHint())); } } idat.pendingLabels.clear(); for(int i=0; isetIcon(idat.icon); } } idat.pendingActions.clear(); //Now update the hash and let the world know it is available now HASH.insert(id, idat); this->emit IconAvailable(id); }else{ QtConcurrent::run(this, &LIconCache::ReadFile, this, id, path); } } void LIconCache::ReadFile(LIconCache *obj, QString id, QString path){ //qDebug() << "Start Reading File:" << id << path; QByteArray *BA = new QByteArray(); QDateTime cdt = QDateTime::currentDateTime(); if(!path.isEmpty()){ QFile file(path); if(file.open(QIODevice::ReadOnly)){ BA->append(file.readAll()); file.close(); } } obj->emit InternalIconLoaded(id, cdt, BA); } bool LIconCache::isThemeIcon(QString id){ return (!id.contains("/") && !id.contains(".") ); } QIcon LIconCache::iconFromTheme(QString id){ QIcon ico = QIcon::fromTheme(id); if(ico.isNull()){ //icon missing in theme? run the old icon-finder system ico = QIcon(findFile(id)); } return ico; } // === PRIVATE SLOTS === void LIconCache::IconLoaded(QString id, QDateTime sync, QByteArray *data){ //qDebug() << "Icon Loaded:" << id << HASH.contains(id); QPixmap pix; bool ok = pix.loadFromData(*data); delete data; //no longer used - free this up if(!HASH.contains(id)){ return; } //icon loading cancelled - just stop here if(!ok){ HASH.remove(id); } //icon data corrupted or unreadable else{ icon_data idat = HASH[id]; idat.lastread = sync; idat.icon.addPixmap(pix); if(pix.width() < 64){ idat.icon.addPixmap( pix.scaled( QSize(64,64), Qt::KeepAspectRatio, Qt::SmoothTransformation) ); } //also add a version which has been scaled up a bit //Now throw this icon into any pending objects for(int i=0; isetIcon(idat.icon); } } idat.pendingButtons.clear(); for(int i=0; isetPixmap(pix.scaled(idat.pendingLabels[i]->sizeHint(), Qt::KeepAspectRatio, Qt::SmoothTransformation)); } } idat.pendingLabels.clear(); for(int i=0; isetIcon(idat.icon); } } idat.pendingActions.clear(); //Now update the hash and let the world know it is available now HASH.insert(id, idat); this->emit IconAvailable(id); } }