#include "imageringlist.h" #include #include #include #include #include #include #include "loadrunable.h" #include "rawimage.h" #include "database.h" using namespace std; int DEFAULT_WIDTH = 2; Image::Image(const QString name, int number, ImageRingList *ringList) : m_loading(false), m_released(true), m_current(false), m_number(number), m_name(name), m_ringList(ringList) { } void Image::load(QThreadPool *pool) { if(!m_rawImage && !m_loading) { m_loading = true; m_released = false; pool->start(new LoadRunable(m_name, this, m_ringList->analyzeLevel())); } if(!m_loading && m_rawImage) emit pixmapLoaded(this); } void Image::loadThumbnail(QThreadPool *pool) { if(!m_thumbnail) pool->start(new LoadRunable(m_name, this, AnalyzeLevel::None, true)); else emit thumbnailLoaded(this); } void Image::release() { m_rawImage.reset(); m_released = true; m_loading = false; } QString Image::name() const { return m_name; } std::shared_ptr Image::rawImage() { return m_rawImage; } const RawImage *Image::thumbnail() const { return m_thumbnail.get(); } ImageInfoData Image::info() const { return m_info; } bool Image::isCurrent() const { return !m_released; } int Image::number() const { return m_number; } void Image::clearThumbnail() { m_thumbnail.reset(); } bool Image::isLoading() const { return m_loading; } void Image::imageLoaded(std::shared_ptr rawImage, ImageInfoData info) { m_loading = false; if(!m_released) { m_rawImage = rawImage; m_info = info; emit pixmapLoaded(this); } } void Image::thumbnailLoadFinish(std::shared_ptr rawImage) { m_thumbnail = rawImage; if(m_thumbnail) emit thumbnailLoaded(this); } ImageRingList::ImageRingList(Database *database, const QStringList &nameFilter, QObject *parent) : QAbstractItemModel(parent) , m_liveMode(false) , m_analyzeLevel(None) , m_database(database) , m_nameFilter(nameFilter) , m_fileSuffix(nameFilter) { connect(&m_fileSystemWatcher, SIGNAL(directoryChanged(QString)), this, SLOT(dirChanged(QString))); m_nameFilter.replaceInStrings(QRegularExpression("^"), "*."); m_loadPool = new QThreadPool(this); m_loadPool->setThreadPriority(QThread::LowPriority); m_thumbPool = new QThreadPool(this); m_thumbPool->setThreadPriority(QThread::LowPriority); m_slideShowTimer = new QTimer(this); connect(m_slideShowTimer, &QTimer::timeout, this, static_cast(&ImageRingList::increment)); m_dirChangeDelay = new QTimer(this); m_dirChangeDelay->setInterval(3000); m_dirChangeDelay->setSingleShot(true); connect(m_dirChangeDelay, &QTimer::timeout, this, &ImageRingList::reloadDir); } ImageRingList::~ImageRingList() { m_loadPool->clear(); m_thumbPool->clear(); m_loadPool->waitForDone(); m_thumbPool->waitForDone(); } bool ImageRingList::setDir(const QString path, const QString ¤tFile, bool recursive) { QDir dir(path); if(dir.exists()) { QStringList scannedDirs; QStringList absolutePaths; std::function scanDir = [&](const QString &path) { QDir dir(path); if(scannedDirs.contains(dir.canonicalPath()))return; scannedDirs.append(dir.canonicalPath()); QDir::SortFlags sortFlags = m_liveMode ? QDir::Time : m_sort | QDir::IgnoreCase; if(m_reversed)sortFlags |= QDir::Reversed; if(recursive) { QStringList dirs = dir.entryList(QDir::Readable | QDir::Dirs | QDir::NoDotAndDotDot, sortFlags); for(const QString &subdir : dirs) scanDir(dir.absoluteFilePath(subdir)); } QStringList list = dir.entryList(m_nameFilter, QDir::Files | QDir::Readable, sortFlags); for(const QString &file : list) { absolutePaths.append(dir.absoluteFilePath(file)); } }; scanDir(path); //qDebug() << absolutePaths.size(); setFilesPrivate(absolutePaths, m_liveMode ? absolutePaths.first() : currentFile); m_fileSystemWatcher.removePaths(m_fileSystemWatcher.directories()); m_fileSystemWatcher.addPath(path); return true; } return false; } void ImageRingList::setFile(const QString &file) { if(!file.isEmpty()) { QFileInfo info(file); if(info.isDir()) setDir(file, QString(), true); else setDir(info.absolutePath(), file); } } void ImageRingList::setFiles(QStringList files) { QRegularExpression reg("(" + m_fileSuffix.join("|") + ")"); files.removeIf([®](const QString &file){ QFileInfo info(file); auto match = reg.match(info.suffix()); return !match.hasMatch() || !info.exists() || !info.isReadable() || !info.isFile(); }); setFilesPrivate(files); } ImagePtr ImageRingList::currentImage() { if(m_images.size()) return *m_currImage; else return 0; } void ImageRingList::increment() { if(m_images.size()) { //don't increment if current image was not loaded yet if((*m_currImage)->isLoading()) return; (*m_firstImage)->release(); m_firstImage = increment(m_firstImage); m_currImage = increment(m_currImage); (*m_currImage)->load(m_loadPool); m_lastImage = increment(m_lastImage); (*m_lastImage)->load(m_loadPool); } } void ImageRingList::decrement() { if(m_images.size()) { //don't decrement if current image was not loaded yet if((*m_currImage)->isLoading()) return; (*m_lastImage)->release(); m_firstImage = decrement(m_firstImage); m_currImage = decrement(m_currImage); (*m_currImage)->load(m_loadPool); m_lastImage = decrement(m_lastImage); (*m_firstImage)->load(m_loadPool); } } void ImageRingList::setMarked() { QStringList files = m_database->getMarkedFiles(); files.removeIf([](const QString &file){ QFileInfo info(file); return !info.exists() || !info.isReadable(); }); setFilesPrivate(files); } void ImageRingList::setLiveMode(bool live) { m_liveMode = live; } void ImageRingList::setCalculateStats(bool stats) { m_analyzeLevel = stats ? Statistics : None; } void ImageRingList::setFindPeaks(bool findPeaks) { m_analyzeLevel = findPeaks ? Peaks : None; } void ImageRingList::setFindStars(bool findStars) { m_analyzeLevel = findStars ? Stars : None; } AnalyzeLevel ImageRingList::analyzeLevel() const { return m_analyzeLevel; } void ImageRingList::loadFile(int row) { if(row < m_images.size()) { int diff = m_currImage != m_images.end() ? row - (m_currImage - m_images.begin()) : -100; if(diff == 1) increment(); else if(diff == -1) decrement(); else { m_firstImage = m_currImage = m_lastImage = m_images.begin()+row; if(m_images.empty()) return; (*m_currImage)->load(m_loadPool); m_width = DEFAULT_WIDTHload(m_loadPool); m_lastImage = increment(m_lastImage); (*m_lastImage)->load(m_loadPool); } if(m_lastImage != m_firstImage) { QList::iterator iter = increment(m_lastImage); while(m_firstImage != iter) { (*iter)->release(); iter = increment(iter); } } } } } void ImageRingList::loadThumbnails() { for(auto &img : m_images) { img->loadThumbnail(m_thumbPool); } } void ImageRingList::stopLoading() { m_thumbPool->clear(); m_thumbPool->waitForDone(); } int ImageRingList::imageCount() const { return m_images.size(); } QStringList ImageRingList::imageNames() const { QStringList ret; for(auto &img : m_images) ret.push_back(img->name()); return ret; } void ImageRingList::updateMark() { if(m_images.size()) { QModelIndex idx = index(m_currImage - m_images.begin(), 0); emit dataChanged(idx, idx, {Qt::FontRole}); } } void ImageRingList::clearThumbnails() { for(auto &img : m_images) img->clearThumbnail(); } QModelIndex ImageRingList::index(int row, int column, const QModelIndex &parent) const { Q_UNUSED(parent); return createIndex(row, column, m_images.at(row).get()); } QModelIndex ImageRingList::parent(const QModelIndex &child) const { Q_UNUSED(child); return QModelIndex(); } int ImageRingList::rowCount(const QModelIndex &parent) const { if(parent == QModelIndex()) return m_images.size(); else return 0; } int ImageRingList::columnCount(const QModelIndex &parent) const { Q_UNUSED(parent); return 1; } QVariant ImageRingList::data(const QModelIndex &index, int role) const { switch(role) { case Qt::DisplayRole: { QFileInfo info(m_images.at(index.row())->name()); return info.fileName(); } case Qt::FontRole: { bool marked = m_database->isMarked(m_images.at(index.row())->name()); QFont font; font.setBold(marked); return font; } default: return QVariant(); } } QVariant ImageRingList::headerData(int section, Qt::Orientation orientation, int role) const { if(section==0 && orientation==Qt::Horizontal && role==Qt::DisplayRole) { return tr("Name"); } return QVariant(); } void ImageRingList::setPreload(int width) { DEFAULT_WIDTH = width; if(m_images.size() == 0)return; int newWidth = DEFAULT_WIDTH m_width) { for(int i = newWidth - m_width; i>0; i--) { m_firstImage = decrement(m_firstImage); (*m_firstImage)->load(m_loadPool); m_lastImage = increment(m_lastImage); (*m_lastImage)->load(m_loadPool); } } if(newWidth < m_width) { for(int i = m_width - newWidth; i>0; i--) { (*m_firstImage)->release(); m_firstImage = increment(m_firstImage); (*m_lastImage)->release(); m_lastImage = decrement(m_lastImage); } } m_width = newWidth; } void ImageRingList::setSort(QDir::SortFlag sort) { if(m_sort != sort) { m_sort = sort; if(m_images.size()) { QString path = (*m_currImage)->name(); setFile(path); } } } void ImageRingList::reverseSort() { m_reversed = !m_reversed; if(m_images.size()) { QString path = (*m_currImage)->name(); setFile(path); } } void ImageRingList::toggleSlideshow(bool start) { if(start) { QSettings settings; int time = settings.value("settings/slideshowtime", 1.0).toDouble() * 1000; m_slideShowTimer->start(time); } else { m_slideShowTimer->stop(); } } void ImageRingList::setFilesPrivate(const QStringList files, const QString ¤tFile) { m_loadPool->clear(); m_thumbPool->clear(); m_loadPool->waitForDone(); m_thumbPool->waitForDone(); beginResetModel(); m_images.clear(); int i = 0; for(const QString &file : files) { ImagePtr ptr = make_shared(file, i++, this); connect(ptr.get(), SIGNAL(pixmapLoaded(Image*)), this, SLOT(imageLoaded(Image*))); connect(ptr.get(), SIGNAL(thumbnailLoaded(Image*)), this, SIGNAL(thumbnailLoaded(Image*))); m_images.append(ptr); } int index = files.indexOf(currentFile); if(index < 0) index = 0; endResetModel(); m_currImage = m_images.end(); loadFile(index); } QList::iterator ImageRingList::increment(QList::iterator iter) { iter++; if(iter == m_images.end()) iter = m_images.begin(); return iter; } QList::iterator ImageRingList::decrement(QList::iterator iter) { if(iter == m_images.begin()) iter = m_images.end(); iter--; return iter; } void ImageRingList::imageLoaded(Image *image) { if(image->name() == (*m_currImage)->name()) { emit pixmapLoaded(image); emit infoLoaded(image->info()); emit currentImageChanged(m_currImage-m_images.begin()); } } void ImageRingList::dirChanged(QString dir) { m_currentDir = dir; if(m_liveMode) reloadDir(); else m_dirChangeDelay->start(); } void ImageRingList::reloadDir() { QString currentFile; if(m_images.size()) currentFile = (*m_currImage)->name(); setDir(m_currentDir, currentFile); if(m_images.size()) emit currentImageChanged(m_currImage-m_images.begin()); }