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"
83f0e0bc Poul Sander 2026-05-30 17:59 5
#include "shortcutmanager.h"
ac476ca8 Poul Sander 2026-05-18 16:47 6
#include "thumbnailcache.h"
53fdab93 Poul Sander 2026-05-18 18:45 7
#include "exifreader.h"
40efdfde Poul Sander 2026-05-19 21:24 8
#include "fsdirmodel.h"
fd7f51cd Poul Sander 2026-05-22 15:30 9
#include "pathcompletermodel.h"
1dcea855 Poul Sander 2026-05-26 21:48 10
#include "thumbnaildelegate.h"
032aa1e1 Poul Sander 2026-02-19 18:55 11
40efdfde Poul Sander 2026-05-19 21:24 12
#include <QTreeView>
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>
cfe4a67b Poul Sander 2026-02-19 23:06 23
#include <QItemSelectionModel>
cbc7d22b Poul Sander 2026-02-24 19:23 24
#include <QDockWidget>
f61bf886 Poul Sander 2026-02-23 19:27 25
#include <QMenuBar>
f61bf886 Poul Sander 2026-02-23 19:27 26
#include <QMenu>
f61bf886 Poul Sander 2026-02-23 19:27 27
#include <QAction>
f61bf886 Poul Sander 2026-02-23 19:27 28
#include <QSettings>
53fdab93 Poul Sander 2026-05-18 18:45 29
#include <QTableWidget>
beb4a823 Poul Sander 2026-05-19 21:53 30
#include <QDialog>
fd7f51cd Poul Sander 2026-05-22 15:30 31
#include <QHBoxLayout>
beb4a823 Poul Sander 2026-05-19 21:53 32
#include <QPlainTextEdit>
835a6bea Poul Sander 2026-05-19 22:03 33
#include <QMessageBox>
fd7f51cd Poul Sander 2026-05-22 15:30 34
#include <QLineEdit>
fd7f51cd Poul Sander 2026-05-22 15:30 35
#include <QPushButton>
fd7f51cd Poul Sander 2026-05-22 15:30 36
#include <QCompleter>
6fcd6c90 Poul Sander 2026-02-19 21:12 37
#include <iostream>
032aa1e1 Poul Sander 2026-02-19 18:55 38
80a9926d Poul Sander 2026-05-19 20:28 39
#include <fcntl.h>
bf525669 Poul Sander 2026-05-30 23:18 40
#include <filesystem>
80a9926d Poul Sander 2026-05-19 20:28 41
#include <sys/stat.h>
80a9926d Poul Sander 2026-05-19 20:28 42
#include <unistd.h>
80a9926d Poul Sander 2026-05-19 20:28 43
ac476ca8 Poul Sander 2026-05-18 16:47 44
namespace
ac476ca8 Poul Sander 2026-05-18 16:47 45
{
ac476ca8 Poul Sander 2026-05-18 16:47 46
ThumbnailCache::Size thumbnailSizeFromString(const QString &s)
ac476ca8 Poul Sander 2026-05-18 16:47 47
{
ac476ca8 Poul Sander 2026-05-18 16:47 48
    return (s == "large") ? ThumbnailCache::Size::Large
ac476ca8 Poul Sander 2026-05-18 16:47 49
                          : ThumbnailCache::Size::Normal;
ac476ca8 Poul Sander 2026-05-18 16:47 50
}
ac476ca8 Poul Sander 2026-05-18 16:47 51
} // namespace
ac476ca8 Poul Sander 2026-05-18 16:47 52
032aa1e1 Poul Sander 2026-02-19 18:55 53
MainWindow::MainWindow(QWidget *parent)
032aa1e1 Poul Sander 2026-02-19 18:55 54
    : QMainWindow(parent)
032aa1e1 Poul Sander 2026-02-19 18:55 55
{
032aa1e1 Poul Sander 2026-02-19 18:55 56
    setupUi();
f61bf886 Poul Sander 2026-02-23 19:27 57
    setupMenuBar();
032aa1e1 Poul Sander 2026-02-19 18:55 58
    setupConnections();
f61bf886 Poul Sander 2026-02-23 19:27 59
    loadPreferences();
032aa1e1 Poul Sander 2026-02-19 18:55 60
}
032aa1e1 Poul Sander 2026-02-19 18:55 61
032aa1e1 Poul Sander 2026-02-19 18:55 62
void MainWindow::setupUi()
032aa1e1 Poul Sander 2026-02-19 18:55 63
{
40efdfde Poul Sander 2026-05-19 21:24 64
    // Folder model (left pane) — backed by std::filesystem so directories with
40efdfde Poul Sander 2026-05-19 21:24 65
    // non-UTF-8 filenames are listed correctly.
40efdfde Poul Sander 2026-05-19 21:24 66
    m_dirModel = new FsDirModel(this);
032aa1e1 Poul Sander 2026-02-19 18:55 67
    m_dirModel->setRootPath(QDir::homePath());
032aa1e1 Poul Sander 2026-02-19 18:55 68
3bee3e3e Poul Sander 2026-03-05 17:07 69
    // Root chooser list
3bee3e3e Poul Sander 2026-03-05 17:07 70
    m_rootList = new QListWidget;
3bee3e3e Poul Sander 2026-03-05 17:07 71
    m_rootList->setSelectionMode(QAbstractItemView::SingleSelection);
3bee3e3e Poul Sander 2026-03-05 17:07 72
3bee3e3e Poul Sander 2026-03-05 17:07 73
    auto addRootItem = [this](const QString &label, const QString &path) {
3bee3e3e Poul Sander 2026-03-05 17:07 74
        QListWidgetItem *item = new QListWidgetItem(label, m_rootList);
3bee3e3e Poul Sander 2026-03-05 17:07 75
        item->setData(Qt::UserRole, path);
3bee3e3e Poul Sander 2026-03-05 17:07 76
    };
4c41f5ff Poul Sander 2026-05-31 00:01 77
    addRootItem(tr("Home"), QDir::homePath());
4c41f5ff Poul Sander 2026-05-31 00:01 78
    addRootItem(tr("Pictures"), QStandardPaths::writableLocation(QStandardPaths::PicturesLocation));
4c41f5ff Poul Sander 2026-05-31 00:01 79
    addRootItem(tr("File System"), QDir::rootPath());
3bee3e3e Poul Sander 2026-03-05 17:07 80
    m_rootList->setCurrentRow(0);
fc5f2e27 Poul Sander 2026-02-22 22:06 81
032aa1e1 Poul Sander 2026-02-19 18:55 82
    m_treeView = new QTreeView;
032aa1e1 Poul Sander 2026-02-19 18:55 83
    m_treeView->setModel(m_dirModel);
40efdfde Poul Sander 2026-05-19 21:24 84
    m_treeView->setRootIndex(m_dirModel->rootIndex());
032aa1e1 Poul Sander 2026-02-19 18:55 85
    m_treeView->header()->hide();
032aa1e1 Poul Sander 2026-02-19 18:55 86
    m_treeView->setColumnHidden(1, true);
032aa1e1 Poul Sander 2026-02-19 18:55 87
    m_treeView->setColumnHidden(2, true);
032aa1e1 Poul Sander 2026-02-19 18:55 88
    m_treeView->setColumnHidden(3, true);
032aa1e1 Poul Sander 2026-02-19 18:55 89
032aa1e1 Poul Sander 2026-02-19 18:55 90
    // Thumbnail model (center pane)
032aa1e1 Poul Sander 2026-02-19 18:55 91
    m_imageModel = new ImageModel(this);
032aa1e1 Poul Sander 2026-02-19 18:55 92
032aa1e1 Poul Sander 2026-02-19 18:55 93
    m_listView = new QListView;
032aa1e1 Poul Sander 2026-02-19 18:55 94
    m_listView->setModel(m_imageModel);
032aa1e1 Poul Sander 2026-02-19 18:55 95
    m_listView->setViewMode(QListView::IconMode);
032aa1e1 Poul Sander 2026-02-19 18:55 96
    m_listView->setIconSize(QSize(128, 128));
b614a581 Poul Sander 2026-02-19 21:18 97
    m_listView->setGridSize(QSize(148, 168));
032aa1e1 Poul Sander 2026-02-19 18:55 98
    m_listView->setResizeMode(QListView::Adjust);
032aa1e1 Poul Sander 2026-02-19 18:55 99
    m_listView->setSelectionMode(QAbstractItemView::SingleSelection);
b614a581 Poul Sander 2026-02-19 21:18 100
    m_listView->setSpacing(4);
b614a581 Poul Sander 2026-02-19 21:18 101
    m_listView->setWordWrap(true);
b614a581 Poul Sander 2026-02-19 21:18 102
    m_listView->setTextElideMode(Qt::ElideRight);
1dcea855 Poul Sander 2026-05-26 21:48 103
    m_listView->setItemDelegate(new ThumbnailDelegate(this));
032aa1e1 Poul Sander 2026-02-19 18:55 104
032aa1e1 Poul Sander 2026-02-19 18:55 105
    // Preview pane (right)
032aa1e1 Poul Sander 2026-02-19 18:55 106
    m_previewLabel = new QLabel;
032aa1e1 Poul Sander 2026-02-19 18:55 107
    m_previewLabel->setAlignment(Qt::AlignCenter);
cbc7d22b Poul Sander 2026-02-24 19:23 108
    m_previewLabel->setMinimumWidth(200);
61136b77 Poul Sander 2026-02-20 22:45 109
61136b77 Poul Sander 2026-02-20 22:45 110
    // Single image view
61136b77 Poul Sander 2026-02-20 22:45 111
    m_imageView = new ImageViewWidget;
61136b77 Poul Sander 2026-02-20 22:45 112
fd7f51cd Poul Sander 2026-05-22 15:30 113
    // Path bar: editable absolute path with ajax-style folder completion and a Go button
fd7f51cd Poul Sander 2026-05-22 15:30 114
    m_pathEdit = new QLineEdit;
fd7f51cd Poul Sander 2026-05-22 15:30 115
    m_goButton = new QPushButton(tr("Go"));
bf525669 Poul Sander 2026-05-30 23:18 116
    m_prevFolderButton = new QPushButton(tr("Prev"));
bf525669 Poul Sander 2026-05-30 23:18 117
    m_nextFolderButton = new QPushButton(tr("Next"));
fd7f51cd Poul Sander 2026-05-22 15:30 118
    m_pathCompleterModel = new PathCompleterModel(this);
fd7f51cd Poul Sander 2026-05-22 15:30 119
    m_pathCompleter = new QCompleter(m_pathCompleterModel, this);
fd7f51cd Poul Sander 2026-05-22 15:30 120
    m_pathCompleter->setCaseSensitivity(Qt::CaseSensitive);
fd7f51cd Poul Sander 2026-05-22 15:30 121
    m_pathCompleter->setCompletionMode(QCompleter::PopupCompletion);
fd7f51cd Poul Sander 2026-05-22 15:30 122
    m_pathCompleter->setFilterMode(Qt::MatchStartsWith);
fd7f51cd Poul Sander 2026-05-22 15:30 123
    m_pathEdit->setCompleter(m_pathCompleter);
fd7f51cd Poul Sander 2026-05-22 15:30 124
fd7f51cd Poul Sander 2026-05-22 15:30 125
    auto *pathRow = new QHBoxLayout;
fd7f51cd Poul Sander 2026-05-22 15:30 126
    pathRow->setContentsMargins(4, 4, 4, 0);
fd7f51cd Poul Sander 2026-05-22 15:30 127
    pathRow->addWidget(m_pathEdit, 1);
fd7f51cd Poul Sander 2026-05-22 15:30 128
    pathRow->addWidget(m_goButton);
bf525669 Poul Sander 2026-05-30 23:18 129
    pathRow->addWidget(m_prevFolderButton);
bf525669 Poul Sander 2026-05-30 23:18 130
    pathRow->addWidget(m_nextFolderButton);
fd7f51cd Poul Sander 2026-05-22 15:30 131
fd7f51cd Poul Sander 2026-05-22 15:30 132
    auto *browserPage = new QWidget;
fd7f51cd Poul Sander 2026-05-22 15:30 133
    auto *vbox = new QVBoxLayout(browserPage);
fd7f51cd Poul Sander 2026-05-22 15:30 134
    vbox->setContentsMargins(0, 0, 0, 0);
fd7f51cd Poul Sander 2026-05-22 15:30 135
    vbox->setSpacing(0);
fd7f51cd Poul Sander 2026-05-22 15:30 136
    vbox->addLayout(pathRow);
fd7f51cd Poul Sander 2026-05-22 15:30 137
    vbox->addWidget(m_listView, 1);
fd7f51cd Poul Sander 2026-05-22 15:30 138
61136b77 Poul Sander 2026-02-20 22:45 139
    // Stack to switch between browser and single image
61136b77 Poul Sander 2026-02-20 22:45 140
    m_stack = new QStackedWidget;
fd7f51cd Poul Sander 2026-05-22 15:30 141
    m_stack->addWidget(browserPage);
61136b77 Poul Sander 2026-02-20 22:45 142
    m_stack->addWidget(m_imageView);
fd7f51cd Poul Sander 2026-05-22 15:30 143
    m_browserPage = browserPage;
61136b77 Poul Sander 2026-02-20 22:45 144
61136b77 Poul Sander 2026-02-20 22:45 145
    setCentralWidget(m_stack);
cbc7d22b Poul Sander 2026-02-24 19:23 146
fd7f51cd Poul Sander 2026-05-22 15:30 147
    updatePathField(QFile::encodeName(QDir::homePath()));
fd7f51cd Poul Sander 2026-05-22 15:30 148
cbc7d22b Poul Sander 2026-02-24 19:23 149
    // Dock widgets — can be dragged, floated and stacked by the user
3bee3e3e Poul Sander 2026-03-05 17:07 150
    m_rootDock = new QDockWidget(tr("Roots"), this);
3bee3e3e Poul Sander 2026-03-05 17:07 151
    m_rootDock->setObjectName("RootDock");
3bee3e3e Poul Sander 2026-03-05 17:07 152
    m_rootDock->setWidget(m_rootList);
3bee3e3e Poul Sander 2026-03-05 17:07 153
    addDockWidget(Qt::LeftDockWidgetArea, m_rootDock);
3bee3e3e Poul Sander 2026-03-05 17:07 154
cbc7d22b Poul Sander 2026-02-24 19:23 155
    m_folderDock = new QDockWidget(tr("Folders"), this);
cbc7d22b Poul Sander 2026-02-24 19:23 156
    m_folderDock->setObjectName("FolderDock");
3bee3e3e Poul Sander 2026-03-05 17:07 157
    m_folderDock->setWidget(m_treeView);
cbc7d22b Poul Sander 2026-02-24 19:23 158
    addDockWidget(Qt::LeftDockWidgetArea, m_folderDock);
3bee3e3e Poul Sander 2026-03-05 17:07 159
    splitDockWidget(m_rootDock, m_folderDock, Qt::Vertical);
cbc7d22b Poul Sander 2026-02-24 19:23 160
cbc7d22b Poul Sander 2026-02-24 19:23 161
    m_previewDock = new QDockWidget(tr("Preview"), this);
cbc7d22b Poul Sander 2026-02-24 19:23 162
    m_previewDock->setObjectName("PreviewDock");
cbc7d22b Poul Sander 2026-02-24 19:23 163
    m_previewDock->setWidget(m_previewLabel);
cbc7d22b Poul Sander 2026-02-24 19:23 164
    addDockWidget(Qt::RightDockWidgetArea, m_previewDock);
53fdab93 Poul Sander 2026-05-18 18:45 165
53fdab93 Poul Sander 2026-05-18 18:45 166
    // EXIF info dock — placed below the preview dock by default
53fdab93 Poul Sander 2026-05-18 18:45 167
    m_exifTable = new QTableWidget(0, 2);
53fdab93 Poul Sander 2026-05-18 18:45 168
    m_exifTable->setHorizontalHeaderLabels({tr("Field"), tr("Value")});
53fdab93 Poul Sander 2026-05-18 18:45 169
    m_exifTable->horizontalHeader()->setStretchLastSection(true);
53fdab93 Poul Sander 2026-05-18 18:45 170
    m_exifTable->verticalHeader()->hide();
53fdab93 Poul Sander 2026-05-18 18:45 171
    m_exifTable->setEditTriggers(QAbstractItemView::NoEditTriggers);
53fdab93 Poul Sander 2026-05-18 18:45 172
    m_exifTable->setSelectionMode(QAbstractItemView::NoSelection);
53fdab93 Poul Sander 2026-05-18 18:45 173
    m_exifTable->setShowGrid(false);
53fdab93 Poul Sander 2026-05-18 18:45 174
    m_exifTable->setAlternatingRowColors(true);
53fdab93 Poul Sander 2026-05-18 18:45 175
    m_exifTable->setMinimumWidth(200);
53fdab93 Poul Sander 2026-05-18 18:45 176
53fdab93 Poul Sander 2026-05-18 18:45 177
    m_exifDock = new QDockWidget(tr("EXIF Info"), this);
53fdab93 Poul Sander 2026-05-18 18:45 178
    m_exifDock->setObjectName("ExifDock");
53fdab93 Poul Sander 2026-05-18 18:45 179
    m_exifDock->setWidget(m_exifTable);
53fdab93 Poul Sander 2026-05-18 18:45 180
    addDockWidget(Qt::RightDockWidgetArea, m_exifDock);
53fdab93 Poul Sander 2026-05-18 18:45 181
    splitDockWidget(m_previewDock, m_exifDock, Qt::Vertical);
032aa1e1 Poul Sander 2026-02-19 18:55 182
}
032aa1e1 Poul Sander 2026-02-19 18:55 183
f61bf886 Poul Sander 2026-02-23 19:27 184
void MainWindow::setupMenuBar()
f61bf886 Poul Sander 2026-02-23 19:27 185
{
ccb726b8 Poul Sander 2026-02-23 22:43 186
    m_menuBar = menuBar();
0df01dc1 Poul Sander 2026-02-24 18:51 187
    m_menuBar->setStyleSheet(
0df01dc1 Poul Sander 2026-02-24 18:51 188
        "QMenuBar { padding: 1px 2px; }"
0df01dc1 Poul Sander 2026-02-24 18:51 189
        "QMenuBar::item { padding: 2px 6px; }"
0df01dc1 Poul Sander 2026-02-24 18:51 190
    );
4c41f5ff Poul Sander 2026-05-31 00:01 191
    QMenu *viewMenu = m_menuBar->addMenu(tr("&View"));
3bee3e3e Poul Sander 2026-03-05 17:07 192
    viewMenu->addAction(m_rootDock->toggleViewAction());
cbc7d22b Poul Sander 2026-02-24 19:23 193
    viewMenu->addAction(m_folderDock->toggleViewAction());
cbc7d22b Poul Sander 2026-02-24 19:23 194
    viewMenu->addAction(m_previewDock->toggleViewAction());
53fdab93 Poul Sander 2026-05-18 18:45 195
    viewMenu->addAction(m_exifDock->toggleViewAction());
cbc7d22b Poul Sander 2026-02-24 19:23 196
4c41f5ff Poul Sander 2026-05-31 00:01 197
    QMenu *editMenu = m_menuBar->addMenu(tr("&Edit"));
4c41f5ff Poul Sander 2026-05-31 00:01 198
    QAction *preferencesAction = editMenu->addAction(tr("&Preferences"));
f61bf886 Poul Sander 2026-02-23 19:27 199
    connect(preferencesAction, &QAction::triggered, this, &MainWindow::onPreferencesTriggered);
f61bf886 Poul Sander 2026-02-23 19:27 200
}
f61bf886 Poul Sander 2026-02-23 19:27 201
665ac308 Poul Sander 2026-02-23 19:34 202
void MainWindow::navigateToFolder(const QString &path)
665ac308 Poul Sander 2026-02-23 19:34 203
{
80a9926d Poul Sander 2026-05-19 20:28 204
    navigateToFolder(QFile::encodeName(path));
80a9926d Poul Sander 2026-05-19 20:28 205
}
80a9926d Poul Sander 2026-05-19 20:28 206
bf525669 Poul Sander 2026-05-30 23:18 207
QByteArray MainWindow::siblingFolder(int delta) const
bf525669 Poul Sander 2026-05-30 23:18 208
{
bf525669 Poul Sander 2026-05-30 23:18 209
    QByteArray currentDir = m_imageModel->currentDirectory();
bf525669 Poul Sander 2026-05-30 23:18 210
    if (currentDir.isEmpty())
bf525669 Poul Sander 2026-05-30 23:18 211
        return {};
bf525669 Poul Sander 2026-05-30 23:18 212
bf525669 Poul Sander 2026-05-30 23:18 213
    namespace fs = std::filesystem;
bf525669 Poul Sander 2026-05-30 23:18 214
    fs::path current(currentDir.toStdString());
bf525669 Poul Sander 2026-05-30 23:18 215
    fs::path parent = current.parent_path();
bf525669 Poul Sander 2026-05-30 23:18 216
    if (parent.empty() || parent == current)
bf525669 Poul Sander 2026-05-30 23:18 217
        return {};
bf525669 Poul Sander 2026-05-30 23:18 218
bf525669 Poul Sander 2026-05-30 23:18 219
    QByteArray parentPath = QByteArray::fromStdString(parent.native());
bf525669 Poul Sander 2026-05-30 23:18 220
    QModelIndex parentIdx = m_dirModel->indexForPath(parentPath);
bf525669 Poul Sander 2026-05-30 23:18 221
    if (!parentIdx.isValid())
bf525669 Poul Sander 2026-05-30 23:18 222
        return {};
bf525669 Poul Sander 2026-05-30 23:18 223
bf525669 Poul Sander 2026-05-30 23:18 224
    int count = m_dirModel->rowCount(parentIdx);
bf525669 Poul Sander 2026-05-30 23:18 225
    int currentRow = -1;
bf525669 Poul Sander 2026-05-30 23:18 226
    for (int i = 0; i < count; ++i) {
bf525669 Poul Sander 2026-05-30 23:18 227
        QModelIndex idx = m_dirModel->index(i, 0, parentIdx);
bf525669 Poul Sander 2026-05-30 23:18 228
        if (m_dirModel->filePath(idx) == currentDir) {
bf525669 Poul Sander 2026-05-30 23:18 229
            currentRow = i;
bf525669 Poul Sander 2026-05-30 23:18 230
            break;
bf525669 Poul Sander 2026-05-30 23:18 231
        }
bf525669 Poul Sander 2026-05-30 23:18 232
    }
bf525669 Poul Sander 2026-05-30 23:18 233
bf525669 Poul Sander 2026-05-30 23:18 234
    if (currentRow < 0)
bf525669 Poul Sander 2026-05-30 23:18 235
        return {};
bf525669 Poul Sander 2026-05-30 23:18 236
bf525669 Poul Sander 2026-05-30 23:18 237
    int targetRow = currentRow + delta;
bf525669 Poul Sander 2026-05-30 23:18 238
    if (targetRow < 0 || targetRow >= count)
bf525669 Poul Sander 2026-05-30 23:18 239
        return {};
bf525669 Poul Sander 2026-05-30 23:18 240
bf525669 Poul Sander 2026-05-30 23:18 241
    QModelIndex targetIdx = m_dirModel->index(targetRow, 0, parentIdx);
bf525669 Poul Sander 2026-05-30 23:18 242
    return m_dirModel->filePath(targetIdx);
bf525669 Poul Sander 2026-05-30 23:18 243
}
bf525669 Poul Sander 2026-05-30 23:18 244
bf525669 Poul Sander 2026-05-30 23:18 245
void MainWindow::navigateToPrevFolder()
bf525669 Poul Sander 2026-05-30 23:18 246
{
bf525669 Poul Sander 2026-05-30 23:18 247
    QByteArray path = siblingFolder(-1);
bf525669 Poul Sander 2026-05-30 23:18 248
    if (!path.isEmpty())
bf525669 Poul Sander 2026-05-30 23:18 249
        navigateToFolder(path);
bf525669 Poul Sander 2026-05-30 23:18 250
}
bf525669 Poul Sander 2026-05-30 23:18 251
bf525669 Poul Sander 2026-05-30 23:18 252
void MainWindow::navigateToNextFolder()
bf525669 Poul Sander 2026-05-30 23:18 253
{
bf525669 Poul Sander 2026-05-30 23:18 254
    QByteArray path = siblingFolder(+1);
bf525669 Poul Sander 2026-05-30 23:18 255
    if (!path.isEmpty())
bf525669 Poul Sander 2026-05-30 23:18 256
        navigateToFolder(path);
bf525669 Poul Sander 2026-05-30 23:18 257
}
bf525669 Poul Sander 2026-05-30 23:18 258
80a9926d Poul Sander 2026-05-19 20:28 259
void MainWindow::navigateToFolder(const QByteArray &path)
80a9926d Poul Sander 2026-05-19 20:28 260
{
665ac308 Poul Sander 2026-02-23 19:34 261
    m_previousFolderPath = m_imageModel->currentDirectory();
665ac308 Poul Sander 2026-02-23 19:34 262
    m_imageModel->setDirectory(path);
40efdfde Poul Sander 2026-05-19 21:24 263
    QModelIndex dirIdx = m_dirModel->indexForPath(path);
665ac308 Poul Sander 2026-02-23 19:34 264
    if (dirIdx.isValid())
665ac308 Poul Sander 2026-02-23 19:34 265
        m_treeView->setCurrentIndex(dirIdx);
fd7f51cd Poul Sander 2026-05-22 15:30 266
    updatePathField(path);
665ac308 Poul Sander 2026-02-23 19:34 267
    QTimer::singleShot(0, this, [this]() {
665ac308 Poul Sander 2026-02-23 19:34 268
        selectPreviousFolderIfExists();
665ac308 Poul Sander 2026-02-23 19:34 269
        loadVisibleThumbnails();
665ac308 Poul Sander 2026-02-23 19:34 270
    });
665ac308 Poul Sander 2026-02-23 19:34 271
}
665ac308 Poul Sander 2026-02-23 19:34 272
fd7f51cd Poul Sander 2026-05-22 15:30 273
void MainWindow::updatePathField(const QByteArray &path)
fd7f51cd Poul Sander 2026-05-22 15:30 274
{
fd7f51cd Poul Sander 2026-05-22 15:30 275
    if (!m_pathEdit)
fd7f51cd Poul Sander 2026-05-22 15:30 276
        return;
fd7f51cd Poul Sander 2026-05-22 15:30 277
    const QString text = QFile::decodeName(path);
fd7f51cd Poul Sander 2026-05-22 15:30 278
    if (m_pathEdit->text() != text)
fd7f51cd Poul Sander 2026-05-22 15:30 279
        m_pathEdit->setText(text);
fd7f51cd Poul Sander 2026-05-22 15:30 280
}
fd7f51cd Poul Sander 2026-05-22 15:30 281
fd7f51cd Poul Sander 2026-05-22 15:30 282
void MainWindow::onPathEntered()
fd7f51cd Poul Sander 2026-05-22 15:30 283
{
fd7f51cd Poul Sander 2026-05-22 15:30 284
    const QString text = m_pathEdit->text().trimmed();
fd7f51cd Poul Sander 2026-05-22 15:30 285
    if (text.isEmpty())
fd7f51cd Poul Sander 2026-05-22 15:30 286
        return;
fd7f51cd Poul Sander 2026-05-22 15:30 287
    openPath(text);
fd7f51cd Poul Sander 2026-05-22 15:30 288
}
fd7f51cd Poul Sander 2026-05-22 15:30 289
48607848 Poul Sander 2026-05-19 19:20 290
void MainWindow::openPath(const QString &path)
48607848 Poul Sander 2026-05-19 19:20 291
{
48607848 Poul Sander 2026-05-19 19:20 292
    QFileInfo info(path);
48607848 Poul Sander 2026-05-19 19:20 293
    if (!info.exists())
48607848 Poul Sander 2026-05-19 19:20 294
        return;
48607848 Poul Sander 2026-05-19 19:20 295
48607848 Poul Sander 2026-05-19 19:20 296
    if (info.isDir()) {
80a9926d Poul Sander 2026-05-19 20:28 297
        navigateToFolder(QFile::encodeName(info.absoluteFilePath()));
48607848 Poul Sander 2026-05-19 19:20 298
    } else if (info.isFile()) {
80a9926d Poul Sander 2026-05-19 20:28 299
        navigateToFolder(QFile::encodeName(info.absolutePath()));
48607848 Poul Sander 2026-05-19 19:20 300
48607848 Poul Sander 2026-05-19 19:20 301
        // ImageModel::setDirectory is synchronous, so items are ready now.
48607848 Poul Sander 2026-05-19 19:20 302
        // Find the file in the model and open it.
80a9926d Poul Sander 2026-05-19 20:28 303
        QByteArray target = QFile::encodeName(info.absoluteFilePath());
48607848 Poul Sander 2026-05-19 19:20 304
        for (int i = 0; i < m_imageModel->rowCount(); ++i) {
48607848 Poul Sander 2026-05-19 19:20 305
            QModelIndex idx = m_imageModel->index(i);
80a9926d Poul Sander 2026-05-19 20:28 306
            if (m_imageModel->filePath(idx) == target) {
48607848 Poul Sander 2026-05-19 19:20 307
                m_listView->setCurrentIndex(idx);
48607848 Poul Sander 2026-05-19 19:20 308
                m_listView->scrollTo(idx);
48607848 Poul Sander 2026-05-19 19:20 309
                showPreview(idx);
48607848 Poul Sander 2026-05-19 19:20 310
                if (!m_imageModel->isFolder(idx))
48607848 Poul Sander 2026-05-19 19:20 311
                    enterSingleImageMode(idx);
48607848 Poul Sander 2026-05-19 19:20 312
                break;
48607848 Poul Sander 2026-05-19 19:20 313
            }
48607848 Poul Sander 2026-05-19 19:20 314
        }
48607848 Poul Sander 2026-05-19 19:20 315
    }
48607848 Poul Sander 2026-05-19 19:20 316
}
48607848 Poul Sander 2026-05-19 19:20 317
665ac308 Poul Sander 2026-02-23 19:34 318
void MainWindow::selectPreviousFolderIfExists()
665ac308 Poul Sander 2026-02-23 19:34 319
{
665ac308 Poul Sander 2026-02-23 19:34 320
    if (m_previousFolderPath.isEmpty())
665ac308 Poul Sander 2026-02-23 19:34 321
        return;
665ac308 Poul Sander 2026-02-23 19:34 322
665ac308 Poul Sander 2026-02-23 19:34 323
    // Find the previous folder in the current directory's contents
665ac308 Poul Sander 2026-02-23 19:34 324
    for (int i = 0; i < m_imageModel->rowCount(); ++i) {
665ac308 Poul Sander 2026-02-23 19:34 325
        QModelIndex idx = m_imageModel->index(i);
665ac308 Poul Sander 2026-02-23 19:34 326
        if (m_imageModel->isFolder(idx)) {
80a9926d Poul Sander 2026-05-19 20:28 327
            QByteArray folderPath = m_imageModel->filePath(idx);
665ac308 Poul Sander 2026-02-23 19:34 328
            if (folderPath == m_previousFolderPath) {
665ac308 Poul Sander 2026-02-23 19:34 329
                m_listView->setCurrentIndex(idx);
665ac308 Poul Sander 2026-02-23 19:34 330
                m_listView->scrollTo(idx);
665ac308 Poul Sander 2026-02-23 19:34 331
                return;
665ac308 Poul Sander 2026-02-23 19:34 332
            }
665ac308 Poul Sander 2026-02-23 19:34 333
        }
665ac308 Poul Sander 2026-02-23 19:34 334
    }
665ac308 Poul Sander 2026-02-23 19:34 335
}
665ac308 Poul Sander 2026-02-23 19:34 336
032aa1e1 Poul Sander 2026-02-19 18:55 337
void MainWindow::setupConnections()
032aa1e1 Poul Sander 2026-02-19 18:55 338
{
fd7f51cd Poul Sander 2026-05-22 15:30 339
    connect(m_pathEdit, &QLineEdit::returnPressed,
fd7f51cd Poul Sander 2026-05-22 15:30 340
            this, &MainWindow::onPathEntered);
fd7f51cd Poul Sander 2026-05-22 15:30 341
    connect(m_goButton, &QPushButton::clicked,
fd7f51cd Poul Sander 2026-05-22 15:30 342
            this, &MainWindow::onPathEntered);
bf525669 Poul Sander 2026-05-30 23:18 343
    connect(m_prevFolderButton, &QPushButton::clicked,
bf525669 Poul Sander 2026-05-30 23:18 344
            this, &MainWindow::navigateToPrevFolder);
bf525669 Poul Sander 2026-05-30 23:18 345
    connect(m_nextFolderButton, &QPushButton::clicked,
bf525669 Poul Sander 2026-05-30 23:18 346
            this, &MainWindow::navigateToNextFolder);
fd7f51cd Poul Sander 2026-05-22 15:30 347
    connect(m_pathEdit, &QLineEdit::textEdited,
fd7f51cd Poul Sander 2026-05-22 15:30 348
            m_pathCompleterModel, &PathCompleterModel::setPrefix);
fd7f51cd Poul Sander 2026-05-22 15:30 349
3bee3e3e Poul Sander 2026-03-05 17:07 350
    connect(m_rootList, &QListWidget::currentItemChanged, this,
3bee3e3e Poul Sander 2026-03-05 17:07 351
            [this](QListWidgetItem *current, QListWidgetItem *) {
3bee3e3e Poul Sander 2026-03-05 17:07 352
                if (current == nullptr)
3bee3e3e Poul Sander 2026-03-05 17:07 353
                    return;
3bee3e3e Poul Sander 2026-03-05 17:07 354
3bee3e3e Poul Sander 2026-03-05 17:07 355
                QString rootPath = current->data(Qt::UserRole).toString();
fc5f2e27 Poul Sander 2026-02-22 22:06 356
                m_dirModel->setRootPath(rootPath);
40efdfde Poul Sander 2026-05-19 21:24 357
                m_treeView->setRootIndex(m_dirModel->rootIndex());
fc5f2e27 Poul Sander 2026-02-22 22:06 358
            });
fc5f2e27 Poul Sander 2026-02-22 22:06 359
032aa1e1 Poul Sander 2026-02-19 18:55 360
    connect(m_treeView, &QTreeView::clicked, this,
032aa1e1 Poul Sander 2026-02-19 18:55 361
            [this](const QModelIndex &index)
6fcd6c90 Poul Sander 2026-02-19 21:12 362
            {
40efdfde Poul Sander 2026-05-19 21:24 363
                QByteArray path = m_dirModel->filePath(index);
665ac308 Poul Sander 2026-02-23 19:34 364
                navigateToFolder(path);
6fcd6c90 Poul Sander 2026-02-19 21:12 365
            });
032aa1e1 Poul Sander 2026-02-19 18:55 366
032aa1e1 Poul Sander 2026-02-19 18:55 367
    connect(m_listView, &QListView::clicked, this,
cfe4a67b Poul Sander 2026-02-19 23:06 368
            [this](const QModelIndex &index) { showPreview(index); });
6fcd6c90 Poul Sander 2026-02-19 21:12 369
cfe4a67b Poul Sander 2026-02-19 23:06 370
    connect(m_listView->selectionModel(), &QItemSelectionModel::currentChanged,
cfe4a67b Poul Sander 2026-02-19 23:06 371
            this, [this](const QModelIndex &current) { showPreview(current); });
6fcd6c90 Poul Sander 2026-02-19 21:12 372
61136b77 Poul Sander 2026-02-20 22:45 373
    connect(m_listView, &QListView::doubleClicked, this,
09130ab9 Poul Sander 2026-02-22 22:19 374
            [this](const QModelIndex &index) {
09130ab9 Poul Sander 2026-02-22 22:19 375
                if (m_imageModel->isFolder(index)) {
80a9926d Poul Sander 2026-05-19 20:28 376
                    QByteArray path = m_imageModel->filePath(index);
665ac308 Poul Sander 2026-02-23 19:34 377
                    navigateToFolder(path);
09130ab9 Poul Sander 2026-02-22 22:19 378
                } else {
09130ab9 Poul Sander 2026-02-22 22:19 379
                    enterSingleImageMode(index);
09130ab9 Poul Sander 2026-02-22 22:19 380
                }
09130ab9 Poul Sander 2026-02-22 22:19 381
            });
61136b77 Poul Sander 2026-02-20 22:45 382
61136b77 Poul Sander 2026-02-20 22:45 383
    connect(m_imageView, &ImageViewWidget::closeRequested,
61136b77 Poul Sander 2026-02-20 22:45 384
            this, &MainWindow::leaveSingleImageMode);
beb4a823 Poul Sander 2026-05-19 21:53 385
    connect(m_imageView, &ImageViewWidget::editCaptionRequested,
beb4a823 Poul Sander 2026-05-19 21:53 386
        this, &MainWindow::onEditCaption);
1ffc1962 Poul Sander 2026-02-20 23:41 387
    connect(m_imageView, &ImageViewWidget::nextRequested, this, [this]() {
1ffc1962 Poul Sander 2026-02-20 23:41 388
        QModelIndex cur = m_listView->currentIndex();
1ffc1962 Poul Sander 2026-02-20 23:41 389
        int next = cur.isValid() ? cur.row() + 1 : 0;
09130ab9 Poul Sander 2026-02-22 22:19 390
        while (next < m_imageModel->rowCount() && m_imageModel->isFolder(m_imageModel->index(next)))
09130ab9 Poul Sander 2026-02-22 22:19 391
            ++next;
1ffc1962 Poul Sander 2026-02-20 23:41 392
        if (next < m_imageModel->rowCount())
1ffc1962 Poul Sander 2026-02-20 23:41 393
        {
1ffc1962 Poul Sander 2026-02-20 23:41 394
            QModelIndex idx = m_imageModel->index(next);
1ffc1962 Poul Sander 2026-02-20 23:41 395
            m_listView->setCurrentIndex(idx);
1ffc1962 Poul Sander 2026-02-20 23:41 396
            enterSingleImageMode(idx);
1ffc1962 Poul Sander 2026-02-20 23:41 397
        }
1ffc1962 Poul Sander 2026-02-20 23:41 398
    });
1ffc1962 Poul Sander 2026-02-20 23:41 399
1ffc1962 Poul Sander 2026-02-20 23:41 400
    connect(m_imageView, &ImageViewWidget::previousRequested, this, [this]() {
1ffc1962 Poul Sander 2026-02-20 23:41 401
        QModelIndex cur = m_listView->currentIndex();
1ffc1962 Poul Sander 2026-02-20 23:41 402
        int prev = cur.isValid() ? cur.row() - 1 : 0;
09130ab9 Poul Sander 2026-02-22 22:19 403
        while (prev >= 0 && m_imageModel->isFolder(m_imageModel->index(prev)))
09130ab9 Poul Sander 2026-02-22 22:19 404
            --prev;
1ffc1962 Poul Sander 2026-02-20 23:41 405
        if (prev >= 0)
1ffc1962 Poul Sander 2026-02-20 23:41 406
        {
1ffc1962 Poul Sander 2026-02-20 23:41 407
            QModelIndex idx = m_imageModel->index(prev);
1ffc1962 Poul Sander 2026-02-20 23:41 408
            m_listView->setCurrentIndex(idx);
1ffc1962 Poul Sander 2026-02-20 23:41 409
            enterSingleImageMode(idx);
1ffc1962 Poul Sander 2026-02-20 23:41 410
        }
1ffc1962 Poul Sander 2026-02-20 23:41 411
    });
1ffc1962 Poul Sander 2026-02-20 23:41 412
6fcd6c90 Poul Sander 2026-02-19 21:12 413
    connect(m_listView->verticalScrollBar(), &QScrollBar::valueChanged,
6fcd6c90 Poul Sander 2026-02-19 21:12 414
            this, [this]()
6fcd6c90 Poul Sander 2026-02-19 21:12 415
            { loadVisibleThumbnails(); });
6fcd6c90 Poul Sander 2026-02-19 21:12 416
6fcd6c90 Poul Sander 2026-02-19 21:12 417
    // Also catch resize — visible range changes when the viewport is resized
6fcd6c90 Poul Sander 2026-02-19 21:12 418
    m_listView->viewport()->installEventFilter(this);
ff6ac640 Poul Sander 2026-06-02 16:53 419
ff6ac640 Poul Sander 2026-06-02 16:53 420
    // The folder list and tree views consume Ctrl+Left/Ctrl+Right for their own
ff6ac640 Poul Sander 2026-06-02 16:53 421
    // item navigation, so those keys never reach MainWindow::keyPressEvent.
ff6ac640 Poul Sander 2026-06-02 16:53 422
    // Filter them here so PrevFolder/NextFolder shortcuts work while a view has focus.
ff6ac640 Poul Sander 2026-06-02 16:53 423
    m_listView->installEventFilter(this);
ff6ac640 Poul Sander 2026-06-02 16:53 424
    m_treeView->installEventFilter(this);
6fcd6c90 Poul Sander 2026-02-19 21:12 425
}
6fcd6c90 Poul Sander 2026-02-19 21:12 426
6fcd6c90 Poul Sander 2026-02-19 21:12 427
bool MainWindow::eventFilter(QObject *obj, QEvent *event)
6fcd6c90 Poul Sander 2026-02-19 21:12 428
{
6fcd6c90 Poul Sander 2026-02-19 21:12 429
    if (obj == m_listView->viewport() && event->type() == QEvent::Resize)
6fcd6c90 Poul Sander 2026-02-19 21:12 430
        QTimer::singleShot(0, this, &MainWindow::loadVisibleThumbnails);
6fcd6c90 Poul Sander 2026-02-19 21:12 431
ff6ac640 Poul Sander 2026-06-02 16:53 432
    if ((obj == m_listView || obj == m_treeView)
ff6ac640 Poul Sander 2026-06-02 16:53 433
        && event->type() == QEvent::KeyPress
ff6ac640 Poul Sander 2026-06-02 16:53 434
        && m_stack->currentWidget() == m_browserPage)
ff6ac640 Poul Sander 2026-06-02 16:53 435
    {
ff6ac640 Poul Sander 2026-06-02 16:53 436
        const ShortcutManager &sc = ShortcutManager::instance();
ff6ac640 Poul Sander 2026-06-02 16:53 437
        QKeyEvent *keyEvent = static_cast<QKeyEvent *>(event);
ff6ac640 Poul Sander 2026-06-02 16:53 438
ff6ac640 Poul Sander 2026-06-02 16:53 439
        if (sc.matches(keyEvent, ShortcutManager::PrevFolder)) {
ff6ac640 Poul Sander 2026-06-02 16:53 440
            navigateToPrevFolder();
ff6ac640 Poul Sander 2026-06-02 16:53 441
            return true;
ff6ac640 Poul Sander 2026-06-02 16:53 442
        }
ff6ac640 Poul Sander 2026-06-02 16:53 443
        if (sc.matches(keyEvent, ShortcutManager::NextFolder)) {
ff6ac640 Poul Sander 2026-06-02 16:53 444
            navigateToNextFolder();
ff6ac640 Poul Sander 2026-06-02 16:53 445
            return true;
ff6ac640 Poul Sander 2026-06-02 16:53 446
        }
ff6ac640 Poul Sander 2026-06-02 16:53 447
    }
ff6ac640 Poul Sander 2026-06-02 16:53 448
6fcd6c90 Poul Sander 2026-02-19 21:12 449
    return QMainWindow::eventFilter(obj, event);
6fcd6c90 Poul Sander 2026-02-19 21:12 450
}
6fcd6c90 Poul Sander 2026-02-19 21:12 451
6fcd6c90 Poul Sander 2026-02-19 21:12 452
void MainWindow::loadVisibleThumbnails()
6fcd6c90 Poul Sander 2026-02-19 21:12 453
{
6fcd6c90 Poul Sander 2026-02-19 21:12 454
    QModelIndex topLeft = m_listView->indexAt(QPoint(0, 0));
6fcd6c90 Poul Sander 2026-02-19 21:12 455
    QModelIndex bottomRight = m_listView->indexAt(
6fcd6c90 Poul Sander 2026-02-19 21:12 456
        QPoint(m_listView->viewport()->width() - 1,
6fcd6c90 Poul Sander 2026-02-19 21:12 457
               m_listView->viewport()->height() - 1));
6fcd6c90 Poul Sander 2026-02-19 21:12 458
6fcd6c90 Poul Sander 2026-02-19 21:12 459
    int first = topLeft.isValid() ? topLeft.row() : 0;
6fcd6c90 Poul Sander 2026-02-19 21:12 460
    int last = bottomRight.isValid() ? bottomRight.row()
6fcd6c90 Poul Sander 2026-02-19 21:12 461
                                     : m_imageModel->rowCount() - 1;
6fcd6c90 Poul Sander 2026-02-19 21:12 462
6fcd6c90 Poul Sander 2026-02-19 21:12 463
    if (last < 0)
6fcd6c90 Poul Sander 2026-02-19 21:12 464
        return;
6fcd6c90 Poul Sander 2026-02-19 21:12 465
6fcd6c90 Poul Sander 2026-02-19 21:12 466
    std::cout << "loadVisibleThumbnails: " << first << " - " << last << "\n";
6fcd6c90 Poul Sander 2026-02-19 21:12 467
    m_imageModel->requestThumbnails(first, last);
032aa1e1 Poul Sander 2026-02-19 18:55 468
}
cfe4a67b Poul Sander 2026-02-19 23:06 469
cfe4a67b Poul Sander 2026-02-19 23:06 470
void MainWindow::showPreview(const QModelIndex &index)
cfe4a67b Poul Sander 2026-02-19 23:06 471
{
cfe4a67b Poul Sander 2026-02-19 23:06 472
    if (!index.isValid())
cfe4a67b Poul Sander 2026-02-19 23:06 473
        return;
cfe4a67b Poul Sander 2026-02-19 23:06 474
80a9926d Poul Sander 2026-05-19 20:28 475
    QByteArray path = m_imageModel->filePath(index);
80a9926d Poul Sander 2026-05-19 20:28 476
80a9926d Poul Sander 2026-05-19 20:28 477
    int fd = ::open(path.constData(), O_RDONLY | O_CLOEXEC);
80a9926d Poul Sander 2026-05-19 20:28 478
    if (fd >= 0) {
80a9926d Poul Sander 2026-05-19 20:28 479
        QFile file;
80a9926d Poul Sander 2026-05-19 20:28 480
        if (file.open(fd, QIODevice::ReadOnly, QFileDevice::AutoCloseHandle)) {
80a9926d Poul Sander 2026-05-19 20:28 481
            QImageReader reader(&file);
80a9926d Poul Sander 2026-05-19 20:28 482
            reader.setAutoTransform(true);
80a9926d Poul Sander 2026-05-19 20:28 483
            QSize fullSize = reader.size();
80a9926d Poul Sander 2026-05-19 20:28 484
            if (fullSize.isValid()) {
80a9926d Poul Sander 2026-05-19 20:28 485
                QSize target = fullSize.scaled(m_previewLabel->size(), Qt::KeepAspectRatio);
80a9926d Poul Sander 2026-05-19 20:28 486
                reader.setScaledSize(target);
80a9926d Poul Sander 2026-05-19 20:28 487
            }
80a9926d Poul Sander 2026-05-19 20:28 488
            QImage image = reader.read();
80a9926d Poul Sander 2026-05-19 20:28 489
            m_previewLabel->setPixmap(
80a9926d Poul Sander 2026-05-19 20:28 490
                QPixmap::fromImage(image).scaled(
80a9926d Poul Sander 2026-05-19 20:28 491
                    m_previewLabel->size(),
80a9926d Poul Sander 2026-05-19 20:28 492
                    Qt::KeepAspectRatio,
80a9926d Poul Sander 2026-05-19 20:28 493
                    Qt::SmoothTransformation));
80a9926d Poul Sander 2026-05-19 20:28 494
        }
7889168c Poul Sander 2026-02-26 18:27 495
    }
53fdab93 Poul Sander 2026-05-18 18:45 496
53fdab93 Poul Sander 2026-05-18 18:45 497
    updateExifInfo(path);
53fdab93 Poul Sander 2026-05-18 18:45 498
}
53fdab93 Poul Sander 2026-05-18 18:45 499
80a9926d Poul Sander 2026-05-19 20:28 500
void MainWindow::updateExifInfo(const QByteArray &path)
53fdab93 Poul Sander 2026-05-18 18:45 501
{
80a9926d Poul Sander 2026-05-19 20:28 502
    // Check if this is a folder by attempting stat
80a9926d Poul Sander 2026-05-19 20:28 503
    struct ::stat st{};
80a9926d Poul Sander 2026-05-19 20:28 504
    if (::stat(path.constData(), &st) != 0 || S_ISDIR(st.st_mode)) {
53fdab93 Poul Sander 2026-05-18 18:45 505
        m_exifTable->setRowCount(0);
53fdab93 Poul Sander 2026-05-18 18:45 506
        return;
53fdab93 Poul Sander 2026-05-18 18:45 507
    }
53fdab93 Poul Sander 2026-05-18 18:45 508
    const ExifData data = ExifReader::read(path);
53fdab93 Poul Sander 2026-05-18 18:45 509
    const auto fields = data.toList();
53fdab93 Poul Sander 2026-05-18 18:45 510
    m_exifTable->setRowCount(fields.size());
53fdab93 Poul Sander 2026-05-18 18:45 511
    for (int i = 0; i < fields.size(); ++i) {
53fdab93 Poul Sander 2026-05-18 18:45 512
        m_exifTable->setItem(i, 0, new QTableWidgetItem(fields[i].first));
53fdab93 Poul Sander 2026-05-18 18:45 513
        m_exifTable->setItem(i, 1, new QTableWidgetItem(fields[i].second));
53fdab93 Poul Sander 2026-05-18 18:45 514
    }
53fdab93 Poul Sander 2026-05-18 18:45 515
    m_exifTable->resizeColumnToContents(0);
cfe4a67b Poul Sander 2026-02-19 23:06 516
}
61136b77 Poul Sander 2026-02-20 22:45 517
61136b77 Poul Sander 2026-02-20 22:45 518
void MainWindow::enterSingleImageMode(const QModelIndex &index)
61136b77 Poul Sander 2026-02-20 22:45 519
{
61136b77 Poul Sander 2026-02-20 22:45 520
    if (!index.isValid())
61136b77 Poul Sander 2026-02-20 22:45 521
        return;
61136b77 Poul Sander 2026-02-20 22:45 522
cbc7d22b Poul Sander 2026-02-24 19:23 523
    // Only save state and hide chrome when transitioning from browse mode
cbc7d22b Poul Sander 2026-02-24 19:23 524
    if (m_stack->currentWidget() != m_imageView) {
3bee3e3e Poul Sander 2026-03-05 17:07 525
        m_rootDockWasVisible = m_rootDock->isVisible();
cbc7d22b Poul Sander 2026-02-24 19:23 526
        m_folderDockWasVisible = m_folderDock->isVisible();
cbc7d22b Poul Sander 2026-02-24 19:23 527
        m_previewDockWasVisible = m_previewDock->isVisible();
53fdab93 Poul Sander 2026-05-18 18:45 528
        m_exifDockWasVisible = m_exifDock->isVisible();
3bee3e3e Poul Sander 2026-03-05 17:07 529
        m_rootDock->hide();
cbc7d22b Poul Sander 2026-02-24 19:23 530
        m_folderDock->hide();
cbc7d22b Poul Sander 2026-02-24 19:23 531
        m_previewDock->hide();
53fdab93 Poul Sander 2026-05-18 18:45 532
        m_exifDock->hide();
cbc7d22b Poul Sander 2026-02-24 19:23 533
        m_menuBar->hide();
cbc7d22b Poul Sander 2026-02-24 19:23 534
    }
cbc7d22b Poul Sander 2026-02-24 19:23 535
f61bf886 Poul Sander 2026-02-23 19:27 536
    m_imageView->setBackgroundColor(m_backgroundColorPreference);
80a9926d Poul Sander 2026-05-19 20:28 537
    QByteArray path = m_imageModel->filePath(index);
58103277 Poul Sander 2026-03-01 22:53 538
    m_imageView->setNeighborPaths(computeNeighborPaths(index));
61136b77 Poul Sander 2026-02-20 22:45 539
    m_imageView->setImage(path);
53fdab93 Poul Sander 2026-05-18 18:45 540
    m_imageView->setExifData(ExifReader::read(path));
53fdab93 Poul Sander 2026-05-18 18:45 541
    updateExifInfo(path);
61136b77 Poul Sander 2026-02-20 22:45 542
    m_stack->setCurrentWidget(m_imageView);
61136b77 Poul Sander 2026-02-20 22:45 543
    m_imageView->setFocus();
ccb726b8 Poul Sander 2026-02-23 22:43 544
ccb726b8 Poul Sander 2026-02-23 22:43 545
    if (m_imageViewWasFullScreen) {
ccb726b8 Poul Sander 2026-02-23 22:43 546
        setFullScreenMode(true);
ccb726b8 Poul Sander 2026-02-23 22:43 547
        m_imageViewWasFullScreen = false;
ccb726b8 Poul Sander 2026-02-23 22:43 548
    }
61136b77 Poul Sander 2026-02-20 22:45 549
}
61136b77 Poul Sander 2026-02-20 22:45 550
beb4a823 Poul Sander 2026-05-19 21:53 551
void MainWindow::onEditCaption()
beb4a823 Poul Sander 2026-05-19 21:53 552
{
beb4a823 Poul Sander 2026-05-19 21:53 553
    const QByteArray path = m_imageView->currentPath();
beb4a823 Poul Sander 2026-05-19 21:53 554
    if (path.isEmpty())
beb4a823 Poul Sander 2026-05-19 21:53 555
        return;
beb4a823 Poul Sander 2026-05-19 21:53 556
835a6bea Poul Sander 2026-05-19 22:03 557
    if (::access(path.constData(), W_OK) != 0) {
835a6bea Poul Sander 2026-05-19 22:03 558
        QMessageBox::warning(this, tr("Cannot Edit Caption"),
835a6bea Poul Sander 2026-05-19 22:03 559
            tr("The file is write-protected and cannot be edited.\n\n%1")
835a6bea Poul Sander 2026-05-19 22:03 560
                .arg(QString::fromLocal8Bit(path)));
835a6bea Poul Sander 2026-05-19 22:03 561
        return;
835a6bea Poul Sander 2026-05-19 22:03 562
    }
835a6bea Poul Sander 2026-05-19 22:03 563
beb4a823 Poul Sander 2026-05-19 21:53 564
    ExifData data = ExifReader::read(path);
beb4a823 Poul Sander 2026-05-19 21:53 565
beb4a823 Poul Sander 2026-05-19 21:53 566
    QDialog dlg(this);
beb4a823 Poul Sander 2026-05-19 21:53 567
    dlg.setWindowTitle(tr("Edit Caption"));
beb4a823 Poul Sander 2026-05-19 21:53 568
    auto *layout = new QVBoxLayout(&dlg);
beb4a823 Poul Sander 2026-05-19 21:53 569
    layout->addWidget(new QLabel(tr("Caption-Abstract:"), &dlg));
beb4a823 Poul Sander 2026-05-19 21:53 570
    auto *edit = new QPlainTextEdit(&dlg);
beb4a823 Poul Sander 2026-05-19 21:53 571
    edit->setPlainText(data.captionAbstract);
beb4a823 Poul Sander 2026-05-19 21:53 572
    edit->setMinimumWidth(400);
beb4a823 Poul Sander 2026-05-19 21:53 573
    layout->addWidget(edit);
beb4a823 Poul Sander 2026-05-19 21:53 574
    auto *buttons = new QDialogButtonBox(
beb4a823 Poul Sander 2026-05-19 21:53 575
        QDialogButtonBox::Ok | QDialogButtonBox::Cancel, &dlg);
beb4a823 Poul Sander 2026-05-19 21:53 576
    layout->addWidget(buttons);
beb4a823 Poul Sander 2026-05-19 21:53 577
    connect(buttons, &QDialogButtonBox::accepted, &dlg, &QDialog::accept);
beb4a823 Poul Sander 2026-05-19 21:53 578
    connect(buttons, &QDialogButtonBox::rejected, &dlg, &QDialog::reject);
beb4a823 Poul Sander 2026-05-19 21:53 579
beb4a823 Poul Sander 2026-05-19 21:53 580
    if (dlg.exec() != QDialog::Accepted)
beb4a823 Poul Sander 2026-05-19 21:53 581
        return;
beb4a823 Poul Sander 2026-05-19 21:53 582
beb4a823 Poul Sander 2026-05-19 21:53 583
    const QString newCaption = edit->toPlainText();
beb4a823 Poul Sander 2026-05-19 21:53 584
    if (ExifReader::saveCaption(path, newCaption, data)) {
beb4a823 Poul Sander 2026-05-19 21:53 585
        ExifData updated = ExifReader::read(path);
beb4a823 Poul Sander 2026-05-19 21:53 586
        m_imageView->setExifData(updated);
beb4a823 Poul Sander 2026-05-19 21:53 587
        updateExifInfo(path);
beb4a823 Poul Sander 2026-05-19 21:53 588
    }
beb4a823 Poul Sander 2026-05-19 21:53 589
}
beb4a823 Poul Sander 2026-05-19 21:53 590
61136b77 Poul Sander 2026-02-20 22:45 591
void MainWindow::leaveSingleImageMode()
61136b77 Poul Sander 2026-02-20 22:45 592
{
3968465e Poul Sander 2026-02-23 19:13 593
    if (isFullScreen()) {
ccb726b8 Poul Sander 2026-02-23 22:43 594
        m_imageViewWasFullScreen = true;
ccb726b8 Poul Sander 2026-02-23 22:43 595
        setFullScreenMode(false);
61136b77 Poul Sander 2026-02-20 22:45 596
    }
58103277 Poul Sander 2026-03-01 22:53 597
    m_imageView->clearCache();
61136b77 Poul Sander 2026-02-20 22:45 598
    m_stack->setCurrentWidget(m_browserPage);
cbc7d22b Poul Sander 2026-02-24 19:23 599
    m_menuBar->show();
3bee3e3e Poul Sander 2026-03-05 17:07 600
    if (m_rootDockWasVisible)
3bee3e3e Poul Sander 2026-03-05 17:07 601
        m_rootDock->show();
cbc7d22b Poul Sander 2026-02-24 19:23 602
    if (m_folderDockWasVisible)
cbc7d22b Poul Sander 2026-02-24 19:23 603
        m_folderDock->show();
cbc7d22b Poul Sander 2026-02-24 19:23 604
    if (m_previewDockWasVisible)
cbc7d22b Poul Sander 2026-02-24 19:23 605
        m_previewDock->show();
53fdab93 Poul Sander 2026-05-18 18:45 606
    if (m_exifDockWasVisible)
53fdab93 Poul Sander 2026-05-18 18:45 607
        m_exifDock->show();
61136b77 Poul Sander 2026-02-20 22:45 608
    m_listView->setFocus();
61136b77 Poul Sander 2026-02-20 22:45 609
}
61136b77 Poul Sander 2026-02-20 22:45 610
80a9926d Poul Sander 2026-05-19 20:28 611
QList<QByteArray> MainWindow::computeNeighborPaths(const QModelIndex &index) const
58103277 Poul Sander 2026-03-01 22:53 612
{
80a9926d Poul Sander 2026-05-19 20:28 613
    QList<QByteArray> paths;
58103277 Poul Sander 2026-03-01 22:53 614
    int row = index.row();
58103277 Poul Sander 2026-03-01 22:53 615
58103277 Poul Sander 2026-03-01 22:53 616
    // Collect up to CacheKeepBehind non-folder images before current
58103277 Poul Sander 2026-03-01 22:53 617
    int count = 0;
58103277 Poul Sander 2026-03-01 22:53 618
    for (int i = row - 1; i >= 0 && count < ImageViewWidget::CacheKeepBehind; --i) {
58103277 Poul Sander 2026-03-01 22:53 619
        QModelIndex idx = m_imageModel->index(i);
58103277 Poul Sander 2026-03-01 22:53 620
        if (!m_imageModel->isFolder(idx)) {
58103277 Poul Sander 2026-03-01 22:53 621
            paths.prepend(m_imageModel->filePath(idx));
58103277 Poul Sander 2026-03-01 22:53 622
            ++count;
58103277 Poul Sander 2026-03-01 22:53 623
        }
58103277 Poul Sander 2026-03-01 22:53 624
    }
58103277 Poul Sander 2026-03-01 22:53 625
58103277 Poul Sander 2026-03-01 22:53 626
    // Collect up to CacheReadAhead non-folder images after current
58103277 Poul Sander 2026-03-01 22:53 627
    count = 0;
58103277 Poul Sander 2026-03-01 22:53 628
    for (int i = row + 1; i < m_imageModel->rowCount() && count < ImageViewWidget::CacheReadAhead; ++i) {
58103277 Poul Sander 2026-03-01 22:53 629
        QModelIndex idx = m_imageModel->index(i);
58103277 Poul Sander 2026-03-01 22:53 630
        if (!m_imageModel->isFolder(idx)) {
58103277 Poul Sander 2026-03-01 22:53 631
            paths.append(m_imageModel->filePath(idx));
58103277 Poul Sander 2026-03-01 22:53 632
            ++count;
58103277 Poul Sander 2026-03-01 22:53 633
        }
58103277 Poul Sander 2026-03-01 22:53 634
    }
58103277 Poul Sander 2026-03-01 22:53 635
58103277 Poul Sander 2026-03-01 22:53 636
    return paths;
58103277 Poul Sander 2026-03-01 22:53 637
}
58103277 Poul Sander 2026-03-01 22:53 638
ccb726b8 Poul Sander 2026-02-23 22:43 639
void MainWindow::setFullScreenMode(bool fullScreen)
ccb726b8 Poul Sander 2026-02-23 22:43 640
{
ccb726b8 Poul Sander 2026-02-23 22:43 641
    if (fullScreen == isFullScreen())
ccb726b8 Poul Sander 2026-02-23 22:43 642
        return;
ccb726b8 Poul Sander 2026-02-23 22:43 643
ccb726b8 Poul Sander 2026-02-23 22:43 644
    if (fullScreen) {
ccb726b8 Poul Sander 2026-02-23 22:43 645
        m_wasMaximized = isMaximized();
ccb726b8 Poul Sander 2026-02-23 22:43 646
        m_menuBar->hide();
ccb726b8 Poul Sander 2026-02-23 22:43 647
        showFullScreen();
ccb726b8 Poul Sander 2026-02-23 22:43 648
    } else {
cbc7d22b Poul Sander 2026-02-24 19:23 649
        // Only restore menu bar when returning to browse mode
cbc7d22b Poul Sander 2026-02-24 19:23 650
        if (m_stack->currentWidget() != m_imageView)
cbc7d22b Poul Sander 2026-02-24 19:23 651
            m_menuBar->show();
ccb726b8 Poul Sander 2026-02-23 22:43 652
        if (m_wasMaximized)
ccb726b8 Poul Sander 2026-02-23 22:43 653
            showMaximized();
ccb726b8 Poul Sander 2026-02-23 22:43 654
        else
ccb726b8 Poul Sander 2026-02-23 22:43 655
            showNormal();
ccb726b8 Poul Sander 2026-02-23 22:43 656
    }
ccb726b8 Poul Sander 2026-02-23 22:43 657
}
ccb726b8 Poul Sander 2026-02-23 22:43 658
61136b77 Poul Sander 2026-02-20 22:45 659
void MainWindow::keyPressEvent(QKeyEvent *event)
61136b77 Poul Sander 2026-02-20 22:45 660
{
4d6b7476 Poul Sander 2026-05-30 16:39 661
    // THREADING: No race condition with F11 fullscreen toggle.  All cross-thread
4d6b7476 Poul Sander 2026-05-30 16:39 662
    // signal deliveries (thumbnailReady via explicit QueuedConnection,
4d6b7476 Poul Sander 2026-05-30 16:39 663
    // onImageLoaded via AutoConnection resolving to QueuedConnection) execute on
4d6b7476 Poul Sander 2026-05-30 16:39 664
    // the main thread's event loop.  The event loop processes events one at a
4d6b7476 Poul Sander 2026-05-30 16:39 665
    // time, so this handler and any signal slot can never interleave.  All widget
4d6b7476 Poul Sander 2026-05-30 16:39 666
    // state (m_wasMaximized, m_stack, fullscreen flag) is only accessed from the
4d6b7476 Poul Sander 2026-05-30 16:39 667
    // main thread.
83f0e0bc Poul Sander 2026-05-30 17:59 668
    const ShortcutManager &sc = ShortcutManager::instance();
83f0e0bc Poul Sander 2026-05-30 17:59 669
83f0e0bc Poul Sander 2026-05-30 17:59 670
    if (sc.matches(event, ShortcutManager::ToggleFullscreen)
83f0e0bc Poul Sander 2026-05-30 17:59 671
        && m_stack->currentWidget() == m_imageView)
61136b77 Poul Sander 2026-02-20 22:45 672
    {
ccb726b8 Poul Sander 2026-02-23 22:43 673
        setFullScreenMode(!isFullScreen());
61136b77 Poul Sander 2026-02-20 22:45 674
        event->accept();
61136b77 Poul Sander 2026-02-20 22:45 675
        return;
61136b77 Poul Sander 2026-02-20 22:45 676
    }
61136b77 Poul Sander 2026-02-20 22:45 677
83f0e0bc Poul Sander 2026-05-30 17:59 678
    if (sc.matches(event, ShortcutManager::ActivateEntry)
83f0e0bc Poul Sander 2026-05-30 17:59 679
        && m_stack->currentWidget() == m_browserPage)
61136b77 Poul Sander 2026-02-20 22:45 680
    {
61136b77 Poul Sander 2026-02-20 22:45 681
        QModelIndex current = m_listView->currentIndex();
61136b77 Poul Sander 2026-02-20 22:45 682
        if (current.isValid())
61136b77 Poul Sander 2026-02-20 22:45 683
        {
09130ab9 Poul Sander 2026-02-22 22:19 684
            if (m_imageModel->isFolder(current)) {
80a9926d Poul Sander 2026-05-19 20:28 685
                QByteArray path = m_imageModel->filePath(current);
665ac308 Poul Sander 2026-02-23 19:34 686
                navigateToFolder(path);
09130ab9 Poul Sander 2026-02-22 22:19 687
            } else {
09130ab9 Poul Sander 2026-02-22 22:19 688
                enterSingleImageMode(current);
09130ab9 Poul Sander 2026-02-22 22:19 689
            }
61136b77 Poul Sander 2026-02-20 22:45 690
            event->accept();
61136b77 Poul Sander 2026-02-20 22:45 691
            return;
61136b77 Poul Sander 2026-02-20 22:45 692
        }
61136b77 Poul Sander 2026-02-20 22:45 693
    }
61136b77 Poul Sander 2026-02-20 22:45 694
bf525669 Poul Sander 2026-05-30 23:18 695
    if (sc.matches(event, ShortcutManager::PrevFolder)
bf525669 Poul Sander 2026-05-30 23:18 696
        && m_stack->currentWidget() == m_browserPage)
bf525669 Poul Sander 2026-05-30 23:18 697
    {
bf525669 Poul Sander 2026-05-30 23:18 698
        navigateToPrevFolder();
bf525669 Poul Sander 2026-05-30 23:18 699
        event->accept();
bf525669 Poul Sander 2026-05-30 23:18 700
        return;
bf525669 Poul Sander 2026-05-30 23:18 701
    }
bf525669 Poul Sander 2026-05-30 23:18 702
bf525669 Poul Sander 2026-05-30 23:18 703
    if (sc.matches(event, ShortcutManager::NextFolder)
bf525669 Poul Sander 2026-05-30 23:18 704
        && m_stack->currentWidget() == m_browserPage)
bf525669 Poul Sander 2026-05-30 23:18 705
    {
bf525669 Poul Sander 2026-05-30 23:18 706
        navigateToNextFolder();
bf525669 Poul Sander 2026-05-30 23:18 707
        event->accept();
bf525669 Poul Sander 2026-05-30 23:18 708
        return;
bf525669 Poul Sander 2026-05-30 23:18 709
    }
bf525669 Poul Sander 2026-05-30 23:18 710
61136b77 Poul Sander 2026-02-20 22:45 711
    QMainWindow::keyPressEvent(event);
61136b77 Poul Sander 2026-02-20 22:45 712
}
c9e5684a Poul Sander 2026-02-23 19:18 713
c9e5684a Poul Sander 2026-02-23 19:18 714
void MainWindow::closeEvent(QCloseEvent *event)
c9e5684a Poul Sander 2026-02-23 19:18 715
{
c9e5684a Poul Sander 2026-02-23 19:18 716
    if (m_stack->currentWidget() == m_imageView)
c9e5684a Poul Sander 2026-02-23 19:18 717
    {
c9e5684a Poul Sander 2026-02-23 19:18 718
        leaveSingleImageMode();
c9e5684a Poul Sander 2026-02-23 19:18 719
        event->ignore();
c9e5684a Poul Sander 2026-02-23 19:18 720
        return;
c9e5684a Poul Sander 2026-02-23 19:18 721
    }
c9e5684a Poul Sander 2026-02-23 19:18 722
f61bf886 Poul Sander 2026-02-23 19:27 723
    savePreferences();
c9e5684a Poul Sander 2026-02-23 19:18 724
    QMainWindow::closeEvent(event);
c9e5684a Poul Sander 2026-02-23 19:18 725
}
f61bf886 Poul Sander 2026-02-23 19:27 726
4ae57f60 Poul Sander 2026-02-25 21:35 727
void MainWindow::applyDockLocking(bool lock)
4ae57f60 Poul Sander 2026-02-25 21:35 728
{
4ae57f60 Poul Sander 2026-02-25 21:35 729
    const QDockWidget::DockWidgetFeatures movableFeatures =
4ae57f60 Poul Sander 2026-02-25 21:35 730
        QDockWidget::DockWidgetMovable | QDockWidget::DockWidgetFloatable;
4ae57f60 Poul Sander 2026-02-25 21:35 731
    const QDockWidget::DockWidgetFeatures baseFeatures =
4ae57f60 Poul Sander 2026-02-25 21:35 732
        QDockWidget::DockWidgetClosable;
4ae57f60 Poul Sander 2026-02-25 21:35 733
4ae57f60 Poul Sander 2026-02-25 21:35 734
    QDockWidget::DockWidgetFeatures features = lock ? baseFeatures
4ae57f60 Poul Sander 2026-02-25 21:35 735
                                                    : (baseFeatures | movableFeatures);
3bee3e3e Poul Sander 2026-03-05 17:07 736
    m_rootDock->setFeatures(features);
4ae57f60 Poul Sander 2026-02-25 21:35 737
    m_folderDock->setFeatures(features);
4ae57f60 Poul Sander 2026-02-25 21:35 738
    m_previewDock->setFeatures(features);
53fdab93 Poul Sander 2026-05-18 18:45 739
    m_exifDock->setFeatures(features);
4ae57f60 Poul Sander 2026-02-25 21:35 740
}
4ae57f60 Poul Sander 2026-02-25 21:35 741
f61bf886 Poul Sander 2026-02-23 19:27 742
void MainWindow::loadPreferences()
f61bf886 Poul Sander 2026-02-23 19:27 743
{
f61bf886 Poul Sander 2026-02-23 19:27 744
    QSettings settings("SagoImageBrowser", "SagoImageBrowser");
f61bf886 Poul Sander 2026-02-23 19:27 745
    m_backgroundColorPreference = settings.value("backgroundColor", "black").toString();
f61bf886 Poul Sander 2026-02-23 19:27 746
    m_imageView->setBackgroundColor(m_backgroundColorPreference);
4ae57f60 Poul Sander 2026-02-25 21:35 747
    m_lockDockingPreference = settings.value("lockDocking", false).toBool();
4ae57f60 Poul Sander 2026-02-25 21:35 748
    applyDockLocking(m_lockDockingPreference);
459bad0f Poul Sander 2026-05-17 14:11 749
    m_folderCacheSizePreference = settings.value("folderCacheSize", 4).toInt();
459bad0f Poul Sander 2026-05-17 14:11 750
    m_imageModel->setCacheMaxSize(m_folderCacheSizePreference);
ac476ca8 Poul Sander 2026-05-18 16:47 751
    m_thumbnailCacheSizePreference = settings.value("thumbnailCacheSize", "normal").toString();
ac476ca8 Poul Sander 2026-05-18 16:47 752
    m_imageModel->setThumbnailSize(thumbnailSizeFromString(m_thumbnailCacheSizePreference));
83f0e0bc Poul Sander 2026-05-30 17:59 753
    ShortcutManager::instance().load(settings);
cbc7d22b Poul Sander 2026-02-24 19:23 754
    if (settings.contains("windowGeometry"))
cbc7d22b Poul Sander 2026-02-24 19:23 755
        restoreGeometry(settings.value("windowGeometry").toByteArray());
cbc7d22b Poul Sander 2026-02-24 19:23 756
    if (settings.contains("windowState"))
cbc7d22b Poul Sander 2026-02-24 19:23 757
        restoreState(settings.value("windowState").toByteArray());
f61bf886 Poul Sander 2026-02-23 19:27 758
}
f61bf886 Poul Sander 2026-02-23 19:27 759
f61bf886 Poul Sander 2026-02-23 19:27 760
void MainWindow::savePreferences()
f61bf886 Poul Sander 2026-02-23 19:27 761
{
f61bf886 Poul Sander 2026-02-23 19:27 762
    QSettings settings("SagoImageBrowser", "SagoImageBrowser");
f61bf886 Poul Sander 2026-02-23 19:27 763
    settings.setValue("backgroundColor", m_backgroundColorPreference);
4ae57f60 Poul Sander 2026-02-25 21:35 764
    settings.setValue("lockDocking", m_lockDockingPreference);
459bad0f Poul Sander 2026-05-17 14:11 765
    settings.setValue("folderCacheSize", m_folderCacheSizePreference);
ac476ca8 Poul Sander 2026-05-18 16:47 766
    settings.setValue("thumbnailCacheSize", m_thumbnailCacheSizePreference);
83f0e0bc Poul Sander 2026-05-30 17:59 767
    ShortcutManager::instance().save(settings);
cbc7d22b Poul Sander 2026-02-24 19:23 768
    settings.setValue("windowGeometry", saveGeometry());
cbc7d22b Poul Sander 2026-02-24 19:23 769
    settings.setValue("windowState", saveState());
f61bf886 Poul Sander 2026-02-23 19:27 770
}
f61bf886 Poul Sander 2026-02-23 19:27 771
f61bf886 Poul Sander 2026-02-23 19:27 772
void MainWindow::onPreferencesTriggered()
f61bf886 Poul Sander 2026-02-23 19:27 773
{
f61bf886 Poul Sander 2026-02-23 19:27 774
    PreferencesDialog dialog(this);
f61bf886 Poul Sander 2026-02-23 19:27 775
    dialog.setBackgroundColor(m_backgroundColorPreference);
4ae57f60 Poul Sander 2026-02-25 21:35 776
    dialog.setLockDocking(m_lockDockingPreference);
459bad0f Poul Sander 2026-05-17 14:11 777
    dialog.setCacheSize(m_folderCacheSizePreference);
ac476ca8 Poul Sander 2026-05-18 16:47 778
    dialog.setThumbnailCacheSize(m_thumbnailCacheSizePreference);
f61bf886 Poul Sander 2026-02-23 19:27 779
83f0e0bc Poul Sander 2026-05-30 17:59 780
    // Populate shortcuts tab from current bindings
83f0e0bc Poul Sander 2026-05-30 17:59 781
    {
83f0e0bc Poul Sander 2026-05-30 17:59 782
        QMap<int, QKeySequence> scMap;
83f0e0bc Poul Sander 2026-05-30 17:59 783
        for (int i = 0; i < ShortcutManager::ActionCount; ++i)
83f0e0bc Poul Sander 2026-05-30 17:59 784
            scMap[i] = ShortcutManager::instance().shortcut(
83f0e0bc Poul Sander 2026-05-30 17:59 785
                static_cast<ShortcutManager::Action>(i));
83f0e0bc Poul Sander 2026-05-30 17:59 786
        dialog.setShortcuts(scMap);
83f0e0bc Poul Sander 2026-05-30 17:59 787
    }
83f0e0bc Poul Sander 2026-05-30 17:59 788
7370f1c0 Poul Sander 2026-03-01 15:15 789
    bool shouldResetLayout = false;
7370f1c0 Poul Sander 2026-03-01 15:15 790
    connect(&dialog, &PreferencesDialog::resetLayoutRequested, this, [&shouldResetLayout]() {
7370f1c0 Poul Sander 2026-03-01 15:15 791
        shouldResetLayout = true;
7370f1c0 Poul Sander 2026-03-01 15:15 792
    });
7370f1c0 Poul Sander 2026-03-01 15:15 793
f61bf886 Poul Sander 2026-02-23 19:27 794
    if (dialog.exec() == QDialog::Accepted)
f61bf886 Poul Sander 2026-02-23 19:27 795
    {
f61bf886 Poul Sander 2026-02-23 19:27 796
        m_backgroundColorPreference = dialog.getBackgroundColor();
f61bf886 Poul Sander 2026-02-23 19:27 797
        m_imageView->setBackgroundColor(m_backgroundColorPreference);
4ae57f60 Poul Sander 2026-02-25 21:35 798
        m_lockDockingPreference = dialog.getLockDocking();
4ae57f60 Poul Sander 2026-02-25 21:35 799
        applyDockLocking(m_lockDockingPreference);
459bad0f Poul Sander 2026-05-17 14:11 800
        m_folderCacheSizePreference = dialog.getCacheSize();
459bad0f Poul Sander 2026-05-17 14:11 801
        m_imageModel->setCacheMaxSize(m_folderCacheSizePreference);
ac476ca8 Poul Sander 2026-05-18 16:47 802
        QString newThumbSize = dialog.getThumbnailCacheSize();
ac476ca8 Poul Sander 2026-05-18 16:47 803
        if (newThumbSize != m_thumbnailCacheSizePreference)
ac476ca8 Poul Sander 2026-05-18 16:47 804
        {
ac476ca8 Poul Sander 2026-05-18 16:47 805
            m_thumbnailCacheSizePreference = newThumbSize;
ac476ca8 Poul Sander 2026-05-18 16:47 806
            m_imageModel->setThumbnailSize(thumbnailSizeFromString(m_thumbnailCacheSizePreference));
ac476ca8 Poul Sander 2026-05-18 16:47 807
            loadVisibleThumbnails();
ac476ca8 Poul Sander 2026-05-18 16:47 808
        }
83f0e0bc Poul Sander 2026-05-30 17:59 809
83f0e0bc Poul Sander 2026-05-30 17:59 810
        // Apply shortcut changes
83f0e0bc Poul Sander 2026-05-30 17:59 811
        QMap<int, QKeySequence> scMap = dialog.getShortcuts();
83f0e0bc Poul Sander 2026-05-30 17:59 812
        for (auto it = scMap.cbegin(); it != scMap.cend(); ++it)
83f0e0bc Poul Sander 2026-05-30 17:59 813
            ShortcutManager::instance().setShortcut(
83f0e0bc Poul Sander 2026-05-30 17:59 814
                static_cast<ShortcutManager::Action>(it.key()), it.value());
83f0e0bc Poul Sander 2026-05-30 17:59 815
f61bf886 Poul Sander 2026-02-23 19:27 816
        savePreferences();
f61bf886 Poul Sander 2026-02-23 19:27 817
    }
7370f1c0 Poul Sander 2026-03-01 15:15 818
    
7370f1c0 Poul Sander 2026-03-01 15:15 819
    // Apply layout reset immediately after dialog closes if requested
7370f1c0 Poul Sander 2026-03-01 15:15 820
    if (shouldResetLayout) {
7370f1c0 Poul Sander 2026-03-01 15:15 821
        resetLayoutToDefault();
7370f1c0 Poul Sander 2026-03-01 15:15 822
    }
7370f1c0 Poul Sander 2026-03-01 15:15 823
}
7370f1c0 Poul Sander 2026-03-01 15:15 824
7370f1c0 Poul Sander 2026-03-01 15:15 825
void MainWindow::resetLayoutToDefault()
7370f1c0 Poul Sander 2026-03-01 15:15 826
{
7370f1c0 Poul Sander 2026-03-01 15:15 827
    QSettings settings("SagoImageBrowser", "SagoImageBrowser");
7370f1c0 Poul Sander 2026-03-01 15:15 828
    settings.remove("windowGeometry");
7370f1c0 Poul Sander 2026-03-01 15:15 829
    settings.remove("windowState");
7370f1c0 Poul Sander 2026-03-01 15:15 830
    
7370f1c0 Poul Sander 2026-03-01 15:15 831
    // Hide and detach docks completely
3bee3e3e Poul Sander 2026-03-05 17:07 832
    m_rootDock->hide();
7370f1c0 Poul Sander 2026-03-01 15:15 833
    m_folderDock->hide();
7370f1c0 Poul Sander 2026-03-01 15:15 834
    m_previewDock->hide();
7370f1c0 Poul Sander 2026-03-01 15:15 835
    
3bee3e3e Poul Sander 2026-03-05 17:07 836
    m_rootDock->setFloating(false);
7370f1c0 Poul Sander 2026-03-01 15:15 837
    m_folderDock->setFloating(false);
7370f1c0 Poul Sander 2026-03-01 15:15 838
    m_previewDock->setFloating(false);
7370f1c0 Poul Sander 2026-03-01 15:15 839
    
3bee3e3e Poul Sander 2026-03-05 17:07 840
    removeDockWidget(m_rootDock);
7370f1c0 Poul Sander 2026-03-01 15:15 841
    removeDockWidget(m_folderDock);
7370f1c0 Poul Sander 2026-03-01 15:15 842
    removeDockWidget(m_previewDock);
7370f1c0 Poul Sander 2026-03-01 15:15 843
    
7370f1c0 Poul Sander 2026-03-01 15:15 844
    // Re-add in default positions
3bee3e3e Poul Sander 2026-03-05 17:07 845
    addDockWidget(Qt::LeftDockWidgetArea, m_rootDock);
7370f1c0 Poul Sander 2026-03-01 15:15 846
    addDockWidget(Qt::LeftDockWidgetArea, m_folderDock);
7370f1c0 Poul Sander 2026-03-01 15:15 847
    addDockWidget(Qt::RightDockWidgetArea, m_previewDock);
3bee3e3e Poul Sander 2026-03-05 17:07 848
    splitDockWidget(m_rootDock, m_folderDock, Qt::Vertical);
7370f1c0 Poul Sander 2026-03-01 15:15 849
    
3bee3e3e Poul Sander 2026-03-05 17:07 850
    // Show all docks
3bee3e3e Poul Sander 2026-03-05 17:07 851
    m_rootDock->show();
7370f1c0 Poul Sander 2026-03-01 15:15 852
    m_folderDock->show();
7370f1c0 Poul Sander 2026-03-01 15:15 853
    m_previewDock->show();
f61bf886 Poul Sander 2026-02-23 19:27 854
}
1970-01-01 00:00 855