git repos / SagoImageBrowser

blame: imagemodel.cpp

normal view · raw

032aa1e1 Poul Sander 2026-02-19 18:55 1
#include "imagemodel.h"
cec3b5da Poul Sander 2026-02-19 19:17 2
#include "thumbnailworker.h"
032aa1e1 Poul Sander 2026-02-19 18:55 3
032aa1e1 Poul Sander 2026-02-19 18:55 4
#include <QDir>
032aa1e1 Poul Sander 2026-02-19 18:55 5
#include <QImageReader>
032aa1e1 Poul Sander 2026-02-19 18:55 6
#include <QFileInfo>
09130ab9 Poul Sander 2026-02-22 22:19 7
#include <QFileIconProvider>
032aa1e1 Poul Sander 2026-02-19 18:55 8
032aa1e1 Poul Sander 2026-02-19 18:55 9
ImageModel::ImageModel(QObject *parent)
032aa1e1 Poul Sander 2026-02-19 18:55 10
    : QAbstractListModel(parent)
032aa1e1 Poul Sander 2026-02-19 18:55 11
{
cec3b5da Poul Sander 2026-02-19 19:17 12
    m_threadPool.setMaxThreadCount(QThread::idealThreadCount());
cec3b5da Poul Sander 2026-02-19 19:17 13
cec3b5da Poul Sander 2026-02-19 19:17 14
    m_placeholder = QPixmap(128, 128);
cec3b5da Poul Sander 2026-02-19 19:17 15
    m_placeholder.fill(Qt::lightGray);
09130ab9 Poul Sander 2026-02-22 22:19 16
09130ab9 Poul Sander 2026-02-22 22:19 17
    QFileIconProvider iconProvider;
09130ab9 Poul Sander 2026-02-22 22:19 18
    m_folderIcon = iconProvider.icon(QFileIconProvider::Folder);
cec3b5da Poul Sander 2026-02-19 19:17 19
}
cec3b5da Poul Sander 2026-02-19 19:17 20
cec3b5da Poul Sander 2026-02-19 19:17 21
ImageModel::~ImageModel()
cec3b5da Poul Sander 2026-02-19 19:17 22
{
6fcd6c90 Poul Sander 2026-02-19 21:12 23
    m_cancelFlag = true;
cec3b5da Poul Sander 2026-02-19 19:17 24
    m_threadPool.waitForDone();
032aa1e1 Poul Sander 2026-02-19 18:55 25
}
032aa1e1 Poul Sander 2026-02-19 18:55 26
032aa1e1 Poul Sander 2026-02-19 18:55 27
void ImageModel::setDirectory(const QString &path)
032aa1e1 Poul Sander 2026-02-19 18:55 28
{
6fcd6c90 Poul Sander 2026-02-19 21:12 29
    m_cancelFlag = true;
6fcd6c90 Poul Sander 2026-02-19 21:12 30
    m_threadPool.waitForDone();
6fcd6c90 Poul Sander 2026-02-19 21:12 31
    m_cancelFlag = false;
6fcd6c90 Poul Sander 2026-02-19 21:12 32
032aa1e1 Poul Sander 2026-02-19 18:55 33
    beginResetModel();
032aa1e1 Poul Sander 2026-02-19 18:55 34
    m_items.clear();
6fcd6c90 Poul Sander 2026-02-19 21:12 35
    m_pendingRows.clear();
09130ab9 Poul Sander 2026-02-22 22:19 36
    m_currentDir = path;
032aa1e1 Poul Sander 2026-02-19 18:55 37
032aa1e1 Poul Sander 2026-02-19 18:55 38
    QDir dir(path);
09130ab9 Poul Sander 2026-02-22 22:19 39
09130ab9 Poul Sander 2026-02-22 22:19 40
    // Add subdirectories first
09130ab9 Poul Sander 2026-02-22 22:19 41
    dir.setFilter(QDir::AllDirs | QDir::NoDot);
09130ab9 Poul Sander 2026-02-22 22:19 42
    dir.setSorting(QDir::Name);
09130ab9 Poul Sander 2026-02-22 22:19 43
    const auto dirs = dir.entryInfoList();
09130ab9 Poul Sander 2026-02-22 22:19 44
    QPixmap folderPixmap = m_folderIcon.pixmap(128, 128);
09130ab9 Poul Sander 2026-02-22 22:19 45
    for (const QFileInfo &d : dirs)
09130ab9 Poul Sander 2026-02-22 22:19 46
    {
09130ab9 Poul Sander 2026-02-22 22:19 47
        QString name = d.fileName();  // preserves ".." for the parent entry
09130ab9 Poul Sander 2026-02-22 22:19 48
        m_items.append({d.absoluteFilePath(), name, folderPixmap, true, true});
09130ab9 Poul Sander 2026-02-22 22:19 49
    }
09130ab9 Poul Sander 2026-02-22 22:19 50
09130ab9 Poul Sander 2026-02-22 22:19 51
    // Then add image files
032aa1e1 Poul Sander 2026-02-19 18:55 52
    QStringList filters;
032aa1e1 Poul Sander 2026-02-19 18:55 53
032aa1e1 Poul Sander 2026-02-19 18:55 54
    const auto formats = QImageReader::supportedImageFormats();
032aa1e1 Poul Sander 2026-02-19 18:55 55
    for (const QByteArray &fmt : formats)
032aa1e1 Poul Sander 2026-02-19 18:55 56
        filters << "*." + fmt;
032aa1e1 Poul Sander 2026-02-19 18:55 57
032aa1e1 Poul Sander 2026-02-19 18:55 58
    dir.setNameFilters(filters);
032aa1e1 Poul Sander 2026-02-19 18:55 59
    dir.setFilter(QDir::Files);
09130ab9 Poul Sander 2026-02-22 22:19 60
    dir.setSorting(QDir::Name);
032aa1e1 Poul Sander 2026-02-19 18:55 61
cec3b5da Poul Sander 2026-02-19 19:17 62
    const auto files = dir.entryInfoList();
cec3b5da Poul Sander 2026-02-19 19:17 63
cec3b5da Poul Sander 2026-02-19 19:17 64
    for (const QFileInfo &file : files)
032aa1e1 Poul Sander 2026-02-19 18:55 65
    {
09130ab9 Poul Sander 2026-02-22 22:19 66
        m_items.append({file.absoluteFilePath(), file.fileName(), m_placeholder, false, false});
6fcd6c90 Poul Sander 2026-02-19 21:12 67
    }
cec3b5da Poul Sander 2026-02-19 19:17 68
6fcd6c90 Poul Sander 2026-02-19 21:12 69
    endResetModel();
6fcd6c90 Poul Sander 2026-02-19 21:12 70
}
032aa1e1 Poul Sander 2026-02-19 18:55 71
6fcd6c90 Poul Sander 2026-02-19 21:12 72
void ImageModel::requestThumbnails(int firstRow, int lastRow)
6fcd6c90 Poul Sander 2026-02-19 21:12 73
{
6fcd6c90 Poul Sander 2026-02-19 21:12 74
    if (m_items.isEmpty())
6fcd6c90 Poul Sander 2026-02-19 21:12 75
        return;
032aa1e1 Poul Sander 2026-02-19 18:55 76
6fcd6c90 Poul Sander 2026-02-19 21:12 77
    firstRow = std::max(0, firstRow);
6fcd6c90 Poul Sander 2026-02-19 21:12 78
    lastRow = std::min(lastRow, static_cast<int>(m_items.size()) - 1);
cec3b5da Poul Sander 2026-02-19 19:17 79
6fcd6c90 Poul Sander 2026-02-19 21:12 80
    for (int row = firstRow; row <= lastRow; ++row)
6fcd6c90 Poul Sander 2026-02-19 21:12 81
    {
09130ab9 Poul Sander 2026-02-22 22:19 82
        if (!m_items[row].loaded && !m_items[row].isFolder)
6fcd6c90 Poul Sander 2026-02-19 21:12 83
            queueRow(row);
032aa1e1 Poul Sander 2026-02-19 18:55 84
    }
032aa1e1 Poul Sander 2026-02-19 18:55 85
6fcd6c90 Poul Sander 2026-02-19 21:12 86
    // Optional: preload nearby rows
6fcd6c90 Poul Sander 2026-02-19 21:12 87
    int buffer = 20;
6fcd6c90 Poul Sander 2026-02-19 21:12 88
    for (int row = firstRow - buffer; row < firstRow; ++row)
09130ab9 Poul Sander 2026-02-22 22:19 89
        if (row >= 0 && !m_items[row].loaded && !m_items[row].isFolder)
6fcd6c90 Poul Sander 2026-02-19 21:12 90
            queueRow(row);
6fcd6c90 Poul Sander 2026-02-19 21:12 91
6fcd6c90 Poul Sander 2026-02-19 21:12 92
    for (int row = lastRow + 1; row <= lastRow + buffer; ++row)
09130ab9 Poul Sander 2026-02-22 22:19 93
        if (row < m_items.size() && !m_items[row].loaded && !m_items[row].isFolder)
6fcd6c90 Poul Sander 2026-02-19 21:12 94
            queueRow(row);
6fcd6c90 Poul Sander 2026-02-19 21:12 95
}
6fcd6c90 Poul Sander 2026-02-19 21:12 96
6fcd6c90 Poul Sander 2026-02-19 21:12 97
void ImageModel::queueRow(int row)
6fcd6c90 Poul Sander 2026-02-19 21:12 98
{
6fcd6c90 Poul Sander 2026-02-19 21:12 99
    if (m_pendingRows.contains(row))
6fcd6c90 Poul Sander 2026-02-19 21:12 100
        return;
6fcd6c90 Poul Sander 2026-02-19 21:12 101
6fcd6c90 Poul Sander 2026-02-19 21:12 102
    m_pendingRows.insert(row);
6fcd6c90 Poul Sander 2026-02-19 21:12 103
6fcd6c90 Poul Sander 2026-02-19 21:12 104
    auto *worker = new ThumbnailWorker(
6fcd6c90 Poul Sander 2026-02-19 21:12 105
        m_items[row].path,
6fcd6c90 Poul Sander 2026-02-19 21:12 106
        row,
6fcd6c90 Poul Sander 2026-02-19 21:12 107
        &m_cancelFlag);
6fcd6c90 Poul Sander 2026-02-19 21:12 108
6fcd6c90 Poul Sander 2026-02-19 21:12 109
    connect(worker, &ThumbnailWorker::finished,
6fcd6c90 Poul Sander 2026-02-19 21:12 110
            this, &ImageModel::thumbnailReady,
6fcd6c90 Poul Sander 2026-02-19 21:12 111
            Qt::QueuedConnection);
6fcd6c90 Poul Sander 2026-02-19 21:12 112
6fcd6c90 Poul Sander 2026-02-19 21:12 113
    m_threadPool.start(worker);
032aa1e1 Poul Sander 2026-02-19 18:55 114
}
032aa1e1 Poul Sander 2026-02-19 18:55 115
032aa1e1 Poul Sander 2026-02-19 18:55 116
QString ImageModel::filePath(const QModelIndex &index) const
032aa1e1 Poul Sander 2026-02-19 18:55 117
{
032aa1e1 Poul Sander 2026-02-19 18:55 118
    if (!index.isValid())
032aa1e1 Poul Sander 2026-02-19 18:55 119
        return {};
032aa1e1 Poul Sander 2026-02-19 18:55 120
032aa1e1 Poul Sander 2026-02-19 18:55 121
    return m_items[index.row()].path;
032aa1e1 Poul Sander 2026-02-19 18:55 122
}
032aa1e1 Poul Sander 2026-02-19 18:55 123
09130ab9 Poul Sander 2026-02-22 22:19 124
bool ImageModel::isFolder(const QModelIndex &index) const
09130ab9 Poul Sander 2026-02-22 22:19 125
{
09130ab9 Poul Sander 2026-02-22 22:19 126
    if (!index.isValid() || index.row() >= m_items.size())
09130ab9 Poul Sander 2026-02-22 22:19 127
        return false;
09130ab9 Poul Sander 2026-02-22 22:19 128
    return m_items[index.row()].isFolder;
09130ab9 Poul Sander 2026-02-22 22:19 129
}
09130ab9 Poul Sander 2026-02-22 22:19 130
09130ab9 Poul Sander 2026-02-22 22:19 131
QString ImageModel::currentDirectory() const
09130ab9 Poul Sander 2026-02-22 22:19 132
{
09130ab9 Poul Sander 2026-02-22 22:19 133
    return m_currentDir;
09130ab9 Poul Sander 2026-02-22 22:19 134
}
09130ab9 Poul Sander 2026-02-22 22:19 135
032aa1e1 Poul Sander 2026-02-19 18:55 136
int ImageModel::rowCount(const QModelIndex &) const
032aa1e1 Poul Sander 2026-02-19 18:55 137
{
032aa1e1 Poul Sander 2026-02-19 18:55 138
    return m_items.size();
032aa1e1 Poul Sander 2026-02-19 18:55 139
}
032aa1e1 Poul Sander 2026-02-19 18:55 140
032aa1e1 Poul Sander 2026-02-19 18:55 141
QVariant ImageModel::data(const QModelIndex &index, int role) const
032aa1e1 Poul Sander 2026-02-19 18:55 142
{
032aa1e1 Poul Sander 2026-02-19 18:55 143
    if (!index.isValid())
032aa1e1 Poul Sander 2026-02-19 18:55 144
        return {};
032aa1e1 Poul Sander 2026-02-19 18:55 145
032aa1e1 Poul Sander 2026-02-19 18:55 146
    const Item &item = m_items[index.row()];
032aa1e1 Poul Sander 2026-02-19 18:55 147
032aa1e1 Poul Sander 2026-02-19 18:55 148
    if (role == Qt::DecorationRole)
032aa1e1 Poul Sander 2026-02-19 18:55 149
        return item.thumbnail;
032aa1e1 Poul Sander 2026-02-19 18:55 150
032aa1e1 Poul Sander 2026-02-19 18:55 151
    if (role == Qt::DisplayRole)
09130ab9 Poul Sander 2026-02-22 22:19 152
        return item.displayName;
032aa1e1 Poul Sander 2026-02-19 18:55 153
032aa1e1 Poul Sander 2026-02-19 18:55 154
    return {};
032aa1e1 Poul Sander 2026-02-19 18:55 155
}
cec3b5da Poul Sander 2026-02-19 19:17 156
6fcd6c90 Poul Sander 2026-02-19 21:12 157
void ImageModel::thumbnailReady(int row, const QImage &image)
cec3b5da Poul Sander 2026-02-19 19:17 158
{
6fcd6c90 Poul Sander 2026-02-19 21:12 159
    if (m_cancelFlag)
6fcd6c90 Poul Sander 2026-02-19 21:12 160
        return;
6fcd6c90 Poul Sander 2026-02-19 21:12 161
cec3b5da Poul Sander 2026-02-19 19:17 162
    if (row < 0 || row >= m_items.size())
cec3b5da Poul Sander 2026-02-19 19:17 163
        return;
cec3b5da Poul Sander 2026-02-19 19:17 164
6fcd6c90 Poul Sander 2026-02-19 21:12 165
    m_items[row].thumbnail = QPixmap::fromImage(image);
cec3b5da Poul Sander 2026-02-19 19:17 166
    m_items[row].loaded = true;
6fcd6c90 Poul Sander 2026-02-19 21:12 167
    m_pendingRows.remove(row);
cec3b5da Poul Sander 2026-02-19 19:17 168
cec3b5da Poul Sander 2026-02-19 19:17 169
    QModelIndex idx = index(row);
cec3b5da Poul Sander 2026-02-19 19:17 170
    emit dataChanged(idx, idx, {Qt::DecorationRole});
cec3b5da Poul Sander 2026-02-19 19:17 171
}
1970-01-01 00:00 172