git repos / SagoImageBrowser

blame: src/mainwindow.cpp

normal view · raw

032aa1e1 Poul Sander 2026-02-19 18:55 1
#include "mainwindow.h"
032aa1e1 Poul Sander 2026-02-19 18:55 2
#include "imagemodel.h"
61136b77 Poul Sander 2026-02-20 22:45 3
#include "imageviewwidget.h"
f61bf886 Poul Sander 2026-02-23 19:27 4
#include "preferencesdialog.h"
ac476ca8 Poul Sander 2026-05-18 16:47 5
#include "thumbnailcache.h"
53fdab93 Poul Sander 2026-05-18 18:45 6
#include "exifreader.h"
40efdfde Poul Sander 2026-05-19 21:24 7
#include "fsdirmodel.h"
032aa1e1 Poul Sander 2026-02-19 18:55 8
80a9926d Poul Sander 2026-05-19 20:28 9
#include <QFile>
40efdfde Poul Sander 2026-05-19 21:24 10
#include <QTreeView>
032aa1e1 Poul Sander 2026-02-19 18:55 11
#include <QTreeView>
032aa1e1 Poul Sander 2026-02-19 18:55 12
#include <QListView>
032aa1e1 Poul Sander 2026-02-19 18:55 13
#include <QLabel>
61136b77 Poul Sander 2026-02-20 22:45 14
#include <QStackedWidget>
032aa1e1 Poul Sander 2026-02-19 18:55 15
#include <QDir>
3bee3e3e Poul Sander 2026-03-05 17:07 16
#include <QListWidget>
fc5f2e27 Poul Sander 2026-02-22 22:06 17
#include <QStandardPaths>
032aa1e1 Poul Sander 2026-02-19 18:55 18
#include <QImageReader>
032aa1e1 Poul Sander 2026-02-19 18:55 19
#include <QHeaderView>
6fcd6c90 Poul Sander 2026-02-19 21:12 20
#include <QScrollBar>
6fcd6c90 Poul Sander 2026-02-19 21:12 21
#include <QTimer>
6fcd6c90 Poul Sander 2026-02-19 21:12 22
#include <QEvent>
61136b77 Poul Sander 2026-02-20 22:45 23
#include <QKeyEvent>
c9e5684a Poul Sander 2026-02-23 19:18 24
#include <QCloseEvent>
cfe4a67b Poul Sander 2026-02-19 23:06 25
#include <QItemSelectionModel>
cbc7d22b Poul Sander 2026-02-24 19:23 26
#include <QDockWidget>
f61bf886 Poul Sander 2026-02-23 19:27 27
#include <QMenuBar>
f61bf886 Poul Sander 2026-02-23 19:27 28
#include <QMenu>
f61bf886 Poul Sander 2026-02-23 19:27 29
#include <QAction>
f61bf886 Poul Sander 2026-02-23 19:27 30
#include <QSettings>
53fdab93 Poul Sander 2026-05-18 18:45 31
#include <QTableWidget>
beb4a823 Poul Sander 2026-05-19 21:53 32
#include <QDialog>
beb4a823 Poul Sander 2026-05-19 21:53 33
#include <QVBoxLayout>
beb4a823 Poul Sander 2026-05-19 21:53 34
#include <QDialogButtonBox>
beb4a823 Poul Sander 2026-05-19 21:53 35
#include <QPlainTextEdit>
6fcd6c90 Poul Sander 2026-02-19 21:12 36
#include <iostream>
032aa1e1 Poul Sander 2026-02-19 18:55 37
80a9926d Poul Sander 2026-05-19 20:28 38
#include <fcntl.h>
80a9926d Poul Sander 2026-05-19 20:28 39
#include <sys/stat.h>
80a9926d Poul Sander 2026-05-19 20:28 40
#include <unistd.h>
80a9926d Poul Sander 2026-05-19 20:28 41
ac476ca8 Poul Sander 2026-05-18 16:47 42
namespace
ac476ca8 Poul Sander 2026-05-18 16:47 43
{
ac476ca8 Poul Sander 2026-05-18 16:47 44
ThumbnailCache::Size thumbnailSizeFromString(const QString &s)
ac476ca8 Poul Sander 2026-05-18 16:47 45
{
ac476ca8 Poul Sander 2026-05-18 16:47 46
    return (s == "large") ? ThumbnailCache::Size::Large
ac476ca8 Poul Sander 2026-05-18 16:47 47
                          : ThumbnailCache::Size::Normal;
ac476ca8 Poul Sander 2026-05-18 16:47 48
}
ac476ca8 Poul Sander 2026-05-18 16:47 49
} // namespace
ac476ca8 Poul Sander 2026-05-18 16:47 50
032aa1e1 Poul Sander 2026-02-19 18:55 51
MainWindow::MainWindow(QWidget *parent)
032aa1e1 Poul Sander 2026-02-19 18:55 52
    : QMainWindow(parent)
032aa1e1 Poul Sander 2026-02-19 18:55 53
{
032aa1e1 Poul Sander 2026-02-19 18:55 54
    setupUi();
f61bf886 Poul Sander 2026-02-23 19:27 55
    setupMenuBar();
032aa1e1 Poul Sander 2026-02-19 18:55 56
    setupConnections();
f61bf886 Poul Sander 2026-02-23 19:27 57
    loadPreferences();
032aa1e1 Poul Sander 2026-02-19 18:55 58
}
032aa1e1 Poul Sander 2026-02-19 18:55 59
032aa1e1 Poul Sander 2026-02-19 18:55 60
void MainWindow::setupUi()
032aa1e1 Poul Sander 2026-02-19 18:55 61
{
40efdfde Poul Sander 2026-05-19 21:24 62
    // Folder model (left pane) — backed by std::filesystem so directories with
40efdfde Poul Sander 2026-05-19 21:24 63
    // non-UTF-8 filenames are listed correctly.
40efdfde Poul Sander 2026-05-19 21:24 64
    m_dirModel = new FsDirModel(this);
032aa1e1 Poul Sander 2026-02-19 18:55 65
    m_dirModel->setRootPath(QDir::homePath());
032aa1e1 Poul Sander 2026-02-19 18:55 66
3bee3e3e Poul Sander 2026-03-05 17:07 67
    // Root chooser list
3bee3e3e Poul Sander 2026-03-05 17:07 68
    m_rootList = new QListWidget;
3bee3e3e Poul Sander 2026-03-05 17:07 69
    m_rootList->setSelectionMode(QAbstractItemView::SingleSelection);
3bee3e3e Poul Sander 2026-03-05 17:07 70
3bee3e3e Poul Sander 2026-03-05 17:07 71
    auto addRootItem = [this](const QString &label, const QString &path) {
3bee3e3e Poul Sander 2026-03-05 17:07 72
        QListWidgetItem *item = new QListWidgetItem(label, m_rootList);
3bee3e3e Poul Sander 2026-03-05 17:07 73
        item->setData(Qt::UserRole, path);
3bee3e3e Poul Sander 2026-03-05 17:07 74
    };
3bee3e3e Poul Sander 2026-03-05 17:07 75
    addRootItem("Home", QDir::homePath());
3bee3e3e Poul Sander 2026-03-05 17:07 76
    addRootItem("Pictures", QStandardPaths::writableLocation(QStandardPaths::PicturesLocation));
3bee3e3e Poul Sander 2026-03-05 17:07 77
    addRootItem("File System", QDir::rootPath());
3bee3e3e Poul Sander 2026-03-05 17:07 78
    m_rootList->setCurrentRow(0);
fc5f2e27 Poul Sander 2026-02-22 22:06 79
032aa1e1 Poul Sander 2026-02-19 18:55 80
    m_treeView = new QTreeView;
032aa1e1 Poul Sander 2026-02-19 18:55 81
    m_treeView->setModel(m_dirModel);
40efdfde Poul Sander 2026-05-19 21:24 82
    m_treeView->setRootIndex(m_dirModel->rootIndex());
032aa1e1 Poul Sander 2026-02-19 18:55 83
    m_treeView->header()->hide();
032aa1e1 Poul Sander 2026-02-19 18:55 84
    m_treeView->setColumnHidden(1, true);
032aa1e1 Poul Sander 2026-02-19 18:55 85
    m_treeView->setColumnHidden(2, true);
032aa1e1 Poul Sander 2026-02-19 18:55 86
    m_treeView->setColumnHidden(3, true);
032aa1e1 Poul Sander 2026-02-19 18:55 87
032aa1e1 Poul Sander 2026-02-19 18:55 88
    // Thumbnail model (center pane)
032aa1e1 Poul Sander 2026-02-19 18:55 89
    m_imageModel = new ImageModel(this);
032aa1e1 Poul Sander 2026-02-19 18:55 90
032aa1e1 Poul Sander 2026-02-19 18:55 91
    m_listView = new QListView;
032aa1e1 Poul Sander 2026-02-19 18:55 92
    m_listView->setModel(m_imageModel);
032aa1e1 Poul Sander 2026-02-19 18:55 93
    m_listView->setViewMode(QListView::IconMode);
032aa1e1 Poul Sander 2026-02-19 18:55 94
    m_listView->setIconSize(QSize(128, 128));
b614a581 Poul Sander 2026-02-19 21:18 95
    m_listView->setGridSize(QSize(148, 168));
032aa1e1 Poul Sander 2026-02-19 18:55 96
    m_listView->setResizeMode(QListView::Adjust);
032aa1e1 Poul Sander 2026-02-19 18:55 97
    m_listView->setSelectionMode(QAbstractItemView::SingleSelection);
b614a581 Poul Sander 2026-02-19 21:18 98
    m_listView->setSpacing(4);
b614a581 Poul Sander 2026-02-19 21:18 99
    m_listView->setWordWrap(true);
b614a581 Poul Sander 2026-02-19 21:18 100
    m_listView->setTextElideMode(Qt::ElideRight);
032aa1e1 Poul Sander 2026-02-19 18:55 101
032aa1e1 Poul Sander 2026-02-19 18:55 102
    // Preview pane (right)
032aa1e1 Poul Sander 2026-02-19 18:55 103
    m_previewLabel = new QLabel;
032aa1e1 Poul Sander 2026-02-19 18:55 104
    m_previewLabel->setAlignment(Qt::AlignCenter);
cbc7d22b Poul Sander 2026-02-24 19:23 105
    m_previewLabel->setMinimumWidth(200);
61136b77 Poul Sander 2026-02-20 22:45 106
61136b77 Poul Sander 2026-02-20 22:45 107
    // Single image view
61136b77 Poul Sander 2026-02-20 22:45 108
    m_imageView = new ImageViewWidget;
61136b77 Poul Sander 2026-02-20 22:45 109
61136b77 Poul Sander 2026-02-20 22:45 110
    // Stack to switch between browser and single image
61136b77 Poul Sander 2026-02-20 22:45 111
    m_stack = new QStackedWidget;
cbc7d22b Poul Sander 2026-02-24 19:23 112
    m_stack->addWidget(m_listView);
61136b77 Poul Sander 2026-02-20 22:45 113
    m_stack->addWidget(m_imageView);
cbc7d22b Poul Sander 2026-02-24 19:23 114
    m_browserPage = m_listView;
61136b77 Poul Sander 2026-02-20 22:45 115
61136b77 Poul Sander 2026-02-20 22:45 116
    setCentralWidget(m_stack);
cbc7d22b Poul Sander 2026-02-24 19:23 117
cbc7d22b Poul Sander 2026-02-24 19:23 118
    // Dock widgets — can be dragged, floated and stacked by the user
3bee3e3e Poul Sander 2026-03-05 17:07 119
    m_rootDock = new QDockWidget(tr("Roots"), this);
3bee3e3e Poul Sander 2026-03-05 17:07 120
    m_rootDock->setObjectName("RootDock");
3bee3e3e Poul Sander 2026-03-05 17:07 121
    m_rootDock->setWidget(m_rootList);
3bee3e3e Poul Sander 2026-03-05 17:07 122
    addDockWidget(Qt::LeftDockWidgetArea, m_rootDock);
3bee3e3e Poul Sander 2026-03-05 17:07 123
cbc7d22b Poul Sander 2026-02-24 19:23 124
    m_folderDock = new QDockWidget(tr("Folders"), this);
cbc7d22b Poul Sander 2026-02-24 19:23 125
    m_folderDock->setObjectName("FolderDock");
3bee3e3e Poul Sander 2026-03-05 17:07 126
    m_folderDock->setWidget(m_treeView);
cbc7d22b Poul Sander 2026-02-24 19:23 127
    addDockWidget(Qt::LeftDockWidgetArea, m_folderDock);
3bee3e3e Poul Sander 2026-03-05 17:07 128
    splitDockWidget(m_rootDock, m_folderDock, Qt::Vertical);
cbc7d22b Poul Sander 2026-02-24 19:23 129
cbc7d22b Poul Sander 2026-02-24 19:23 130
    m_previewDock = new QDockWidget(tr("Preview"), this);
cbc7d22b Poul Sander 2026-02-24 19:23 131
    m_previewDock->setObjectName("PreviewDock");
cbc7d22b Poul Sander 2026-02-24 19:23 132
    m_previewDock->setWidget(m_previewLabel);
cbc7d22b Poul Sander 2026-02-24 19:23 133
    addDockWidget(Qt::RightDockWidgetArea, m_previewDock);
53fdab93 Poul Sander 2026-05-18 18:45 134
53fdab93 Poul Sander 2026-05-18 18:45 135
    // EXIF info dock — placed below the preview dock by default
53fdab93 Poul Sander 2026-05-18 18:45 136
    m_exifTable = new QTableWidget(0, 2);
53fdab93 Poul Sander 2026-05-18 18:45 137
    m_exifTable->setHorizontalHeaderLabels({tr("Field"), tr("Value")});
53fdab93 Poul Sander 2026-05-18 18:45 138
    m_exifTable->horizontalHeader()->setStretchLastSection(true);
53fdab93 Poul Sander 2026-05-18 18:45 139
    m_exifTable->verticalHeader()->hide();
53fdab93 Poul Sander 2026-05-18 18:45 140
    m_exifTable->setEditTriggers(QAbstractItemView::NoEditTriggers);
53fdab93 Poul Sander 2026-05-18 18:45 141
    m_exifTable->setSelectionMode(QAbstractItemView::NoSelection);
53fdab93 Poul Sander 2026-05-18 18:45 142
    m_exifTable->setShowGrid(false);
53fdab93 Poul Sander 2026-05-18 18:45 143
    m_exifTable->setAlternatingRowColors(true);
53fdab93 Poul Sander 2026-05-18 18:45 144
    m_exifTable->setMinimumWidth(200);
53fdab93 Poul Sander 2026-05-18 18:45 145
53fdab93 Poul Sander 2026-05-18 18:45 146
    m_exifDock = new QDockWidget(tr("EXIF Info"), this);
53fdab93 Poul Sander 2026-05-18 18:45 147
    m_exifDock->setObjectName("ExifDock");
53fdab93 Poul Sander 2026-05-18 18:45 148
    m_exifDock->setWidget(m_exifTable);
53fdab93 Poul Sander 2026-05-18 18:45 149
    addDockWidget(Qt::RightDockWidgetArea, m_exifDock);
53fdab93 Poul Sander 2026-05-18 18:45 150
    splitDockWidget(m_previewDock, m_exifDock, Qt::Vertical);
032aa1e1 Poul Sander 2026-02-19 18:55 151
}
032aa1e1 Poul Sander 2026-02-19 18:55 152
f61bf886 Poul Sander 2026-02-23 19:27 153
void MainWindow::setupMenuBar()
f61bf886 Poul Sander 2026-02-23 19:27 154
{
ccb726b8 Poul Sander 2026-02-23 22:43 155
    m_menuBar = menuBar();
0df01dc1 Poul Sander 2026-02-24 18:51 156
    m_menuBar->setStyleSheet(
0df01dc1 Poul Sander 2026-02-24 18:51 157
        "QMenuBar { padding: 1px 2px; }"
0df01dc1 Poul Sander 2026-02-24 18:51 158
        "QMenuBar::item { padding: 2px 6px; }"
0df01dc1 Poul Sander 2026-02-24 18:51 159
    );
cbc7d22b Poul Sander 2026-02-24 19:23 160
    QMenu *viewMenu = m_menuBar->addMenu("&View");
3bee3e3e Poul Sander 2026-03-05 17:07 161
    viewMenu->addAction(m_rootDock->toggleViewAction());
cbc7d22b Poul Sander 2026-02-24 19:23 162
    viewMenu->addAction(m_folderDock->toggleViewAction());
cbc7d22b Poul Sander 2026-02-24 19:23 163
    viewMenu->addAction(m_previewDock->toggleViewAction());
53fdab93 Poul Sander 2026-05-18 18:45 164
    viewMenu->addAction(m_exifDock->toggleViewAction());
cbc7d22b Poul Sander 2026-02-24 19:23 165
ccb726b8 Poul Sander 2026-02-23 22:43 166
    QMenu *editMenu = m_menuBar->addMenu("&Edit");
f61bf886 Poul Sander 2026-02-23 19:27 167
    QAction *preferencesAction = editMenu->addAction("&Preferences");
f61bf886 Poul Sander 2026-02-23 19:27 168
    connect(preferencesAction, &QAction::triggered, this, &MainWindow::onPreferencesTriggered);
f61bf886 Poul Sander 2026-02-23 19:27 169
}
f61bf886 Poul Sander 2026-02-23 19:27 170
665ac308 Poul Sander 2026-02-23 19:34 171
void MainWindow::navigateToFolder(const QString &path)
665ac308 Poul Sander 2026-02-23 19:34 172
{
80a9926d Poul Sander 2026-05-19 20:28 173
    navigateToFolder(QFile::encodeName(path));
80a9926d Poul Sander 2026-05-19 20:28 174
}
80a9926d Poul Sander 2026-05-19 20:28 175
80a9926d Poul Sander 2026-05-19 20:28 176
void MainWindow::navigateToFolder(const QByteArray &path)
80a9926d Poul Sander 2026-05-19 20:28 177
{
665ac308 Poul Sander 2026-02-23 19:34 178
    m_previousFolderPath = m_imageModel->currentDirectory();
665ac308 Poul Sander 2026-02-23 19:34 179
    m_imageModel->setDirectory(path);
40efdfde Poul Sander 2026-05-19 21:24 180
    QModelIndex dirIdx = m_dirModel->indexForPath(path);
665ac308 Poul Sander 2026-02-23 19:34 181
    if (dirIdx.isValid())
665ac308 Poul Sander 2026-02-23 19:34 182
        m_treeView->setCurrentIndex(dirIdx);
665ac308 Poul Sander 2026-02-23 19:34 183
    QTimer::singleShot(0, this, [this]() {
665ac308 Poul Sander 2026-02-23 19:34 184
        selectPreviousFolderIfExists();
665ac308 Poul Sander 2026-02-23 19:34 185
        loadVisibleThumbnails();
665ac308 Poul Sander 2026-02-23 19:34 186
    });
665ac308 Poul Sander 2026-02-23 19:34 187
}
665ac308 Poul Sander 2026-02-23 19:34 188
48607848 Poul Sander 2026-05-19 19:20 189
void MainWindow::openPath(const QString &path)
48607848 Poul Sander 2026-05-19 19:20 190
{
48607848 Poul Sander 2026-05-19 19:20 191
    QFileInfo info(path);
48607848 Poul Sander 2026-05-19 19:20 192
    if (!info.exists())
48607848 Poul Sander 2026-05-19 19:20 193
        return;
48607848 Poul Sander 2026-05-19 19:20 194
48607848 Poul Sander 2026-05-19 19:20 195
    if (info.isDir()) {
80a9926d Poul Sander 2026-05-19 20:28 196
        navigateToFolder(QFile::encodeName(info.absoluteFilePath()));
48607848 Poul Sander 2026-05-19 19:20 197
    } else if (info.isFile()) {
80a9926d Poul Sander 2026-05-19 20:28 198
        navigateToFolder(QFile::encodeName(info.absolutePath()));
48607848 Poul Sander 2026-05-19 19:20 199
48607848 Poul Sander 2026-05-19 19:20 200
        // ImageModel::setDirectory is synchronous, so items are ready now.
48607848 Poul Sander 2026-05-19 19:20 201
        // Find the file in the model and open it.
80a9926d Poul Sander 2026-05-19 20:28 202
        QByteArray target = QFile::encodeName(info.absoluteFilePath());
48607848 Poul Sander 2026-05-19 19:20 203
        for (int i = 0; i < m_imageModel->rowCount(); ++i) {
48607848 Poul Sander 2026-05-19 19:20 204
            QModelIndex idx = m_imageModel->index(i);
80a9926d Poul Sander 2026-05-19 20:28 205
            if (m_imageModel->filePath(idx) == target) {
48607848 Poul Sander 2026-05-19 19:20 206
                m_listView->setCurrentIndex(idx);
48607848 Poul Sander 2026-05-19 19:20 207
                m_listView->scrollTo(idx);
48607848 Poul Sander 2026-05-19 19:20 208
                showPreview(idx);
48607848 Poul Sander 2026-05-19 19:20 209
                if (!m_imageModel->isFolder(idx))
48607848 Poul Sander 2026-05-19 19:20 210
                    enterSingleImageMode(idx);
48607848 Poul Sander 2026-05-19 19:20 211
                break;
48607848 Poul Sander 2026-05-19 19:20 212
            }
48607848 Poul Sander 2026-05-19 19:20 213
        }
48607848 Poul Sander 2026-05-19 19:20 214
    }
48607848 Poul Sander 2026-05-19 19:20 215
}
48607848 Poul Sander 2026-05-19 19:20 216
665ac308 Poul Sander 2026-02-23 19:34 217
void MainWindow::selectPreviousFolderIfExists()
665ac308 Poul Sander 2026-02-23 19:34 218
{
665ac308 Poul Sander 2026-02-23 19:34 219
    if (m_previousFolderPath.isEmpty())
665ac308 Poul Sander 2026-02-23 19:34 220
        return;
665ac308 Poul Sander 2026-02-23 19:34 221
665ac308 Poul Sander 2026-02-23 19:34 222
    // Find the previous folder in the current directory's contents
665ac308 Poul Sander 2026-02-23 19:34 223
    for (int i = 0; i < m_imageModel->rowCount(); ++i) {
665ac308 Poul Sander 2026-02-23 19:34 224
        QModelIndex idx = m_imageModel->index(i);
665ac308 Poul Sander 2026-02-23 19:34 225
        if (m_imageModel->isFolder(idx)) {
80a9926d Poul Sander 2026-05-19 20:28 226
            QByteArray folderPath = m_imageModel->filePath(idx);
665ac308 Poul Sander 2026-02-23 19:34 227
            if (folderPath == m_previousFolderPath) {
665ac308 Poul Sander 2026-02-23 19:34 228
                m_listView->setCurrentIndex(idx);
665ac308 Poul Sander 2026-02-23 19:34 229
                m_listView->scrollTo(idx);
665ac308 Poul Sander 2026-02-23 19:34 230
                return;
665ac308 Poul Sander 2026-02-23 19:34 231
            }
665ac308 Poul Sander 2026-02-23 19:34 232
        }
665ac308 Poul Sander 2026-02-23 19:34 233
    }
665ac308 Poul Sander 2026-02-23 19:34 234
}
665ac308 Poul Sander 2026-02-23 19:34 235
032aa1e1 Poul Sander 2026-02-19 18:55 236
void MainWindow::setupConnections()
032aa1e1 Poul Sander 2026-02-19 18:55 237
{
3bee3e3e Poul Sander 2026-03-05 17:07 238
    connect(m_rootList, &QListWidget::currentItemChanged, this,
3bee3e3e Poul Sander 2026-03-05 17:07 239
            [this](QListWidgetItem *current, QListWidgetItem *) {
3bee3e3e Poul Sander 2026-03-05 17:07 240
                if (current == nullptr)
3bee3e3e Poul Sander 2026-03-05 17:07 241
                    return;
3bee3e3e Poul Sander 2026-03-05 17:07 242
3bee3e3e Poul Sander 2026-03-05 17:07 243
                QString rootPath = current->data(Qt::UserRole).toString();
fc5f2e27 Poul Sander 2026-02-22 22:06 244
                m_dirModel->setRootPath(rootPath);
40efdfde Poul Sander 2026-05-19 21:24 245
                m_treeView->setRootIndex(m_dirModel->rootIndex());
fc5f2e27 Poul Sander 2026-02-22 22:06 246
            });
fc5f2e27 Poul Sander 2026-02-22 22:06 247
032aa1e1 Poul Sander 2026-02-19 18:55 248
    connect(m_treeView, &QTreeView::clicked, this,
032aa1e1 Poul Sander 2026-02-19 18:55 249
            [this](const QModelIndex &index)
6fcd6c90 Poul Sander 2026-02-19 21:12 250
            {
40efdfde Poul Sander 2026-05-19 21:24 251
                QByteArray path = m_dirModel->filePath(index);
665ac308 Poul Sander 2026-02-23 19:34 252
                navigateToFolder(path);
6fcd6c90 Poul Sander 2026-02-19 21:12 253
            });
032aa1e1 Poul Sander 2026-02-19 18:55 254
032aa1e1 Poul Sander 2026-02-19 18:55 255
    connect(m_listView, &QListView::clicked, this,
cfe4a67b Poul Sander 2026-02-19 23:06 256
            [this](const QModelIndex &index) { showPreview(index); });
6fcd6c90 Poul Sander 2026-02-19 21:12 257
cfe4a67b Poul Sander 2026-02-19 23:06 258
    connect(m_listView->selectionModel(), &QItemSelectionModel::currentChanged,
cfe4a67b Poul Sander 2026-02-19 23:06 259
            this, [this](const QModelIndex &current) { showPreview(current); });
6fcd6c90 Poul Sander 2026-02-19 21:12 260
61136b77 Poul Sander 2026-02-20 22:45 261
    connect(m_listView, &QListView::doubleClicked, this,
09130ab9 Poul Sander 2026-02-22 22:19 262
            [this](const QModelIndex &index) {
09130ab9 Poul Sander 2026-02-22 22:19 263
                if (m_imageModel->isFolder(index)) {
80a9926d Poul Sander 2026-05-19 20:28 264
                    QByteArray path = m_imageModel->filePath(index);
665ac308 Poul Sander 2026-02-23 19:34 265
                    navigateToFolder(path);
09130ab9 Poul Sander 2026-02-22 22:19 266
                } else {
09130ab9 Poul Sander 2026-02-22 22:19 267
                    enterSingleImageMode(index);
09130ab9 Poul Sander 2026-02-22 22:19 268
                }
09130ab9 Poul Sander 2026-02-22 22:19 269
            });
61136b77 Poul Sander 2026-02-20 22:45 270
61136b77 Poul Sander 2026-02-20 22:45 271
    connect(m_imageView, &ImageViewWidget::closeRequested,
61136b77 Poul Sander 2026-02-20 22:45 272
            this, &MainWindow::leaveSingleImageMode);
beb4a823 Poul Sander 2026-05-19 21:53 273
    connect(m_imageView, &ImageViewWidget::editCaptionRequested,
beb4a823 Poul Sander 2026-05-19 21:53 274
        this, &MainWindow::onEditCaption);
1ffc1962 Poul Sander 2026-02-20 23:41 275
    connect(m_imageView, &ImageViewWidget::nextRequested, this, [this]() {
1ffc1962 Poul Sander 2026-02-20 23:41 276
        QModelIndex cur = m_listView->currentIndex();
1ffc1962 Poul Sander 2026-02-20 23:41 277
        int next = cur.isValid() ? cur.row() + 1 : 0;
09130ab9 Poul Sander 2026-02-22 22:19 278
        while (next < m_imageModel->rowCount() && m_imageModel->isFolder(m_imageModel->index(next)))
09130ab9 Poul Sander 2026-02-22 22:19 279
            ++next;
1ffc1962 Poul Sander 2026-02-20 23:41 280
        if (next < m_imageModel->rowCount())
1ffc1962 Poul Sander 2026-02-20 23:41 281
        {
1ffc1962 Poul Sander 2026-02-20 23:41 282
            QModelIndex idx = m_imageModel->index(next);
1ffc1962 Poul Sander 2026-02-20 23:41 283
            m_listView->setCurrentIndex(idx);
1ffc1962 Poul Sander 2026-02-20 23:41 284
            enterSingleImageMode(idx);
1ffc1962 Poul Sander 2026-02-20 23:41 285
        }
1ffc1962 Poul Sander 2026-02-20 23:41 286
    });
1ffc1962 Poul Sander 2026-02-20 23:41 287
1ffc1962 Poul Sander 2026-02-20 23:41 288
    connect(m_imageView, &ImageViewWidget::previousRequested, this, [this]() {
1ffc1962 Poul Sander 2026-02-20 23:41 289
        QModelIndex cur = m_listView->currentIndex();
1ffc1962 Poul Sander 2026-02-20 23:41 290
        int prev = cur.isValid() ? cur.row() - 1 : 0;
09130ab9 Poul Sander 2026-02-22 22:19 291
        while (prev >= 0 && m_imageModel->isFolder(m_imageModel->index(prev)))
09130ab9 Poul Sander 2026-02-22 22:19 292
            --prev;
1ffc1962 Poul Sander 2026-02-20 23:41 293
        if (prev >= 0)
1ffc1962 Poul Sander 2026-02-20 23:41 294
        {
1ffc1962 Poul Sander 2026-02-20 23:41 295
            QModelIndex idx = m_imageModel->index(prev);
1ffc1962 Poul Sander 2026-02-20 23:41 296
            m_listView->setCurrentIndex(idx);
1ffc1962 Poul Sander 2026-02-20 23:41 297
            enterSingleImageMode(idx);
1ffc1962 Poul Sander 2026-02-20 23:41 298
        }
1ffc1962 Poul Sander 2026-02-20 23:41 299
    });
1ffc1962 Poul Sander 2026-02-20 23:41 300
6fcd6c90 Poul Sander 2026-02-19 21:12 301
    connect(m_listView->verticalScrollBar(), &QScrollBar::valueChanged,
6fcd6c90 Poul Sander 2026-02-19 21:12 302
            this, [this]()
6fcd6c90 Poul Sander 2026-02-19 21:12 303
            { loadVisibleThumbnails(); });
6fcd6c90 Poul Sander 2026-02-19 21:12 304
6fcd6c90 Poul Sander 2026-02-19 21:12 305
    // Also catch resize — visible range changes when the viewport is resized
6fcd6c90 Poul Sander 2026-02-19 21:12 306
    m_listView->viewport()->installEventFilter(this);
6fcd6c90 Poul Sander 2026-02-19 21:12 307
}
6fcd6c90 Poul Sander 2026-02-19 21:12 308
6fcd6c90 Poul Sander 2026-02-19 21:12 309
bool MainWindow::eventFilter(QObject *obj, QEvent *event)
6fcd6c90 Poul Sander 2026-02-19 21:12 310
{
6fcd6c90 Poul Sander 2026-02-19 21:12 311
    if (obj == m_listView->viewport() && event->type() == QEvent::Resize)
6fcd6c90 Poul Sander 2026-02-19 21:12 312
        QTimer::singleShot(0, this, &MainWindow::loadVisibleThumbnails);
6fcd6c90 Poul Sander 2026-02-19 21:12 313
6fcd6c90 Poul Sander 2026-02-19 21:12 314
    return QMainWindow::eventFilter(obj, event);
6fcd6c90 Poul Sander 2026-02-19 21:12 315
}
6fcd6c90 Poul Sander 2026-02-19 21:12 316
6fcd6c90 Poul Sander 2026-02-19 21:12 317
void MainWindow::loadVisibleThumbnails()
6fcd6c90 Poul Sander 2026-02-19 21:12 318
{
6fcd6c90 Poul Sander 2026-02-19 21:12 319
    QModelIndex topLeft = m_listView->indexAt(QPoint(0, 0));
6fcd6c90 Poul Sander 2026-02-19 21:12 320
    QModelIndex bottomRight = m_listView->indexAt(
6fcd6c90 Poul Sander 2026-02-19 21:12 321
        QPoint(m_listView->viewport()->width() - 1,
6fcd6c90 Poul Sander 2026-02-19 21:12 322
               m_listView->viewport()->height() - 1));
6fcd6c90 Poul Sander 2026-02-19 21:12 323
6fcd6c90 Poul Sander 2026-02-19 21:12 324
    int first = topLeft.isValid() ? topLeft.row() : 0;
6fcd6c90 Poul Sander 2026-02-19 21:12 325
    int last = bottomRight.isValid() ? bottomRight.row()
6fcd6c90 Poul Sander 2026-02-19 21:12 326
                                     : m_imageModel->rowCount() - 1;
6fcd6c90 Poul Sander 2026-02-19 21:12 327
6fcd6c90 Poul Sander 2026-02-19 21:12 328
    if (last < 0)
6fcd6c90 Poul Sander 2026-02-19 21:12 329
        return;
6fcd6c90 Poul Sander 2026-02-19 21:12 330
6fcd6c90 Poul Sander 2026-02-19 21:12 331
    std::cout << "loadVisibleThumbnails: " << first << " - " << last << "\n";
6fcd6c90 Poul Sander 2026-02-19 21:12 332
    m_imageModel->requestThumbnails(first, last);
032aa1e1 Poul Sander 2026-02-19 18:55 333
}
cfe4a67b Poul Sander 2026-02-19 23:06 334
cfe4a67b Poul Sander 2026-02-19 23:06 335
void MainWindow::showPreview(const QModelIndex &index)
cfe4a67b Poul Sander 2026-02-19 23:06 336
{
cfe4a67b Poul Sander 2026-02-19 23:06 337
    if (!index.isValid())
cfe4a67b Poul Sander 2026-02-19 23:06 338
        return;
cfe4a67b Poul Sander 2026-02-19 23:06 339
80a9926d Poul Sander 2026-05-19 20:28 340
    QByteArray path = m_imageModel->filePath(index);
80a9926d Poul Sander 2026-05-19 20:28 341
80a9926d Poul Sander 2026-05-19 20:28 342
    int fd = ::open(path.constData(), O_RDONLY | O_CLOEXEC);
80a9926d Poul Sander 2026-05-19 20:28 343
    if (fd >= 0) {
80a9926d Poul Sander 2026-05-19 20:28 344
        QFile file;
80a9926d Poul Sander 2026-05-19 20:28 345
        if (file.open(fd, QIODevice::ReadOnly, QFileDevice::AutoCloseHandle)) {
80a9926d Poul Sander 2026-05-19 20:28 346
            QImageReader reader(&file);
80a9926d Poul Sander 2026-05-19 20:28 347
            reader.setAutoTransform(true);
80a9926d Poul Sander 2026-05-19 20:28 348
            QSize fullSize = reader.size();
80a9926d Poul Sander 2026-05-19 20:28 349
            if (fullSize.isValid()) {
80a9926d Poul Sander 2026-05-19 20:28 350
                QSize target = fullSize.scaled(m_previewLabel->size(), Qt::KeepAspectRatio);
80a9926d Poul Sander 2026-05-19 20:28 351
                reader.setScaledSize(target);
80a9926d Poul Sander 2026-05-19 20:28 352
            }
80a9926d Poul Sander 2026-05-19 20:28 353
            QImage image = reader.read();
80a9926d Poul Sander 2026-05-19 20:28 354
            m_previewLabel->setPixmap(
80a9926d Poul Sander 2026-05-19 20:28 355
                QPixmap::fromImage(image).scaled(
80a9926d Poul Sander 2026-05-19 20:28 356
                    m_previewLabel->size(),
80a9926d Poul Sander 2026-05-19 20:28 357
                    Qt::KeepAspectRatio,
80a9926d Poul Sander 2026-05-19 20:28 358
                    Qt::SmoothTransformation));
80a9926d Poul Sander 2026-05-19 20:28 359
        }
7889168c Poul Sander 2026-02-26 18:27 360
    }
53fdab93 Poul Sander 2026-05-18 18:45 361
53fdab93 Poul Sander 2026-05-18 18:45 362
    updateExifInfo(path);
53fdab93 Poul Sander 2026-05-18 18:45 363
}
53fdab93 Poul Sander 2026-05-18 18:45 364
80a9926d Poul Sander 2026-05-19 20:28 365
void MainWindow::updateExifInfo(const QByteArray &path)
53fdab93 Poul Sander 2026-05-18 18:45 366
{
80a9926d Poul Sander 2026-05-19 20:28 367
    // Check if this is a folder by attempting stat
80a9926d Poul Sander 2026-05-19 20:28 368
    struct ::stat st{};
80a9926d Poul Sander 2026-05-19 20:28 369
    if (::stat(path.constData(), &st) != 0 || S_ISDIR(st.st_mode)) {
53fdab93 Poul Sander 2026-05-18 18:45 370
        m_exifTable->setRowCount(0);
53fdab93 Poul Sander 2026-05-18 18:45 371
        return;
53fdab93 Poul Sander 2026-05-18 18:45 372
    }
53fdab93 Poul Sander 2026-05-18 18:45 373
    const ExifData data = ExifReader::read(path);
53fdab93 Poul Sander 2026-05-18 18:45 374
    const auto fields = data.toList();
53fdab93 Poul Sander 2026-05-18 18:45 375
    m_exifTable->setRowCount(fields.size());
53fdab93 Poul Sander 2026-05-18 18:45 376
    for (int i = 0; i < fields.size(); ++i) {
53fdab93 Poul Sander 2026-05-18 18:45 377
        m_exifTable->setItem(i, 0, new QTableWidgetItem(fields[i].first));
53fdab93 Poul Sander 2026-05-18 18:45 378
        m_exifTable->setItem(i, 1, new QTableWidgetItem(fields[i].second));
53fdab93 Poul Sander 2026-05-18 18:45 379
    }
53fdab93 Poul Sander 2026-05-18 18:45 380
    m_exifTable->resizeColumnToContents(0);
cfe4a67b Poul Sander 2026-02-19 23:06 381
}
61136b77 Poul Sander 2026-02-20 22:45 382
61136b77 Poul Sander 2026-02-20 22:45 383
void MainWindow::enterSingleImageMode(const QModelIndex &index)
61136b77 Poul Sander 2026-02-20 22:45 384
{
61136b77 Poul Sander 2026-02-20 22:45 385
    if (!index.isValid())
61136b77 Poul Sander 2026-02-20 22:45 386
        return;
61136b77 Poul Sander 2026-02-20 22:45 387
cbc7d22b Poul Sander 2026-02-24 19:23 388
    // Only save state and hide chrome when transitioning from browse mode
cbc7d22b Poul Sander 2026-02-24 19:23 389
    if (m_stack->currentWidget() != m_imageView) {
3bee3e3e Poul Sander 2026-03-05 17:07 390
        m_rootDockWasVisible = m_rootDock->isVisible();
cbc7d22b Poul Sander 2026-02-24 19:23 391
        m_folderDockWasVisible = m_folderDock->isVisible();
cbc7d22b Poul Sander 2026-02-24 19:23 392
        m_previewDockWasVisible = m_previewDock->isVisible();
53fdab93 Poul Sander 2026-05-18 18:45 393
        m_exifDockWasVisible = m_exifDock->isVisible();
3bee3e3e Poul Sander 2026-03-05 17:07 394
        m_rootDock->hide();
cbc7d22b Poul Sander 2026-02-24 19:23 395
        m_folderDock->hide();
cbc7d22b Poul Sander 2026-02-24 19:23 396
        m_previewDock->hide();
53fdab93 Poul Sander 2026-05-18 18:45 397
        m_exifDock->hide();
cbc7d22b Poul Sander 2026-02-24 19:23 398
        m_menuBar->hide();
cbc7d22b Poul Sander 2026-02-24 19:23 399
    }
cbc7d22b Poul Sander 2026-02-24 19:23 400
f61bf886 Poul Sander 2026-02-23 19:27 401
    m_imageView->setBackgroundColor(m_backgroundColorPreference);
80a9926d Poul Sander 2026-05-19 20:28 402
    QByteArray path = m_imageModel->filePath(index);
58103277 Poul Sander 2026-03-01 22:53 403
    m_imageView->setNeighborPaths(computeNeighborPaths(index));
61136b77 Poul Sander 2026-02-20 22:45 404
    m_imageView->setImage(path);
53fdab93 Poul Sander 2026-05-18 18:45 405
    m_imageView->setExifData(ExifReader::read(path));
53fdab93 Poul Sander 2026-05-18 18:45 406
    updateExifInfo(path);
61136b77 Poul Sander 2026-02-20 22:45 407
    m_stack->setCurrentWidget(m_imageView);
61136b77 Poul Sander 2026-02-20 22:45 408
    m_imageView->setFocus();
ccb726b8 Poul Sander 2026-02-23 22:43 409
ccb726b8 Poul Sander 2026-02-23 22:43 410
    if (m_imageViewWasFullScreen) {
ccb726b8 Poul Sander 2026-02-23 22:43 411
        setFullScreenMode(true);
ccb726b8 Poul Sander 2026-02-23 22:43 412
        m_imageViewWasFullScreen = false;
ccb726b8 Poul Sander 2026-02-23 22:43 413
    }
61136b77 Poul Sander 2026-02-20 22:45 414
}
61136b77 Poul Sander 2026-02-20 22:45 415
beb4a823 Poul Sander 2026-05-19 21:53 416
void MainWindow::onEditCaption()
beb4a823 Poul Sander 2026-05-19 21:53 417
{
beb4a823 Poul Sander 2026-05-19 21:53 418
    const QByteArray path = m_imageView->currentPath();
beb4a823 Poul Sander 2026-05-19 21:53 419
    if (path.isEmpty())
beb4a823 Poul Sander 2026-05-19 21:53 420
        return;
beb4a823 Poul Sander 2026-05-19 21:53 421
beb4a823 Poul Sander 2026-05-19 21:53 422
    ExifData data = ExifReader::read(path);
beb4a823 Poul Sander 2026-05-19 21:53 423
beb4a823 Poul Sander 2026-05-19 21:53 424
    QDialog dlg(this);
beb4a823 Poul Sander 2026-05-19 21:53 425
    dlg.setWindowTitle(tr("Edit Caption"));
beb4a823 Poul Sander 2026-05-19 21:53 426
    auto *layout = new QVBoxLayout(&dlg);
beb4a823 Poul Sander 2026-05-19 21:53 427
    layout->addWidget(new QLabel(tr("Caption-Abstract:"), &dlg));
beb4a823 Poul Sander 2026-05-19 21:53 428
    auto *edit = new QPlainTextEdit(&dlg);
beb4a823 Poul Sander 2026-05-19 21:53 429
    edit->setPlainText(data.captionAbstract);
beb4a823 Poul Sander 2026-05-19 21:53 430
    edit->setMinimumWidth(400);
beb4a823 Poul Sander 2026-05-19 21:53 431
    layout->addWidget(edit);
beb4a823 Poul Sander 2026-05-19 21:53 432
    auto *buttons = new QDialogButtonBox(
beb4a823 Poul Sander 2026-05-19 21:53 433
        QDialogButtonBox::Ok | QDialogButtonBox::Cancel, &dlg);
beb4a823 Poul Sander 2026-05-19 21:53 434
    layout->addWidget(buttons);
beb4a823 Poul Sander 2026-05-19 21:53 435
    connect(buttons, &QDialogButtonBox::accepted, &dlg, &QDialog::accept);
beb4a823 Poul Sander 2026-05-19 21:53 436
    connect(buttons, &QDialogButtonBox::rejected, &dlg, &QDialog::reject);
beb4a823 Poul Sander 2026-05-19 21:53 437
beb4a823 Poul Sander 2026-05-19 21:53 438
    if (dlg.exec() != QDialog::Accepted)
beb4a823 Poul Sander 2026-05-19 21:53 439
        return;
beb4a823 Poul Sander 2026-05-19 21:53 440
beb4a823 Poul Sander 2026-05-19 21:53 441
    const QString newCaption = edit->toPlainText();
beb4a823 Poul Sander 2026-05-19 21:53 442
    if (ExifReader::saveCaption(path, newCaption, data)) {
beb4a823 Poul Sander 2026-05-19 21:53 443
        ExifData updated = ExifReader::read(path);
beb4a823 Poul Sander 2026-05-19 21:53 444
        m_imageView->setExifData(updated);
beb4a823 Poul Sander 2026-05-19 21:53 445
        updateExifInfo(path);
beb4a823 Poul Sander 2026-05-19 21:53 446
    }
beb4a823 Poul Sander 2026-05-19 21:53 447
}
beb4a823 Poul Sander 2026-05-19 21:53 448
61136b77 Poul Sander 2026-02-20 22:45 449
void MainWindow::leaveSingleImageMode()
61136b77 Poul Sander 2026-02-20 22:45 450
{
3968465e Poul Sander 2026-02-23 19:13 451
    if (isFullScreen()) {
ccb726b8 Poul Sander 2026-02-23 22:43 452
        m_imageViewWasFullScreen = true;
ccb726b8 Poul Sander 2026-02-23 22:43 453
        setFullScreenMode(false);
61136b77 Poul Sander 2026-02-20 22:45 454
    }
58103277 Poul Sander 2026-03-01 22:53 455
    m_imageView->clearCache();
61136b77 Poul Sander 2026-02-20 22:45 456
    m_stack->setCurrentWidget(m_browserPage);
cbc7d22b Poul Sander 2026-02-24 19:23 457
    m_menuBar->show();
3bee3e3e Poul Sander 2026-03-05 17:07 458
    if (m_rootDockWasVisible)
3bee3e3e Poul Sander 2026-03-05 17:07 459
        m_rootDock->show();
cbc7d22b Poul Sander 2026-02-24 19:23 460
    if (m_folderDockWasVisible)
cbc7d22b Poul Sander 2026-02-24 19:23 461
        m_folderDock->show();
cbc7d22b Poul Sander 2026-02-24 19:23 462
    if (m_previewDockWasVisible)
cbc7d22b Poul Sander 2026-02-24 19:23 463
        m_previewDock->show();
53fdab93 Poul Sander 2026-05-18 18:45 464
    if (m_exifDockWasVisible)
53fdab93 Poul Sander 2026-05-18 18:45 465
        m_exifDock->show();
61136b77 Poul Sander 2026-02-20 22:45 466
    m_listView->setFocus();
61136b77 Poul Sander 2026-02-20 22:45 467
}
61136b77 Poul Sander 2026-02-20 22:45 468
80a9926d Poul Sander 2026-05-19 20:28 469
QList<QByteArray> MainWindow::computeNeighborPaths(const QModelIndex &index) const
58103277 Poul Sander 2026-03-01 22:53 470
{
80a9926d Poul Sander 2026-05-19 20:28 471
    QList<QByteArray> paths;
58103277 Poul Sander 2026-03-01 22:53 472
    int row = index.row();
58103277 Poul Sander 2026-03-01 22:53 473
58103277 Poul Sander 2026-03-01 22:53 474
    // Collect up to CacheKeepBehind non-folder images before current
58103277 Poul Sander 2026-03-01 22:53 475
    int count = 0;
58103277 Poul Sander 2026-03-01 22:53 476
    for (int i = row - 1; i >= 0 && count < ImageViewWidget::CacheKeepBehind; --i) {
58103277 Poul Sander 2026-03-01 22:53 477
        QModelIndex idx = m_imageModel->index(i);
58103277 Poul Sander 2026-03-01 22:53 478
        if (!m_imageModel->isFolder(idx)) {
58103277 Poul Sander 2026-03-01 22:53 479
            paths.prepend(m_imageModel->filePath(idx));
58103277 Poul Sander 2026-03-01 22:53 480
            ++count;
58103277 Poul Sander 2026-03-01 22:53 481
        }
58103277 Poul Sander 2026-03-01 22:53 482
    }
58103277 Poul Sander 2026-03-01 22:53 483
58103277 Poul Sander 2026-03-01 22:53 484
    // Collect up to CacheReadAhead non-folder images after current
58103277 Poul Sander 2026-03-01 22:53 485
    count = 0;
58103277 Poul Sander 2026-03-01 22:53 486
    for (int i = row + 1; i < m_imageModel->rowCount() && count < ImageViewWidget::CacheReadAhead; ++i) {
58103277 Poul Sander 2026-03-01 22:53 487
        QModelIndex idx = m_imageModel->index(i);
58103277 Poul Sander 2026-03-01 22:53 488
        if (!m_imageModel->isFolder(idx)) {
58103277 Poul Sander 2026-03-01 22:53 489
            paths.append(m_imageModel->filePath(idx));
58103277 Poul Sander 2026-03-01 22:53 490
            ++count;
58103277 Poul Sander 2026-03-01 22:53 491
        }
58103277 Poul Sander 2026-03-01 22:53 492
    }
58103277 Poul Sander 2026-03-01 22:53 493
58103277 Poul Sander 2026-03-01 22:53 494
    return paths;
58103277 Poul Sander 2026-03-01 22:53 495
}
58103277 Poul Sander 2026-03-01 22:53 496
ccb726b8 Poul Sander 2026-02-23 22:43 497
void MainWindow::setFullScreenMode(bool fullScreen)
ccb726b8 Poul Sander 2026-02-23 22:43 498
{
ccb726b8 Poul Sander 2026-02-23 22:43 499
    if (fullScreen == isFullScreen())
ccb726b8 Poul Sander 2026-02-23 22:43 500
        return;
ccb726b8 Poul Sander 2026-02-23 22:43 501
ccb726b8 Poul Sander 2026-02-23 22:43 502
    if (fullScreen) {
ccb726b8 Poul Sander 2026-02-23 22:43 503
        m_wasMaximized = isMaximized();
ccb726b8 Poul Sander 2026-02-23 22:43 504
        m_menuBar->hide();
ccb726b8 Poul Sander 2026-02-23 22:43 505
        showFullScreen();
ccb726b8 Poul Sander 2026-02-23 22:43 506
    } else {
cbc7d22b Poul Sander 2026-02-24 19:23 507
        // Only restore menu bar when returning to browse mode
cbc7d22b Poul Sander 2026-02-24 19:23 508
        if (m_stack->currentWidget() != m_imageView)
cbc7d22b Poul Sander 2026-02-24 19:23 509
            m_menuBar->show();
ccb726b8 Poul Sander 2026-02-23 22:43 510
        if (m_wasMaximized)
ccb726b8 Poul Sander 2026-02-23 22:43 511
            showMaximized();
ccb726b8 Poul Sander 2026-02-23 22:43 512
        else
ccb726b8 Poul Sander 2026-02-23 22:43 513
            showNormal();
ccb726b8 Poul Sander 2026-02-23 22:43 514
    }
ccb726b8 Poul Sander 2026-02-23 22:43 515
}
ccb726b8 Poul Sander 2026-02-23 22:43 516
61136b77 Poul Sander 2026-02-20 22:45 517
void MainWindow::keyPressEvent(QKeyEvent *event)
61136b77 Poul Sander 2026-02-20 22:45 518
{
61136b77 Poul Sander 2026-02-20 22:45 519
    if (event->key() == Qt::Key_F11 && m_stack->currentWidget() == m_imageView)
61136b77 Poul Sander 2026-02-20 22:45 520
    {
ccb726b8 Poul Sander 2026-02-23 22:43 521
        setFullScreenMode(!isFullScreen());
61136b77 Poul Sander 2026-02-20 22:45 522
        event->accept();
61136b77 Poul Sander 2026-02-20 22:45 523
        return;
61136b77 Poul Sander 2026-02-20 22:45 524
    }
61136b77 Poul Sander 2026-02-20 22:45 525
61136b77 Poul Sander 2026-02-20 22:45 526
    if (event->key() == Qt::Key_Return && m_stack->currentWidget() == m_browserPage)
61136b77 Poul Sander 2026-02-20 22:45 527
    {
61136b77 Poul Sander 2026-02-20 22:45 528
        QModelIndex current = m_listView->currentIndex();
61136b77 Poul Sander 2026-02-20 22:45 529
        if (current.isValid())
61136b77 Poul Sander 2026-02-20 22:45 530
        {
09130ab9 Poul Sander 2026-02-22 22:19 531
            if (m_imageModel->isFolder(current)) {
80a9926d Poul Sander 2026-05-19 20:28 532
                QByteArray path = m_imageModel->filePath(current);
665ac308 Poul Sander 2026-02-23 19:34 533
                navigateToFolder(path);
09130ab9 Poul Sander 2026-02-22 22:19 534
            } else {
09130ab9 Poul Sander 2026-02-22 22:19 535
                enterSingleImageMode(current);
09130ab9 Poul Sander 2026-02-22 22:19 536
            }
61136b77 Poul Sander 2026-02-20 22:45 537
            event->accept();
61136b77 Poul Sander 2026-02-20 22:45 538
            return;
61136b77 Poul Sander 2026-02-20 22:45 539
        }
61136b77 Poul Sander 2026-02-20 22:45 540
    }
61136b77 Poul Sander 2026-02-20 22:45 541
61136b77 Poul Sander 2026-02-20 22:45 542
    QMainWindow::keyPressEvent(event);
61136b77 Poul Sander 2026-02-20 22:45 543
}
c9e5684a Poul Sander 2026-02-23 19:18 544
c9e5684a Poul Sander 2026-02-23 19:18 545
void MainWindow::closeEvent(QCloseEvent *event)
c9e5684a Poul Sander 2026-02-23 19:18 546
{
c9e5684a Poul Sander 2026-02-23 19:18 547
    if (m_stack->currentWidget() == m_imageView)
c9e5684a Poul Sander 2026-02-23 19:18 548
    {
c9e5684a Poul Sander 2026-02-23 19:18 549
        leaveSingleImageMode();
c9e5684a Poul Sander 2026-02-23 19:18 550
        event->ignore();
c9e5684a Poul Sander 2026-02-23 19:18 551
        return;
c9e5684a Poul Sander 2026-02-23 19:18 552
    }
c9e5684a Poul Sander 2026-02-23 19:18 553
f61bf886 Poul Sander 2026-02-23 19:27 554
    savePreferences();
c9e5684a Poul Sander 2026-02-23 19:18 555
    QMainWindow::closeEvent(event);
c9e5684a Poul Sander 2026-02-23 19:18 556
}
f61bf886 Poul Sander 2026-02-23 19:27 557
4ae57f60 Poul Sander 2026-02-25 21:35 558
void MainWindow::applyDockLocking(bool lock)
4ae57f60 Poul Sander 2026-02-25 21:35 559
{
4ae57f60 Poul Sander 2026-02-25 21:35 560
    const QDockWidget::DockWidgetFeatures movableFeatures =
4ae57f60 Poul Sander 2026-02-25 21:35 561
        QDockWidget::DockWidgetMovable | QDockWidget::DockWidgetFloatable;
4ae57f60 Poul Sander 2026-02-25 21:35 562
    const QDockWidget::DockWidgetFeatures baseFeatures =
4ae57f60 Poul Sander 2026-02-25 21:35 563
        QDockWidget::DockWidgetClosable;
4ae57f60 Poul Sander 2026-02-25 21:35 564
4ae57f60 Poul Sander 2026-02-25 21:35 565
    QDockWidget::DockWidgetFeatures features = lock ? baseFeatures
4ae57f60 Poul Sander 2026-02-25 21:35 566
                                                    : (baseFeatures | movableFeatures);
3bee3e3e Poul Sander 2026-03-05 17:07 567
    m_rootDock->setFeatures(features);
4ae57f60 Poul Sander 2026-02-25 21:35 568
    m_folderDock->setFeatures(features);
4ae57f60 Poul Sander 2026-02-25 21:35 569
    m_previewDock->setFeatures(features);
53fdab93 Poul Sander 2026-05-18 18:45 570
    m_exifDock->setFeatures(features);
4ae57f60 Poul Sander 2026-02-25 21:35 571
}
4ae57f60 Poul Sander 2026-02-25 21:35 572
f61bf886 Poul Sander 2026-02-23 19:27 573
void MainWindow::loadPreferences()
f61bf886 Poul Sander 2026-02-23 19:27 574
{
f61bf886 Poul Sander 2026-02-23 19:27 575
    QSettings settings("SagoImageBrowser", "SagoImageBrowser");
f61bf886 Poul Sander 2026-02-23 19:27 576
    m_backgroundColorPreference = settings.value("backgroundColor", "black").toString();
f61bf886 Poul Sander 2026-02-23 19:27 577
    m_imageView->setBackgroundColor(m_backgroundColorPreference);
4ae57f60 Poul Sander 2026-02-25 21:35 578
    m_lockDockingPreference = settings.value("lockDocking", false).toBool();
4ae57f60 Poul Sander 2026-02-25 21:35 579
    applyDockLocking(m_lockDockingPreference);
459bad0f Poul Sander 2026-05-17 14:11 580
    m_folderCacheSizePreference = settings.value("folderCacheSize", 4).toInt();
459bad0f Poul Sander 2026-05-17 14:11 581
    m_imageModel->setCacheMaxSize(m_folderCacheSizePreference);
ac476ca8 Poul Sander 2026-05-18 16:47 582
    m_thumbnailCacheSizePreference = settings.value("thumbnailCacheSize", "normal").toString();
ac476ca8 Poul Sander 2026-05-18 16:47 583
    m_imageModel->setThumbnailSize(thumbnailSizeFromString(m_thumbnailCacheSizePreference));
cbc7d22b Poul Sander 2026-02-24 19:23 584
    if (settings.contains("windowGeometry"))
cbc7d22b Poul Sander 2026-02-24 19:23 585
        restoreGeometry(settings.value("windowGeometry").toByteArray());
cbc7d22b Poul Sander 2026-02-24 19:23 586
    if (settings.contains("windowState"))
cbc7d22b Poul Sander 2026-02-24 19:23 587
        restoreState(settings.value("windowState").toByteArray());
f61bf886 Poul Sander 2026-02-23 19:27 588
}
f61bf886 Poul Sander 2026-02-23 19:27 589
f61bf886 Poul Sander 2026-02-23 19:27 590
void MainWindow::savePreferences()
f61bf886 Poul Sander 2026-02-23 19:27 591
{
f61bf886 Poul Sander 2026-02-23 19:27 592
    QSettings settings("SagoImageBrowser", "SagoImageBrowser");
f61bf886 Poul Sander 2026-02-23 19:27 593
    settings.setValue("backgroundColor", m_backgroundColorPreference);
4ae57f60 Poul Sander 2026-02-25 21:35 594
    settings.setValue("lockDocking", m_lockDockingPreference);
459bad0f Poul Sander 2026-05-17 14:11 595
    settings.setValue("folderCacheSize", m_folderCacheSizePreference);
ac476ca8 Poul Sander 2026-05-18 16:47 596
    settings.setValue("thumbnailCacheSize", m_thumbnailCacheSizePreference);
cbc7d22b Poul Sander 2026-02-24 19:23 597
    settings.setValue("windowGeometry", saveGeometry());
cbc7d22b Poul Sander 2026-02-24 19:23 598
    settings.setValue("windowState", saveState());
f61bf886 Poul Sander 2026-02-23 19:27 599
}
f61bf886 Poul Sander 2026-02-23 19:27 600
f61bf886 Poul Sander 2026-02-23 19:27 601
void MainWindow::onPreferencesTriggered()
f61bf886 Poul Sander 2026-02-23 19:27 602
{
f61bf886 Poul Sander 2026-02-23 19:27 603
    PreferencesDialog dialog(this);
f61bf886 Poul Sander 2026-02-23 19:27 604
    dialog.setBackgroundColor(m_backgroundColorPreference);
4ae57f60 Poul Sander 2026-02-25 21:35 605
    dialog.setLockDocking(m_lockDockingPreference);
459bad0f Poul Sander 2026-05-17 14:11 606
    dialog.setCacheSize(m_folderCacheSizePreference);
ac476ca8 Poul Sander 2026-05-18 16:47 607
    dialog.setThumbnailCacheSize(m_thumbnailCacheSizePreference);
f61bf886 Poul Sander 2026-02-23 19:27 608
7370f1c0 Poul Sander 2026-03-01 15:15 609
    bool shouldResetLayout = false;
7370f1c0 Poul Sander 2026-03-01 15:15 610
    connect(&dialog, &PreferencesDialog::resetLayoutRequested, this, [&shouldResetLayout]() {
7370f1c0 Poul Sander 2026-03-01 15:15 611
        shouldResetLayout = true;
7370f1c0 Poul Sander 2026-03-01 15:15 612
    });
7370f1c0 Poul Sander 2026-03-01 15:15 613
f61bf886 Poul Sander 2026-02-23 19:27 614
    if (dialog.exec() == QDialog::Accepted)
f61bf886 Poul Sander 2026-02-23 19:27 615
    {
f61bf886 Poul Sander 2026-02-23 19:27 616
        m_backgroundColorPreference = dialog.getBackgroundColor();
f61bf886 Poul Sander 2026-02-23 19:27 617
        m_imageView->setBackgroundColor(m_backgroundColorPreference);
4ae57f60 Poul Sander 2026-02-25 21:35 618
        m_lockDockingPreference = dialog.getLockDocking();
4ae57f60 Poul Sander 2026-02-25 21:35 619
        applyDockLocking(m_lockDockingPreference);
459bad0f Poul Sander 2026-05-17 14:11 620
        m_folderCacheSizePreference = dialog.getCacheSize();
459bad0f Poul Sander 2026-05-17 14:11 621
        m_imageModel->setCacheMaxSize(m_folderCacheSizePreference);
ac476ca8 Poul Sander 2026-05-18 16:47 622
        QString newThumbSize = dialog.getThumbnailCacheSize();
ac476ca8 Poul Sander 2026-05-18 16:47 623
        if (newThumbSize != m_thumbnailCacheSizePreference)
ac476ca8 Poul Sander 2026-05-18 16:47 624
        {
ac476ca8 Poul Sander 2026-05-18 16:47 625
            m_thumbnailCacheSizePreference = newThumbSize;
ac476ca8 Poul Sander 2026-05-18 16:47 626
            m_imageModel->setThumbnailSize(thumbnailSizeFromString(m_thumbnailCacheSizePreference));
ac476ca8 Poul Sander 2026-05-18 16:47 627
            loadVisibleThumbnails();
ac476ca8 Poul Sander 2026-05-18 16:47 628
        }
f61bf886 Poul Sander 2026-02-23 19:27 629
        savePreferences();
f61bf886 Poul Sander 2026-02-23 19:27 630
    }
7370f1c0 Poul Sander 2026-03-01 15:15 631
    
7370f1c0 Poul Sander 2026-03-01 15:15 632
    // Apply layout reset immediately after dialog closes if requested
7370f1c0 Poul Sander 2026-03-01 15:15 633
    if (shouldResetLayout) {
7370f1c0 Poul Sander 2026-03-01 15:15 634
        resetLayoutToDefault();
7370f1c0 Poul Sander 2026-03-01 15:15 635
    }
7370f1c0 Poul Sander 2026-03-01 15:15 636
}
7370f1c0 Poul Sander 2026-03-01 15:15 637
7370f1c0 Poul Sander 2026-03-01 15:15 638
void MainWindow::resetLayoutToDefault()
7370f1c0 Poul Sander 2026-03-01 15:15 639
{
7370f1c0 Poul Sander 2026-03-01 15:15 640
    QSettings settings("SagoImageBrowser", "SagoImageBrowser");
7370f1c0 Poul Sander 2026-03-01 15:15 641
    settings.remove("windowGeometry");
7370f1c0 Poul Sander 2026-03-01 15:15 642
    settings.remove("windowState");
7370f1c0 Poul Sander 2026-03-01 15:15 643
    
7370f1c0 Poul Sander 2026-03-01 15:15 644
    // Hide and detach docks completely
3bee3e3e Poul Sander 2026-03-05 17:07 645
    m_rootDock->hide();
7370f1c0 Poul Sander 2026-03-01 15:15 646
    m_folderDock->hide();
7370f1c0 Poul Sander 2026-03-01 15:15 647
    m_previewDock->hide();
7370f1c0 Poul Sander 2026-03-01 15:15 648
    
3bee3e3e Poul Sander 2026-03-05 17:07 649
    m_rootDock->setFloating(false);
7370f1c0 Poul Sander 2026-03-01 15:15 650
    m_folderDock->setFloating(false);
7370f1c0 Poul Sander 2026-03-01 15:15 651
    m_previewDock->setFloating(false);
7370f1c0 Poul Sander 2026-03-01 15:15 652
    
3bee3e3e Poul Sander 2026-03-05 17:07 653
    removeDockWidget(m_rootDock);
7370f1c0 Poul Sander 2026-03-01 15:15 654
    removeDockWidget(m_folderDock);
7370f1c0 Poul Sander 2026-03-01 15:15 655
    removeDockWidget(m_previewDock);
7370f1c0 Poul Sander 2026-03-01 15:15 656
    
7370f1c0 Poul Sander 2026-03-01 15:15 657
    // Re-add in default positions
3bee3e3e Poul Sander 2026-03-05 17:07 658
    addDockWidget(Qt::LeftDockWidgetArea, m_rootDock);
7370f1c0 Poul Sander 2026-03-01 15:15 659
    addDockWidget(Qt::LeftDockWidgetArea, m_folderDock);
7370f1c0 Poul Sander 2026-03-01 15:15 660
    addDockWidget(Qt::RightDockWidgetArea, m_previewDock);
3bee3e3e Poul Sander 2026-03-05 17:07 661
    splitDockWidget(m_rootDock, m_folderDock, Qt::Vertical);
7370f1c0 Poul Sander 2026-03-01 15:15 662
    
3bee3e3e Poul Sander 2026-03-05 17:07 663
    // Show all docks
3bee3e3e Poul Sander 2026-03-05 17:07 664
    m_rootDock->show();
7370f1c0 Poul Sander 2026-03-01 15:15 665
    m_folderDock->show();
7370f1c0 Poul Sander 2026-03-01 15:15 666
    m_previewDock->show();
f61bf886 Poul Sander 2026-02-23 19:27 667
}
1970-01-01 00:00 668