git repos / SagoImageBrowser

blame: src/mainwindow.cpp

normal view · raw

14cf904f Poul Sander 2026-06-22 18:18 1
/*
14cf904f Poul Sander 2026-06-22 18:18 2
MIT License
14cf904f Poul Sander 2026-06-22 18:18 3
14cf904f Poul Sander 2026-06-22 18:18 4
Copyright (c) 2026 Poul Sander
14cf904f Poul Sander 2026-06-22 18:18 5
14cf904f Poul Sander 2026-06-22 18:18 6
Permission is hereby granted, free of charge, to any person obtaining a copy
14cf904f Poul Sander 2026-06-22 18:18 7
of this software and associated documentation files (the "Software"), to deal
14cf904f Poul Sander 2026-06-22 18:18 8
in the Software without restriction, including without limitation the rights
14cf904f Poul Sander 2026-06-22 18:18 9
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
14cf904f Poul Sander 2026-06-22 18:18 10
copies of the Software, and to permit persons to whom the Software is
14cf904f Poul Sander 2026-06-22 18:18 11
furnished to do so, subject to the following conditions:
14cf904f Poul Sander 2026-06-22 18:18 12
14cf904f Poul Sander 2026-06-22 18:18 13
The above copyright notice and this permission notice shall be included in all
14cf904f Poul Sander 2026-06-22 18:18 14
copies or substantial portions of the Software.
14cf904f Poul Sander 2026-06-22 18:18 15
14cf904f Poul Sander 2026-06-22 18:18 16
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
14cf904f Poul Sander 2026-06-22 18:18 17
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
14cf904f Poul Sander 2026-06-22 18:18 18
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
14cf904f Poul Sander 2026-06-22 18:18 19
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
14cf904f Poul Sander 2026-06-22 18:18 20
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
14cf904f Poul Sander 2026-06-22 18:18 21
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
14cf904f Poul Sander 2026-06-22 18:18 22
SOFTWARE.
14cf904f Poul Sander 2026-06-22 18:18 23
14cf904f Poul Sander 2026-06-22 18:18 24
*/
14cf904f Poul Sander 2026-06-22 18:18 25
032aa1e1 Poul Sander 2026-02-19 18:55 26
#include "mainwindow.h"
032aa1e1 Poul Sander 2026-02-19 18:55 27
#include "imagemodel.h"
61136b77 Poul Sander 2026-02-20 22:45 28
#include "imageviewwidget.h"
f61bf886 Poul Sander 2026-02-23 19:27 29
#include "preferencesdialog.h"
83f0e0bc Poul Sander 2026-05-30 17:59 30
#include "shortcutmanager.h"
ac476ca8 Poul Sander 2026-05-18 16:47 31
#include "thumbnailcache.h"
53fdab93 Poul Sander 2026-05-18 18:45 32
#include "exifreader.h"
40efdfde Poul Sander 2026-05-19 21:24 33
#include "fsdirmodel.h"
fd7f51cd Poul Sander 2026-05-22 15:30 34
#include "pathcompletermodel.h"
1dcea855 Poul Sander 2026-05-26 21:48 35
#include "thumbnaildelegate.h"
032aa1e1 Poul Sander 2026-02-19 18:55 36
40efdfde Poul Sander 2026-05-19 21:24 37
#include <QTreeView>
032aa1e1 Poul Sander 2026-02-19 18:55 38
#include <QLabel>
61136b77 Poul Sander 2026-02-20 22:45 39
#include <QStackedWidget>
032aa1e1 Poul Sander 2026-02-19 18:55 40
#include <QDir>
3bee3e3e Poul Sander 2026-03-05 17:07 41
#include <QListWidget>
fc5f2e27 Poul Sander 2026-02-22 22:06 42
#include <QStandardPaths>
032aa1e1 Poul Sander 2026-02-19 18:55 43
#include <QImageReader>
032aa1e1 Poul Sander 2026-02-19 18:55 44
#include <QHeaderView>
6fcd6c90 Poul Sander 2026-02-19 21:12 45
#include <QScrollBar>
6fcd6c90 Poul Sander 2026-02-19 21:12 46
#include <QTimer>
6fcd6c90 Poul Sander 2026-02-19 21:12 47
#include <QEvent>
cfe4a67b Poul Sander 2026-02-19 23:06 48
#include <QItemSelectionModel>
cbc7d22b Poul Sander 2026-02-24 19:23 49
#include <QDockWidget>
f61bf886 Poul Sander 2026-02-23 19:27 50
#include <QMenuBar>
f61bf886 Poul Sander 2026-02-23 19:27 51
#include <QMenu>
f61bf886 Poul Sander 2026-02-23 19:27 52
#include <QAction>
f61bf886 Poul Sander 2026-02-23 19:27 53
#include <QSettings>
53fdab93 Poul Sander 2026-05-18 18:45 54
#include <QTableWidget>
cdae9502 Poul Sander 2026-06-09 20:04 55
#include <QDesktopServices>
cdae9502 Poul Sander 2026-06-09 20:04 56
#include <QUrl>
beb4a823 Poul Sander 2026-05-19 21:53 57
#include <QDialog>
fd7f51cd Poul Sander 2026-05-22 15:30 58
#include <QHBoxLayout>
beb4a823 Poul Sander 2026-05-19 21:53 59
#include <QPlainTextEdit>
835a6bea Poul Sander 2026-05-19 22:03 60
#include <QMessageBox>
fd7f51cd Poul Sander 2026-05-22 15:30 61
#include <QLineEdit>
fd7f51cd Poul Sander 2026-05-22 15:30 62
#include <QPushButton>
fd7f51cd Poul Sander 2026-05-22 15:30 63
#include <QCompleter>
237d2c81 Poul Sander 2026-06-06 14:53 64
#include <QClipboard>
237d2c81 Poul Sander 2026-06-06 14:53 65
#include <QDialogButtonBox>
700d6f2d Poul Sander 2026-06-06 15:44 66
#include <QFile>
237d2c81 Poul Sander 2026-06-06 14:53 67
#include <QFileInfo>
237d2c81 Poul Sander 2026-06-06 14:53 68
#include <QGuiApplication>
237d2c81 Poul Sander 2026-06-06 14:53 69
#include <QVBoxLayout>
6fcd6c90 Poul Sander 2026-02-19 21:12 70
#include <iostream>
032aa1e1 Poul Sander 2026-02-19 18:55 71
80a9926d Poul Sander 2026-05-19 20:28 72
#include <fcntl.h>
bf525669 Poul Sander 2026-05-30 23:18 73
#include <filesystem>
80a9926d Poul Sander 2026-05-19 20:28 74
#include <sys/stat.h>
80a9926d Poul Sander 2026-05-19 20:28 75
#include <unistd.h>
80a9926d Poul Sander 2026-05-19 20:28 76
ac476ca8 Poul Sander 2026-05-18 16:47 77
namespace
ac476ca8 Poul Sander 2026-05-18 16:47 78
{
ac476ca8 Poul Sander 2026-05-18 16:47 79
ThumbnailCache::Size thumbnailSizeFromString(const QString &s)
ac476ca8 Poul Sander 2026-05-18 16:47 80
{
ac476ca8 Poul Sander 2026-05-18 16:47 81
    return (s == "large") ? ThumbnailCache::Size::Large
ac476ca8 Poul Sander 2026-05-18 16:47 82
                          : ThumbnailCache::Size::Normal;
ac476ca8 Poul Sander 2026-05-18 16:47 83
}
237d2c81 Poul Sander 2026-06-06 14:53 84
237d2c81 Poul Sander 2026-06-06 14:53 85
QString displayPath(const QByteArray &path)
237d2c81 Poul Sander 2026-06-06 14:53 86
{
237d2c81 Poul Sander 2026-06-06 14:53 87
    return QString::fromLocal8Bit(path.constData(), path.size());
237d2c81 Poul Sander 2026-06-06 14:53 88
}
237d2c81 Poul Sander 2026-06-06 14:53 89
237d2c81 Poul Sander 2026-06-06 14:53 90
QString filenameFromPath(const QByteArray &path)
237d2c81 Poul Sander 2026-06-06 14:53 91
{
237d2c81 Poul Sander 2026-06-06 14:53 92
    const std::filesystem::path fsPath(path.toStdString());
237d2c81 Poul Sander 2026-06-06 14:53 93
    const std::string nativeName = fsPath.filename().native();
237d2c81 Poul Sander 2026-06-06 14:53 94
    return QString::fromLocal8Bit(nativeName.data(),
237d2c81 Poul Sander 2026-06-06 14:53 95
                                  static_cast<qsizetype>(nativeName.size()));
237d2c81 Poul Sander 2026-06-06 14:53 96
}
ac476ca8 Poul Sander 2026-05-18 16:47 97
} // namespace
ac476ca8 Poul Sander 2026-05-18 16:47 98
032aa1e1 Poul Sander 2026-02-19 18:55 99
MainWindow::MainWindow(QWidget *parent)
032aa1e1 Poul Sander 2026-02-19 18:55 100
    : QMainWindow(parent)
032aa1e1 Poul Sander 2026-02-19 18:55 101
{
032aa1e1 Poul Sander 2026-02-19 18:55 102
    setupUi();
f61bf886 Poul Sander 2026-02-23 19:27 103
    setupMenuBar();
032aa1e1 Poul Sander 2026-02-19 18:55 104
    setupConnections();
f61bf886 Poul Sander 2026-02-23 19:27 105
    loadPreferences();
032aa1e1 Poul Sander 2026-02-19 18:55 106
}
032aa1e1 Poul Sander 2026-02-19 18:55 107
032aa1e1 Poul Sander 2026-02-19 18:55 108
void MainWindow::setupUi()
032aa1e1 Poul Sander 2026-02-19 18:55 109
{
40efdfde Poul Sander 2026-05-19 21:24 110
    // Folder model (left pane) — backed by std::filesystem so directories with
40efdfde Poul Sander 2026-05-19 21:24 111
    // non-UTF-8 filenames are listed correctly.
40efdfde Poul Sander 2026-05-19 21:24 112
    m_dirModel = new FsDirModel(this);
032aa1e1 Poul Sander 2026-02-19 18:55 113
    m_dirModel->setRootPath(QDir::homePath());
032aa1e1 Poul Sander 2026-02-19 18:55 114
3bee3e3e Poul Sander 2026-03-05 17:07 115
    // Root chooser list
3bee3e3e Poul Sander 2026-03-05 17:07 116
    m_rootList = new QListWidget;
3bee3e3e Poul Sander 2026-03-05 17:07 117
    m_rootList->setSelectionMode(QAbstractItemView::SingleSelection);
3bee3e3e Poul Sander 2026-03-05 17:07 118
032aa1e1 Poul Sander 2026-02-19 18:55 119
    m_treeView = new QTreeView;
032aa1e1 Poul Sander 2026-02-19 18:55 120
    m_treeView->setModel(m_dirModel);
40efdfde Poul Sander 2026-05-19 21:24 121
    m_treeView->setRootIndex(m_dirModel->rootIndex());
032aa1e1 Poul Sander 2026-02-19 18:55 122
    m_treeView->header()->hide();
032aa1e1 Poul Sander 2026-02-19 18:55 123
    m_treeView->setColumnHidden(1, true);
032aa1e1 Poul Sander 2026-02-19 18:55 124
    m_treeView->setColumnHidden(2, true);
032aa1e1 Poul Sander 2026-02-19 18:55 125
    m_treeView->setColumnHidden(3, true);
032aa1e1 Poul Sander 2026-02-19 18:55 126
032aa1e1 Poul Sander 2026-02-19 18:55 127
    // Thumbnail model (center pane)
032aa1e1 Poul Sander 2026-02-19 18:55 128
    m_imageModel = new ImageModel(this);
032aa1e1 Poul Sander 2026-02-19 18:55 129
032aa1e1 Poul Sander 2026-02-19 18:55 130
    m_listView = new QListView;
032aa1e1 Poul Sander 2026-02-19 18:55 131
    m_listView->setModel(m_imageModel);
032aa1e1 Poul Sander 2026-02-19 18:55 132
    m_listView->setViewMode(QListView::IconMode);
032aa1e1 Poul Sander 2026-02-19 18:55 133
    m_listView->setIconSize(QSize(128, 128));
b614a581 Poul Sander 2026-02-19 21:18 134
    m_listView->setGridSize(QSize(148, 168));
032aa1e1 Poul Sander 2026-02-19 18:55 135
    m_listView->setResizeMode(QListView::Adjust);
032aa1e1 Poul Sander 2026-02-19 18:55 136
    m_listView->setSelectionMode(QAbstractItemView::SingleSelection);
b614a581 Poul Sander 2026-02-19 21:18 137
    m_listView->setSpacing(4);
b614a581 Poul Sander 2026-02-19 21:18 138
    m_listView->setWordWrap(true);
b614a581 Poul Sander 2026-02-19 21:18 139
    m_listView->setTextElideMode(Qt::ElideRight);
1dcea855 Poul Sander 2026-05-26 21:48 140
    m_listView->setItemDelegate(new ThumbnailDelegate(this));
237d2c81 Poul Sander 2026-06-06 14:53 141
    m_listView->setContextMenuPolicy(Qt::CustomContextMenu);
032aa1e1 Poul Sander 2026-02-19 18:55 142
032aa1e1 Poul Sander 2026-02-19 18:55 143
    // Preview pane (right)
032aa1e1 Poul Sander 2026-02-19 18:55 144
    m_previewLabel = new QLabel;
032aa1e1 Poul Sander 2026-02-19 18:55 145
    m_previewLabel->setAlignment(Qt::AlignCenter);
cbc7d22b Poul Sander 2026-02-24 19:23 146
    m_previewLabel->setMinimumWidth(200);
61136b77 Poul Sander 2026-02-20 22:45 147
61136b77 Poul Sander 2026-02-20 22:45 148
    // Single image view
61136b77 Poul Sander 2026-02-20 22:45 149
    m_imageView = new ImageViewWidget;
d93c1ab0 Poul Sander 2026-06-08 21:44 150
    m_imageView->setContextMenuPolicy(Qt::CustomContextMenu);
61136b77 Poul Sander 2026-02-20 22:45 151
fd7f51cd Poul Sander 2026-05-22 15:30 152
    // Path bar: editable absolute path with ajax-style folder completion and a Go button
fd7f51cd Poul Sander 2026-05-22 15:30 153
    m_pathEdit = new QLineEdit;
fd7f51cd Poul Sander 2026-05-22 15:30 154
    m_goButton = new QPushButton(tr("Go"));
bf525669 Poul Sander 2026-05-30 23:18 155
    m_prevFolderButton = new QPushButton(tr("Prev"));
bf525669 Poul Sander 2026-05-30 23:18 156
    m_nextFolderButton = new QPushButton(tr("Next"));
fd7f51cd Poul Sander 2026-05-22 15:30 157
    m_pathCompleterModel = new PathCompleterModel(this);
fd7f51cd Poul Sander 2026-05-22 15:30 158
    m_pathCompleter = new QCompleter(m_pathCompleterModel, this);
fd7f51cd Poul Sander 2026-05-22 15:30 159
    m_pathCompleter->setCaseSensitivity(Qt::CaseSensitive);
fd7f51cd Poul Sander 2026-05-22 15:30 160
    m_pathCompleter->setCompletionMode(QCompleter::PopupCompletion);
fd7f51cd Poul Sander 2026-05-22 15:30 161
    m_pathCompleter->setFilterMode(Qt::MatchStartsWith);
fd7f51cd Poul Sander 2026-05-22 15:30 162
    m_pathEdit->setCompleter(m_pathCompleter);
fd7f51cd Poul Sander 2026-05-22 15:30 163
fd7f51cd Poul Sander 2026-05-22 15:30 164
    auto *pathRow = new QHBoxLayout;
fd7f51cd Poul Sander 2026-05-22 15:30 165
    pathRow->setContentsMargins(4, 4, 4, 0);
fd7f51cd Poul Sander 2026-05-22 15:30 166
    pathRow->addWidget(m_pathEdit, 1);
fd7f51cd Poul Sander 2026-05-22 15:30 167
    pathRow->addWidget(m_goButton);
bf525669 Poul Sander 2026-05-30 23:18 168
    pathRow->addWidget(m_prevFolderButton);
bf525669 Poul Sander 2026-05-30 23:18 169
    pathRow->addWidget(m_nextFolderButton);
fd7f51cd Poul Sander 2026-05-22 15:30 170
fd7f51cd Poul Sander 2026-05-22 15:30 171
    auto *browserPage = new QWidget;
fd7f51cd Poul Sander 2026-05-22 15:30 172
    auto *vbox = new QVBoxLayout(browserPage);
fd7f51cd Poul Sander 2026-05-22 15:30 173
    vbox->setContentsMargins(0, 0, 0, 0);
fd7f51cd Poul Sander 2026-05-22 15:30 174
    vbox->setSpacing(0);
fd7f51cd Poul Sander 2026-05-22 15:30 175
    vbox->addLayout(pathRow);
fd7f51cd Poul Sander 2026-05-22 15:30 176
    vbox->addWidget(m_listView, 1);
fd7f51cd Poul Sander 2026-05-22 15:30 177
61136b77 Poul Sander 2026-02-20 22:45 178
    // Stack to switch between browser and single image
61136b77 Poul Sander 2026-02-20 22:45 179
    m_stack = new QStackedWidget;
fd7f51cd Poul Sander 2026-05-22 15:30 180
    m_stack->addWidget(browserPage);
61136b77 Poul Sander 2026-02-20 22:45 181
    m_stack->addWidget(m_imageView);
fd7f51cd Poul Sander 2026-05-22 15:30 182
    m_browserPage = browserPage;
61136b77 Poul Sander 2026-02-20 22:45 183
61136b77 Poul Sander 2026-02-20 22:45 184
    setCentralWidget(m_stack);
cbc7d22b Poul Sander 2026-02-24 19:23 185
fd7f51cd Poul Sander 2026-05-22 15:30 186
    updatePathField(QFile::encodeName(QDir::homePath()));
fd7f51cd Poul Sander 2026-05-22 15:30 187
cbc7d22b Poul Sander 2026-02-24 19:23 188
    // Dock widgets — can be dragged, floated and stacked by the user
3bee3e3e Poul Sander 2026-03-05 17:07 189
    m_rootDock = new QDockWidget(tr("Roots"), this);
3bee3e3e Poul Sander 2026-03-05 17:07 190
    m_rootDock->setObjectName("RootDock");
3bee3e3e Poul Sander 2026-03-05 17:07 191
    m_rootDock->setWidget(m_rootList);
3bee3e3e Poul Sander 2026-03-05 17:07 192
    addDockWidget(Qt::LeftDockWidgetArea, m_rootDock);
3bee3e3e Poul Sander 2026-03-05 17:07 193
cbc7d22b Poul Sander 2026-02-24 19:23 194
    m_folderDock = new QDockWidget(tr("Folders"), this);
cbc7d22b Poul Sander 2026-02-24 19:23 195
    m_folderDock->setObjectName("FolderDock");
3bee3e3e Poul Sander 2026-03-05 17:07 196
    m_folderDock->setWidget(m_treeView);
cbc7d22b Poul Sander 2026-02-24 19:23 197
    addDockWidget(Qt::LeftDockWidgetArea, m_folderDock);
3bee3e3e Poul Sander 2026-03-05 17:07 198
    splitDockWidget(m_rootDock, m_folderDock, Qt::Vertical);
cbc7d22b Poul Sander 2026-02-24 19:23 199
cbc7d22b Poul Sander 2026-02-24 19:23 200
    m_previewDock = new QDockWidget(tr("Preview"), this);
cbc7d22b Poul Sander 2026-02-24 19:23 201
    m_previewDock->setObjectName("PreviewDock");
cbc7d22b Poul Sander 2026-02-24 19:23 202
    m_previewDock->setWidget(m_previewLabel);
cbc7d22b Poul Sander 2026-02-24 19:23 203
    addDockWidget(Qt::RightDockWidgetArea, m_previewDock);
53fdab93 Poul Sander 2026-05-18 18:45 204
53fdab93 Poul Sander 2026-05-18 18:45 205
    // EXIF info dock — placed below the preview dock by default
53fdab93 Poul Sander 2026-05-18 18:45 206
    m_exifTable = new QTableWidget(0, 2);
53fdab93 Poul Sander 2026-05-18 18:45 207
    m_exifTable->setHorizontalHeaderLabels({tr("Field"), tr("Value")});
53fdab93 Poul Sander 2026-05-18 18:45 208
    m_exifTable->horizontalHeader()->setStretchLastSection(true);
53fdab93 Poul Sander 2026-05-18 18:45 209
    m_exifTable->verticalHeader()->hide();
53fdab93 Poul Sander 2026-05-18 18:45 210
    m_exifTable->setEditTriggers(QAbstractItemView::NoEditTriggers);
53fdab93 Poul Sander 2026-05-18 18:45 211
    m_exifTable->setSelectionMode(QAbstractItemView::NoSelection);
53fdab93 Poul Sander 2026-05-18 18:45 212
    m_exifTable->setShowGrid(false);
53fdab93 Poul Sander 2026-05-18 18:45 213
    m_exifTable->setAlternatingRowColors(true);
53fdab93 Poul Sander 2026-05-18 18:45 214
    m_exifTable->setMinimumWidth(200);
53fdab93 Poul Sander 2026-05-18 18:45 215
53fdab93 Poul Sander 2026-05-18 18:45 216
    m_exifDock = new QDockWidget(tr("EXIF Info"), this);
53fdab93 Poul Sander 2026-05-18 18:45 217
    m_exifDock->setObjectName("ExifDock");
53fdab93 Poul Sander 2026-05-18 18:45 218
    m_exifDock->setWidget(m_exifTable);
53fdab93 Poul Sander 2026-05-18 18:45 219
    addDockWidget(Qt::RightDockWidgetArea, m_exifDock);
53fdab93 Poul Sander 2026-05-18 18:45 220
    splitDockWidget(m_previewDock, m_exifDock, Qt::Vertical);
032aa1e1 Poul Sander 2026-02-19 18:55 221
}
032aa1e1 Poul Sander 2026-02-19 18:55 222
f61bf886 Poul Sander 2026-02-23 19:27 223
void MainWindow::setupMenuBar()
f61bf886 Poul Sander 2026-02-23 19:27 224
{
ccb726b8 Poul Sander 2026-02-23 22:43 225
    m_menuBar = menuBar();
0df01dc1 Poul Sander 2026-02-24 18:51 226
    m_menuBar->setStyleSheet(
0df01dc1 Poul Sander 2026-02-24 18:51 227
        "QMenuBar { padding: 1px 2px; }"
0df01dc1 Poul Sander 2026-02-24 18:51 228
        "QMenuBar::item { padding: 2px 6px; }"
0df01dc1 Poul Sander 2026-02-24 18:51 229
    );
4c41f5ff Poul Sander 2026-05-31 00:01 230
    QMenu *viewMenu = m_menuBar->addMenu(tr("&View"));
3bee3e3e Poul Sander 2026-03-05 17:07 231
    viewMenu->addAction(m_rootDock->toggleViewAction());
cbc7d22b Poul Sander 2026-02-24 19:23 232
    viewMenu->addAction(m_folderDock->toggleViewAction());
cbc7d22b Poul Sander 2026-02-24 19:23 233
    viewMenu->addAction(m_previewDock->toggleViewAction());
53fdab93 Poul Sander 2026-05-18 18:45 234
    viewMenu->addAction(m_exifDock->toggleViewAction());
cbc7d22b Poul Sander 2026-02-24 19:23 235
4c41f5ff Poul Sander 2026-05-31 00:01 236
    QMenu *editMenu = m_menuBar->addMenu(tr("&Edit"));
4c41f5ff Poul Sander 2026-05-31 00:01 237
    QAction *preferencesAction = editMenu->addAction(tr("&Preferences"));
f61bf886 Poul Sander 2026-02-23 19:27 238
    connect(preferencesAction, &QAction::triggered, this, &MainWindow::onPreferencesTriggered);
f61bf886 Poul Sander 2026-02-23 19:27 239
}
f61bf886 Poul Sander 2026-02-23 19:27 240
665ac308 Poul Sander 2026-02-23 19:34 241
void MainWindow::navigateToFolder(const QString &path)
665ac308 Poul Sander 2026-02-23 19:34 242
{
80a9926d Poul Sander 2026-05-19 20:28 243
    navigateToFolder(QFile::encodeName(path));
80a9926d Poul Sander 2026-05-19 20:28 244
}
80a9926d Poul Sander 2026-05-19 20:28 245
bf525669 Poul Sander 2026-05-30 23:18 246
QByteArray MainWindow::siblingFolder(int delta) const
bf525669 Poul Sander 2026-05-30 23:18 247
{
bf525669 Poul Sander 2026-05-30 23:18 248
    QByteArray currentDir = m_imageModel->currentDirectory();
bf525669 Poul Sander 2026-05-30 23:18 249
    if (currentDir.isEmpty())
bf525669 Poul Sander 2026-05-30 23:18 250
        return {};
bf525669 Poul Sander 2026-05-30 23:18 251
bf525669 Poul Sander 2026-05-30 23:18 252
    namespace fs = std::filesystem;
bf525669 Poul Sander 2026-05-30 23:18 253
    fs::path current(currentDir.toStdString());
bf525669 Poul Sander 2026-05-30 23:18 254
    fs::path parent = current.parent_path();
bf525669 Poul Sander 2026-05-30 23:18 255
    if (parent.empty() || parent == current)
bf525669 Poul Sander 2026-05-30 23:18 256
        return {};
bf525669 Poul Sander 2026-05-30 23:18 257
bf525669 Poul Sander 2026-05-30 23:18 258
    QByteArray parentPath = QByteArray::fromStdString(parent.native());
bf525669 Poul Sander 2026-05-30 23:18 259
    QModelIndex parentIdx = m_dirModel->indexForPath(parentPath);
bf525669 Poul Sander 2026-05-30 23:18 260
    if (!parentIdx.isValid())
bf525669 Poul Sander 2026-05-30 23:18 261
        return {};
bf525669 Poul Sander 2026-05-30 23:18 262
bf525669 Poul Sander 2026-05-30 23:18 263
    int count = m_dirModel->rowCount(parentIdx);
bf525669 Poul Sander 2026-05-30 23:18 264
    int currentRow = -1;
bf525669 Poul Sander 2026-05-30 23:18 265
    for (int i = 0; i < count; ++i) {
bf525669 Poul Sander 2026-05-30 23:18 266
        QModelIndex idx = m_dirModel->index(i, 0, parentIdx);
bf525669 Poul Sander 2026-05-30 23:18 267
        if (m_dirModel->filePath(idx) == currentDir) {
bf525669 Poul Sander 2026-05-30 23:18 268
            currentRow = i;
bf525669 Poul Sander 2026-05-30 23:18 269
            break;
bf525669 Poul Sander 2026-05-30 23:18 270
        }
bf525669 Poul Sander 2026-05-30 23:18 271
    }
bf525669 Poul Sander 2026-05-30 23:18 272
bf525669 Poul Sander 2026-05-30 23:18 273
    if (currentRow < 0)
bf525669 Poul Sander 2026-05-30 23:18 274
        return {};
bf525669 Poul Sander 2026-05-30 23:18 275
bf525669 Poul Sander 2026-05-30 23:18 276
    int targetRow = currentRow + delta;
bf525669 Poul Sander 2026-05-30 23:18 277
    if (targetRow < 0 || targetRow >= count)
bf525669 Poul Sander 2026-05-30 23:18 278
        return {};
bf525669 Poul Sander 2026-05-30 23:18 279
bf525669 Poul Sander 2026-05-30 23:18 280
    QModelIndex targetIdx = m_dirModel->index(targetRow, 0, parentIdx);
bf525669 Poul Sander 2026-05-30 23:18 281
    return m_dirModel->filePath(targetIdx);
bf525669 Poul Sander 2026-05-30 23:18 282
}
bf525669 Poul Sander 2026-05-30 23:18 283
bf525669 Poul Sander 2026-05-30 23:18 284
void MainWindow::navigateToPrevFolder()
bf525669 Poul Sander 2026-05-30 23:18 285
{
bf525669 Poul Sander 2026-05-30 23:18 286
    QByteArray path = siblingFolder(-1);
bf525669 Poul Sander 2026-05-30 23:18 287
    if (!path.isEmpty())
bf525669 Poul Sander 2026-05-30 23:18 288
        navigateToFolder(path);
bf525669 Poul Sander 2026-05-30 23:18 289
}
bf525669 Poul Sander 2026-05-30 23:18 290
bf525669 Poul Sander 2026-05-30 23:18 291
void MainWindow::navigateToNextFolder()
bf525669 Poul Sander 2026-05-30 23:18 292
{
bf525669 Poul Sander 2026-05-30 23:18 293
    QByteArray path = siblingFolder(+1);
bf525669 Poul Sander 2026-05-30 23:18 294
    if (!path.isEmpty())
bf525669 Poul Sander 2026-05-30 23:18 295
        navigateToFolder(path);
bf525669 Poul Sander 2026-05-30 23:18 296
}
bf525669 Poul Sander 2026-05-30 23:18 297
80a9926d Poul Sander 2026-05-19 20:28 298
void MainWindow::navigateToFolder(const QByteArray &path)
80a9926d Poul Sander 2026-05-19 20:28 299
{
665ac308 Poul Sander 2026-02-23 19:34 300
    m_previousFolderPath = m_imageModel->currentDirectory();
665ac308 Poul Sander 2026-02-23 19:34 301
    m_imageModel->setDirectory(path);
40efdfde Poul Sander 2026-05-19 21:24 302
    QModelIndex dirIdx = m_dirModel->indexForPath(path);
665ac308 Poul Sander 2026-02-23 19:34 303
    if (dirIdx.isValid())
665ac308 Poul Sander 2026-02-23 19:34 304
        m_treeView->setCurrentIndex(dirIdx);
fd7f51cd Poul Sander 2026-05-22 15:30 305
    updatePathField(path);
665ac308 Poul Sander 2026-02-23 19:34 306
    QTimer::singleShot(0, this, [this]() {
665ac308 Poul Sander 2026-02-23 19:34 307
        selectPreviousFolderIfExists();
665ac308 Poul Sander 2026-02-23 19:34 308
        loadVisibleThumbnails();
665ac308 Poul Sander 2026-02-23 19:34 309
    });
665ac308 Poul Sander 2026-02-23 19:34 310
}
665ac308 Poul Sander 2026-02-23 19:34 311
fd7f51cd Poul Sander 2026-05-22 15:30 312
void MainWindow::updatePathField(const QByteArray &path)
fd7f51cd Poul Sander 2026-05-22 15:30 313
{
fd7f51cd Poul Sander 2026-05-22 15:30 314
    if (!m_pathEdit)
fd7f51cd Poul Sander 2026-05-22 15:30 315
        return;
fd7f51cd Poul Sander 2026-05-22 15:30 316
    const QString text = QFile::decodeName(path);
fd7f51cd Poul Sander 2026-05-22 15:30 317
    if (m_pathEdit->text() != text)
fd7f51cd Poul Sander 2026-05-22 15:30 318
        m_pathEdit->setText(text);
fd7f51cd Poul Sander 2026-05-22 15:30 319
}
fd7f51cd Poul Sander 2026-05-22 15:30 320
fd7f51cd Poul Sander 2026-05-22 15:30 321
void MainWindow::onPathEntered()
fd7f51cd Poul Sander 2026-05-22 15:30 322
{
fd7f51cd Poul Sander 2026-05-22 15:30 323
    const QString text = m_pathEdit->text().trimmed();
fd7f51cd Poul Sander 2026-05-22 15:30 324
    if (text.isEmpty())
fd7f51cd Poul Sander 2026-05-22 15:30 325
        return;
fd7f51cd Poul Sander 2026-05-22 15:30 326
    openPath(text);
fd7f51cd Poul Sander 2026-05-22 15:30 327
}
fd7f51cd Poul Sander 2026-05-22 15:30 328
48607848 Poul Sander 2026-05-19 19:20 329
void MainWindow::openPath(const QString &path)
48607848 Poul Sander 2026-05-19 19:20 330
{
48607848 Poul Sander 2026-05-19 19:20 331
    QFileInfo info(path);
48607848 Poul Sander 2026-05-19 19:20 332
    if (!info.exists())
48607848 Poul Sander 2026-05-19 19:20 333
        return;
48607848 Poul Sander 2026-05-19 19:20 334
48607848 Poul Sander 2026-05-19 19:20 335
    if (info.isDir()) {
80a9926d Poul Sander 2026-05-19 20:28 336
        navigateToFolder(QFile::encodeName(info.absoluteFilePath()));
48607848 Poul Sander 2026-05-19 19:20 337
    } else if (info.isFile()) {
80a9926d Poul Sander 2026-05-19 20:28 338
        navigateToFolder(QFile::encodeName(info.absolutePath()));
48607848 Poul Sander 2026-05-19 19:20 339
48607848 Poul Sander 2026-05-19 19:20 340
        // ImageModel::setDirectory is synchronous, so items are ready now.
48607848 Poul Sander 2026-05-19 19:20 341
        // Find the file in the model and open it.
80a9926d Poul Sander 2026-05-19 20:28 342
        QByteArray target = QFile::encodeName(info.absoluteFilePath());
48607848 Poul Sander 2026-05-19 19:20 343
        for (int i = 0; i < m_imageModel->rowCount(); ++i) {
48607848 Poul Sander 2026-05-19 19:20 344
            QModelIndex idx = m_imageModel->index(i);
80a9926d Poul Sander 2026-05-19 20:28 345
            if (m_imageModel->filePath(idx) == target) {
48607848 Poul Sander 2026-05-19 19:20 346
                m_listView->setCurrentIndex(idx);
48607848 Poul Sander 2026-05-19 19:20 347
                m_listView->scrollTo(idx);
48607848 Poul Sander 2026-05-19 19:20 348
                showPreview(idx);
48607848 Poul Sander 2026-05-19 19:20 349
                if (!m_imageModel->isFolder(idx))
48607848 Poul Sander 2026-05-19 19:20 350
                    enterSingleImageMode(idx);
48607848 Poul Sander 2026-05-19 19:20 351
                break;
48607848 Poul Sander 2026-05-19 19:20 352
            }
48607848 Poul Sander 2026-05-19 19:20 353
        }
48607848 Poul Sander 2026-05-19 19:20 354
    }
48607848 Poul Sander 2026-05-19 19:20 355
}
48607848 Poul Sander 2026-05-19 19:20 356
665ac308 Poul Sander 2026-02-23 19:34 357
void MainWindow::selectPreviousFolderIfExists()
665ac308 Poul Sander 2026-02-23 19:34 358
{
665ac308 Poul Sander 2026-02-23 19:34 359
    if (m_previousFolderPath.isEmpty())
665ac308 Poul Sander 2026-02-23 19:34 360
        return;
665ac308 Poul Sander 2026-02-23 19:34 361
665ac308 Poul Sander 2026-02-23 19:34 362
    // Find the previous folder in the current directory's contents
665ac308 Poul Sander 2026-02-23 19:34 363
    for (int i = 0; i < m_imageModel->rowCount(); ++i) {
665ac308 Poul Sander 2026-02-23 19:34 364
        QModelIndex idx = m_imageModel->index(i);
665ac308 Poul Sander 2026-02-23 19:34 365
        if (m_imageModel->isFolder(idx)) {
80a9926d Poul Sander 2026-05-19 20:28 366
            QByteArray folderPath = m_imageModel->filePath(idx);
665ac308 Poul Sander 2026-02-23 19:34 367
            if (folderPath == m_previousFolderPath) {
665ac308 Poul Sander 2026-02-23 19:34 368
                m_listView->setCurrentIndex(idx);
665ac308 Poul Sander 2026-02-23 19:34 369
                m_listView->scrollTo(idx);
665ac308 Poul Sander 2026-02-23 19:34 370
                return;
665ac308 Poul Sander 2026-02-23 19:34 371
            }
665ac308 Poul Sander 2026-02-23 19:34 372
        }
665ac308 Poul Sander 2026-02-23 19:34 373
    }
665ac308 Poul Sander 2026-02-23 19:34 374
}
665ac308 Poul Sander 2026-02-23 19:34 375
032aa1e1 Poul Sander 2026-02-19 18:55 376
void MainWindow::setupConnections()
032aa1e1 Poul Sander 2026-02-19 18:55 377
{
fd7f51cd Poul Sander 2026-05-22 15:30 378
    connect(m_pathEdit, &QLineEdit::returnPressed,
fd7f51cd Poul Sander 2026-05-22 15:30 379
            this, &MainWindow::onPathEntered);
fd7f51cd Poul Sander 2026-05-22 15:30 380
    connect(m_goButton, &QPushButton::clicked,
fd7f51cd Poul Sander 2026-05-22 15:30 381
            this, &MainWindow::onPathEntered);
bf525669 Poul Sander 2026-05-30 23:18 382
    connect(m_prevFolderButton, &QPushButton::clicked,
bf525669 Poul Sander 2026-05-30 23:18 383
            this, &MainWindow::navigateToPrevFolder);
bf525669 Poul Sander 2026-05-30 23:18 384
    connect(m_nextFolderButton, &QPushButton::clicked,
bf525669 Poul Sander 2026-05-30 23:18 385
            this, &MainWindow::navigateToNextFolder);
fd7f51cd Poul Sander 2026-05-22 15:30 386
    connect(m_pathEdit, &QLineEdit::textEdited,
fd7f51cd Poul Sander 2026-05-22 15:30 387
            m_pathCompleterModel, &PathCompleterModel::setPrefix);
fd7f51cd Poul Sander 2026-05-22 15:30 388
3bee3e3e Poul Sander 2026-03-05 17:07 389
    connect(m_rootList, &QListWidget::currentItemChanged, this,
3bee3e3e Poul Sander 2026-03-05 17:07 390
            [this](QListWidgetItem *current, QListWidgetItem *) {
3bee3e3e Poul Sander 2026-03-05 17:07 391
                if (current == nullptr)
3bee3e3e Poul Sander 2026-03-05 17:07 392
                    return;
3bee3e3e Poul Sander 2026-03-05 17:07 393
3bee3e3e Poul Sander 2026-03-05 17:07 394
                QString rootPath = current->data(Qt::UserRole).toString();
fc5f2e27 Poul Sander 2026-02-22 22:06 395
                m_dirModel->setRootPath(rootPath);
40efdfde Poul Sander 2026-05-19 21:24 396
                m_treeView->setRootIndex(m_dirModel->rootIndex());
fc5f2e27 Poul Sander 2026-02-22 22:06 397
            });
fc5f2e27 Poul Sander 2026-02-22 22:06 398
032aa1e1 Poul Sander 2026-02-19 18:55 399
    connect(m_treeView, &QTreeView::clicked, this,
032aa1e1 Poul Sander 2026-02-19 18:55 400
            [this](const QModelIndex &index)
6fcd6c90 Poul Sander 2026-02-19 21:12 401
            {
40efdfde Poul Sander 2026-05-19 21:24 402
                QByteArray path = m_dirModel->filePath(index);
665ac308 Poul Sander 2026-02-23 19:34 403
                navigateToFolder(path);
6fcd6c90 Poul Sander 2026-02-19 21:12 404
            });
032aa1e1 Poul Sander 2026-02-19 18:55 405
032aa1e1 Poul Sander 2026-02-19 18:55 406
    connect(m_listView, &QListView::clicked, this,
cfe4a67b Poul Sander 2026-02-19 23:06 407
            [this](const QModelIndex &index) { showPreview(index); });
6fcd6c90 Poul Sander 2026-02-19 21:12 408
cfe4a67b Poul Sander 2026-02-19 23:06 409
    connect(m_listView->selectionModel(), &QItemSelectionModel::currentChanged,
cfe4a67b Poul Sander 2026-02-19 23:06 410
            this, [this](const QModelIndex &current) { showPreview(current); });
6fcd6c90 Poul Sander 2026-02-19 21:12 411
61136b77 Poul Sander 2026-02-20 22:45 412
    connect(m_listView, &QListView::doubleClicked, this,
09130ab9 Poul Sander 2026-02-22 22:19 413
            [this](const QModelIndex &index) {
09130ab9 Poul Sander 2026-02-22 22:19 414
                if (m_imageModel->isFolder(index)) {
80a9926d Poul Sander 2026-05-19 20:28 415
                    QByteArray path = m_imageModel->filePath(index);
665ac308 Poul Sander 2026-02-23 19:34 416
                    navigateToFolder(path);
09130ab9 Poul Sander 2026-02-22 22:19 417
                } else {
09130ab9 Poul Sander 2026-02-22 22:19 418
                    enterSingleImageMode(index);
09130ab9 Poul Sander 2026-02-22 22:19 419
                }
09130ab9 Poul Sander 2026-02-22 22:19 420
            });
61136b77 Poul Sander 2026-02-20 22:45 421
237d2c81 Poul Sander 2026-06-06 14:53 422
    connect(m_listView, &QListView::customContextMenuRequested,
237d2c81 Poul Sander 2026-06-06 14:53 423
            this, &MainWindow::showImageContextMenu);
237d2c81 Poul Sander 2026-06-06 14:53 424
d93c1ab0 Poul Sander 2026-06-08 21:44 425
    connect(m_imageView, &ImageViewWidget::customContextMenuRequested,
d93c1ab0 Poul Sander 2026-06-08 21:44 426
            this, &MainWindow::showImageViewContextMenu);
d93c1ab0 Poul Sander 2026-06-08 21:44 427
61136b77 Poul Sander 2026-02-20 22:45 428
    connect(m_imageView, &ImageViewWidget::closeRequested,
61136b77 Poul Sander 2026-02-20 22:45 429
            this, &MainWindow::leaveSingleImageMode);
beb4a823 Poul Sander 2026-05-19 21:53 430
    connect(m_imageView, &ImageViewWidget::editCaptionRequested,
beb4a823 Poul Sander 2026-05-19 21:53 431
        this, &MainWindow::onEditCaption);
1ffc1962 Poul Sander 2026-02-20 23:41 432
    connect(m_imageView, &ImageViewWidget::nextRequested, this, [this]() {
1ffc1962 Poul Sander 2026-02-20 23:41 433
        QModelIndex cur = m_listView->currentIndex();
1ffc1962 Poul Sander 2026-02-20 23:41 434
        int next = cur.isValid() ? cur.row() + 1 : 0;
09130ab9 Poul Sander 2026-02-22 22:19 435
        while (next < m_imageModel->rowCount() && m_imageModel->isFolder(m_imageModel->index(next)))
09130ab9 Poul Sander 2026-02-22 22:19 436
            ++next;
1ffc1962 Poul Sander 2026-02-20 23:41 437
        if (next < m_imageModel->rowCount())
1ffc1962 Poul Sander 2026-02-20 23:41 438
        {
1ffc1962 Poul Sander 2026-02-20 23:41 439
            QModelIndex idx = m_imageModel->index(next);
1ffc1962 Poul Sander 2026-02-20 23:41 440
            m_listView->setCurrentIndex(idx);
1ffc1962 Poul Sander 2026-02-20 23:41 441
            enterSingleImageMode(idx);
1ffc1962 Poul Sander 2026-02-20 23:41 442
        }
1ffc1962 Poul Sander 2026-02-20 23:41 443
    });
1ffc1962 Poul Sander 2026-02-20 23:41 444
1ffc1962 Poul Sander 2026-02-20 23:41 445
    connect(m_imageView, &ImageViewWidget::previousRequested, this, [this]() {
1ffc1962 Poul Sander 2026-02-20 23:41 446
        QModelIndex cur = m_listView->currentIndex();
1ffc1962 Poul Sander 2026-02-20 23:41 447
        int prev = cur.isValid() ? cur.row() - 1 : 0;
09130ab9 Poul Sander 2026-02-22 22:19 448
        while (prev >= 0 && m_imageModel->isFolder(m_imageModel->index(prev)))
09130ab9 Poul Sander 2026-02-22 22:19 449
            --prev;
1ffc1962 Poul Sander 2026-02-20 23:41 450
        if (prev >= 0)
1ffc1962 Poul Sander 2026-02-20 23:41 451
        {
1ffc1962 Poul Sander 2026-02-20 23:41 452
            QModelIndex idx = m_imageModel->index(prev);
1ffc1962 Poul Sander 2026-02-20 23:41 453
            m_listView->setCurrentIndex(idx);
1ffc1962 Poul Sander 2026-02-20 23:41 454
            enterSingleImageMode(idx);
1ffc1962 Poul Sander 2026-02-20 23:41 455
        }
1ffc1962 Poul Sander 2026-02-20 23:41 456
    });
1ffc1962 Poul Sander 2026-02-20 23:41 457
6fcd6c90 Poul Sander 2026-02-19 21:12 458
    connect(m_listView->verticalScrollBar(), &QScrollBar::valueChanged,
6fcd6c90 Poul Sander 2026-02-19 21:12 459
            this, [this]()
6fcd6c90 Poul Sander 2026-02-19 21:12 460
            { loadVisibleThumbnails(); });
6fcd6c90 Poul Sander 2026-02-19 21:12 461
6fcd6c90 Poul Sander 2026-02-19 21:12 462
    // Also catch resize — visible range changes when the viewport is resized
6fcd6c90 Poul Sander 2026-02-19 21:12 463
    m_listView->viewport()->installEventFilter(this);
ff6ac640 Poul Sander 2026-06-02 16:53 464
ff6ac640 Poul Sander 2026-06-02 16:53 465
    // The folder list and tree views consume Ctrl+Left/Ctrl+Right for their own
ff6ac640 Poul Sander 2026-06-02 16:53 466
    // item navigation, so those keys never reach MainWindow::keyPressEvent.
ff6ac640 Poul Sander 2026-06-02 16:53 467
    // Filter them here so PrevFolder/NextFolder shortcuts work while a view has focus.
ff6ac640 Poul Sander 2026-06-02 16:53 468
    m_listView->installEventFilter(this);
ff6ac640 Poul Sander 2026-06-02 16:53 469
    m_treeView->installEventFilter(this);
cdae9502 Poul Sander 2026-06-09 20:04 470
cdae9502 Poul Sander 2026-06-09 20:04 471
    connect(m_exifTable, &QTableWidget::cellClicked, this, [this](int row, int column) {
cdae9502 Poul Sander 2026-06-09 20:04 472
        if (column == 1) {
cdae9502 Poul Sander 2026-06-09 20:04 473
            QTableWidgetItem *fieldItem = m_exifTable->item(row, 0);
cdae9502 Poul Sander 2026-06-09 20:04 474
            if (fieldItem && fieldItem->text() == "OpenStreetMap") {
cdae9502 Poul Sander 2026-06-09 20:04 475
                QTableWidgetItem *valueItem = m_exifTable->item(row, 1);
cdae9502 Poul Sander 2026-06-09 20:04 476
                if (valueItem && !valueItem->text().isEmpty()) {
cdae9502 Poul Sander 2026-06-09 20:04 477
                    QDesktopServices::openUrl(QUrl(valueItem->text()));
cdae9502 Poul Sander 2026-06-09 20:04 478
                }
cdae9502 Poul Sander 2026-06-09 20:04 479
            }
cdae9502 Poul Sander 2026-06-09 20:04 480
        }
cdae9502 Poul Sander 2026-06-09 20:04 481
    });
6fcd6c90 Poul Sander 2026-02-19 21:12 482
}
6fcd6c90 Poul Sander 2026-02-19 21:12 483
6fcd6c90 Poul Sander 2026-02-19 21:12 484
bool MainWindow::eventFilter(QObject *obj, QEvent *event)
6fcd6c90 Poul Sander 2026-02-19 21:12 485
{
6fcd6c90 Poul Sander 2026-02-19 21:12 486
    if (obj == m_listView->viewport() && event->type() == QEvent::Resize)
6fcd6c90 Poul Sander 2026-02-19 21:12 487
        QTimer::singleShot(0, this, &MainWindow::loadVisibleThumbnails);
6fcd6c90 Poul Sander 2026-02-19 21:12 488
ff6ac640 Poul Sander 2026-06-02 16:53 489
    if ((obj == m_listView || obj == m_treeView)
ff6ac640 Poul Sander 2026-06-02 16:53 490
        && event->type() == QEvent::KeyPress
ff6ac640 Poul Sander 2026-06-02 16:53 491
        && m_stack->currentWidget() == m_browserPage)
ff6ac640 Poul Sander 2026-06-02 16:53 492
    {
ff6ac640 Poul Sander 2026-06-02 16:53 493
        const ShortcutManager &sc = ShortcutManager::instance();
ff6ac640 Poul Sander 2026-06-02 16:53 494
        QKeyEvent *keyEvent = static_cast<QKeyEvent *>(event);
ff6ac640 Poul Sander 2026-06-02 16:53 495
ff6ac640 Poul Sander 2026-06-02 16:53 496
        if (sc.matches(keyEvent, ShortcutManager::PrevFolder)) {
ff6ac640 Poul Sander 2026-06-02 16:53 497
            navigateToPrevFolder();
ff6ac640 Poul Sander 2026-06-02 16:53 498
            return true;
ff6ac640 Poul Sander 2026-06-02 16:53 499
        }
ff6ac640 Poul Sander 2026-06-02 16:53 500
        if (sc.matches(keyEvent, ShortcutManager::NextFolder)) {
ff6ac640 Poul Sander 2026-06-02 16:53 501
            navigateToNextFolder();
ff6ac640 Poul Sander 2026-06-02 16:53 502
            return true;
ff6ac640 Poul Sander 2026-06-02 16:53 503
        }
a686d839 Poul Sander 2026-06-06 22:34 504
        if (obj == m_listView
a686d839 Poul Sander 2026-06-06 22:34 505
            && sc.matches(keyEvent, ShortcutManager::MoveToTrash)) {
a686d839 Poul Sander 2026-06-06 22:34 506
            moveCurrentImageToTrash();
a686d839 Poul Sander 2026-06-06 22:34 507
            return true;
a686d839 Poul Sander 2026-06-06 22:34 508
        }
ff6ac640 Poul Sander 2026-06-02 16:53 509
    }
ff6ac640 Poul Sander 2026-06-02 16:53 510
6fcd6c90 Poul Sander 2026-02-19 21:12 511
    return QMainWindow::eventFilter(obj, event);
6fcd6c90 Poul Sander 2026-02-19 21:12 512
}
6fcd6c90 Poul Sander 2026-02-19 21:12 513
6fcd6c90 Poul Sander 2026-02-19 21:12 514
void MainWindow::loadVisibleThumbnails()
6fcd6c90 Poul Sander 2026-02-19 21:12 515
{
6fcd6c90 Poul Sander 2026-02-19 21:12 516
    QModelIndex topLeft = m_listView->indexAt(QPoint(0, 0));
6fcd6c90 Poul Sander 2026-02-19 21:12 517
    QModelIndex bottomRight = m_listView->indexAt(
6fcd6c90 Poul Sander 2026-02-19 21:12 518
        QPoint(m_listView->viewport()->width() - 1,
6fcd6c90 Poul Sander 2026-02-19 21:12 519
               m_listView->viewport()->height() - 1));
6fcd6c90 Poul Sander 2026-02-19 21:12 520
6fcd6c90 Poul Sander 2026-02-19 21:12 521
    int first = topLeft.isValid() ? topLeft.row() : 0;
6fcd6c90 Poul Sander 2026-02-19 21:12 522
    int last = bottomRight.isValid() ? bottomRight.row()
6fcd6c90 Poul Sander 2026-02-19 21:12 523
                                     : m_imageModel->rowCount() - 1;
6fcd6c90 Poul Sander 2026-02-19 21:12 524
6fcd6c90 Poul Sander 2026-02-19 21:12 525
    if (last < 0)
6fcd6c90 Poul Sander 2026-02-19 21:12 526
        return;
6fcd6c90 Poul Sander 2026-02-19 21:12 527
6fcd6c90 Poul Sander 2026-02-19 21:12 528
    std::cout << "loadVisibleThumbnails: " << first << " - " << last << "\n";
6fcd6c90 Poul Sander 2026-02-19 21:12 529
    m_imageModel->requestThumbnails(first, last);
032aa1e1 Poul Sander 2026-02-19 18:55 530
}
cfe4a67b Poul Sander 2026-02-19 23:06 531
cfe4a67b Poul Sander 2026-02-19 23:06 532
void MainWindow::showPreview(const QModelIndex &index)
cfe4a67b Poul Sander 2026-02-19 23:06 533
{
cfe4a67b Poul Sander 2026-02-19 23:06 534
    if (!index.isValid())
cfe4a67b Poul Sander 2026-02-19 23:06 535
        return;
cfe4a67b Poul Sander 2026-02-19 23:06 536
80a9926d Poul Sander 2026-05-19 20:28 537
    QByteArray path = m_imageModel->filePath(index);
80a9926d Poul Sander 2026-05-19 20:28 538
80a9926d Poul Sander 2026-05-19 20:28 539
    int fd = ::open(path.constData(), O_RDONLY | O_CLOEXEC);
80a9926d Poul Sander 2026-05-19 20:28 540
    if (fd >= 0) {
80a9926d Poul Sander 2026-05-19 20:28 541
        QFile file;
80a9926d Poul Sander 2026-05-19 20:28 542
        if (file.open(fd, QIODevice::ReadOnly, QFileDevice::AutoCloseHandle)) {
80a9926d Poul Sander 2026-05-19 20:28 543
            QImageReader reader(&file);
80a9926d Poul Sander 2026-05-19 20:28 544
            reader.setAutoTransform(true);
80a9926d Poul Sander 2026-05-19 20:28 545
            QSize fullSize = reader.size();
80a9926d Poul Sander 2026-05-19 20:28 546
            if (fullSize.isValid()) {
80a9926d Poul Sander 2026-05-19 20:28 547
                QSize target = fullSize.scaled(m_previewLabel->size(), Qt::KeepAspectRatio);
80a9926d Poul Sander 2026-05-19 20:28 548
                reader.setScaledSize(target);
80a9926d Poul Sander 2026-05-19 20:28 549
            }
80a9926d Poul Sander 2026-05-19 20:28 550
            QImage image = reader.read();
80a9926d Poul Sander 2026-05-19 20:28 551
            m_previewLabel->setPixmap(
80a9926d Poul Sander 2026-05-19 20:28 552
                QPixmap::fromImage(image).scaled(
80a9926d Poul Sander 2026-05-19 20:28 553
                    m_previewLabel->size(),
80a9926d Poul Sander 2026-05-19 20:28 554
                    Qt::KeepAspectRatio,
80a9926d Poul Sander 2026-05-19 20:28 555
                    Qt::SmoothTransformation));
80a9926d Poul Sander 2026-05-19 20:28 556
        }
7889168c Poul Sander 2026-02-26 18:27 557
    }
53fdab93 Poul Sander 2026-05-18 18:45 558
53fdab93 Poul Sander 2026-05-18 18:45 559
    updateExifInfo(path);
53fdab93 Poul Sander 2026-05-18 18:45 560
}
53fdab93 Poul Sander 2026-05-18 18:45 561
80a9926d Poul Sander 2026-05-19 20:28 562
void MainWindow::updateExifInfo(const QByteArray &path)
53fdab93 Poul Sander 2026-05-18 18:45 563
{
80a9926d Poul Sander 2026-05-19 20:28 564
    // Check if this is a folder by attempting stat
80a9926d Poul Sander 2026-05-19 20:28 565
    struct ::stat st{};
80a9926d Poul Sander 2026-05-19 20:28 566
    if (::stat(path.constData(), &st) != 0 || S_ISDIR(st.st_mode)) {
53fdab93 Poul Sander 2026-05-18 18:45 567
        m_exifTable->setRowCount(0);
53fdab93 Poul Sander 2026-05-18 18:45 568
        return;
53fdab93 Poul Sander 2026-05-18 18:45 569
    }
53fdab93 Poul Sander 2026-05-18 18:45 570
    const ExifData data = ExifReader::read(path);
53fdab93 Poul Sander 2026-05-18 18:45 571
    const auto fields = data.toList();
53fdab93 Poul Sander 2026-05-18 18:45 572
    m_exifTable->setRowCount(fields.size());
53fdab93 Poul Sander 2026-05-18 18:45 573
    for (int i = 0; i < fields.size(); ++i) {
53fdab93 Poul Sander 2026-05-18 18:45 574
        m_exifTable->setItem(i, 0, new QTableWidgetItem(fields[i].first));
cdae9502 Poul Sander 2026-06-09 20:04 575
        auto *item = new QTableWidgetItem(fields[i].second);
cdae9502 Poul Sander 2026-06-09 20:04 576
        if (fields[i].first == "OpenStreetMap") {
cdae9502 Poul Sander 2026-06-09 20:04 577
            item->setForeground(Qt::blue);
cdae9502 Poul Sander 2026-06-09 20:04 578
            QFont font = item->font();
cdae9502 Poul Sander 2026-06-09 20:04 579
            font.setUnderline(true);
cdae9502 Poul Sander 2026-06-09 20:04 580
            item->setFont(font);
cdae9502 Poul Sander 2026-06-09 20:04 581
            item->setToolTip(tr("Click to open in browser"));
cdae9502 Poul Sander 2026-06-09 20:04 582
        }
cdae9502 Poul Sander 2026-06-09 20:04 583
        m_exifTable->setItem(i, 1, item);
53fdab93 Poul Sander 2026-05-18 18:45 584
    }
53fdab93 Poul Sander 2026-05-18 18:45 585
    m_exifTable->resizeColumnToContents(0);
cfe4a67b Poul Sander 2026-02-19 23:06 586
}
61136b77 Poul Sander 2026-02-20 22:45 587
61136b77 Poul Sander 2026-02-20 22:45 588
void MainWindow::enterSingleImageMode(const QModelIndex &index)
61136b77 Poul Sander 2026-02-20 22:45 589
{
61136b77 Poul Sander 2026-02-20 22:45 590
    if (!index.isValid())
61136b77 Poul Sander 2026-02-20 22:45 591
        return;
61136b77 Poul Sander 2026-02-20 22:45 592
cbc7d22b Poul Sander 2026-02-24 19:23 593
    // Only save state and hide chrome when transitioning from browse mode
cbc7d22b Poul Sander 2026-02-24 19:23 594
    if (m_stack->currentWidget() != m_imageView) {
3bee3e3e Poul Sander 2026-03-05 17:07 595
        m_rootDockWasVisible = m_rootDock->isVisible();
cbc7d22b Poul Sander 2026-02-24 19:23 596
        m_folderDockWasVisible = m_folderDock->isVisible();
cbc7d22b Poul Sander 2026-02-24 19:23 597
        m_previewDockWasVisible = m_previewDock->isVisible();
53fdab93 Poul Sander 2026-05-18 18:45 598
        m_exifDockWasVisible = m_exifDock->isVisible();
3bee3e3e Poul Sander 2026-03-05 17:07 599
        m_rootDock->hide();
cbc7d22b Poul Sander 2026-02-24 19:23 600
        m_folderDock->hide();
cbc7d22b Poul Sander 2026-02-24 19:23 601
        m_previewDock->hide();
53fdab93 Poul Sander 2026-05-18 18:45 602
        m_exifDock->hide();
cbc7d22b Poul Sander 2026-02-24 19:23 603
        m_menuBar->hide();
cbc7d22b Poul Sander 2026-02-24 19:23 604
    }
cbc7d22b Poul Sander 2026-02-24 19:23 605
f61bf886 Poul Sander 2026-02-23 19:27 606
    m_imageView->setBackgroundColor(m_backgroundColorPreference);
80a9926d Poul Sander 2026-05-19 20:28 607
    QByteArray path = m_imageModel->filePath(index);
58103277 Poul Sander 2026-03-01 22:53 608
    m_imageView->setNeighborPaths(computeNeighborPaths(index));
61136b77 Poul Sander 2026-02-20 22:45 609
    m_imageView->setImage(path);
53fdab93 Poul Sander 2026-05-18 18:45 610
    m_imageView->setExifData(ExifReader::read(path));
53fdab93 Poul Sander 2026-05-18 18:45 611
    updateExifInfo(path);
61136b77 Poul Sander 2026-02-20 22:45 612
    m_stack->setCurrentWidget(m_imageView);
61136b77 Poul Sander 2026-02-20 22:45 613
    m_imageView->setFocus();
ccb726b8 Poul Sander 2026-02-23 22:43 614
ccb726b8 Poul Sander 2026-02-23 22:43 615
    if (m_imageViewWasFullScreen) {
ccb726b8 Poul Sander 2026-02-23 22:43 616
        setFullScreenMode(true);
ccb726b8 Poul Sander 2026-02-23 22:43 617
        m_imageViewWasFullScreen = false;
ccb726b8 Poul Sander 2026-02-23 22:43 618
    }
61136b77 Poul Sander 2026-02-20 22:45 619
}
61136b77 Poul Sander 2026-02-20 22:45 620
beb4a823 Poul Sander 2026-05-19 21:53 621
void MainWindow::onEditCaption()
beb4a823 Poul Sander 2026-05-19 21:53 622
{
237d2c81 Poul Sander 2026-06-06 14:53 623
    QByteArray path;
237d2c81 Poul Sander 2026-06-06 14:53 624
    if (m_stack->currentWidget() == m_imageView) {
237d2c81 Poul Sander 2026-06-06 14:53 625
        path = m_imageView->currentPath();
237d2c81 Poul Sander 2026-06-06 14:53 626
    } else {
237d2c81 Poul Sander 2026-06-06 14:53 627
        const QModelIndex index = m_listView->currentIndex();
237d2c81 Poul Sander 2026-06-06 14:53 628
        if (index.isValid() && !m_imageModel->isFolder(index))
237d2c81 Poul Sander 2026-06-06 14:53 629
            path = m_imageModel->filePath(index);
237d2c81 Poul Sander 2026-06-06 14:53 630
    }
237d2c81 Poul Sander 2026-06-06 14:53 631
beb4a823 Poul Sander 2026-05-19 21:53 632
    if (path.isEmpty())
beb4a823 Poul Sander 2026-05-19 21:53 633
        return;
beb4a823 Poul Sander 2026-05-19 21:53 634
835a6bea Poul Sander 2026-05-19 22:03 635
    if (::access(path.constData(), W_OK) != 0) {
835a6bea Poul Sander 2026-05-19 22:03 636
        QMessageBox::warning(this, tr("Cannot Edit Caption"),
835a6bea Poul Sander 2026-05-19 22:03 637
            tr("The file is write-protected and cannot be edited.\n\n%1")
237d2c81 Poul Sander 2026-06-06 14:53 638
                .arg(displayPath(path)));
835a6bea Poul Sander 2026-05-19 22:03 639
        return;
835a6bea Poul Sander 2026-05-19 22:03 640
    }
835a6bea Poul Sander 2026-05-19 22:03 641
beb4a823 Poul Sander 2026-05-19 21:53 642
    ExifData data = ExifReader::read(path);
beb4a823 Poul Sander 2026-05-19 21:53 643
beb4a823 Poul Sander 2026-05-19 21:53 644
    QDialog dlg(this);
beb4a823 Poul Sander 2026-05-19 21:53 645
    dlg.setWindowTitle(tr("Edit Caption"));
beb4a823 Poul Sander 2026-05-19 21:53 646
    auto *layout = new QVBoxLayout(&dlg);
beb4a823 Poul Sander 2026-05-19 21:53 647
    layout->addWidget(new QLabel(tr("Caption-Abstract:"), &dlg));
beb4a823 Poul Sander 2026-05-19 21:53 648
    auto *edit = new QPlainTextEdit(&dlg);
beb4a823 Poul Sander 2026-05-19 21:53 649
    edit->setPlainText(data.captionAbstract);
beb4a823 Poul Sander 2026-05-19 21:53 650
    edit->setMinimumWidth(400);
beb4a823 Poul Sander 2026-05-19 21:53 651
    layout->addWidget(edit);
beb4a823 Poul Sander 2026-05-19 21:53 652
    auto *buttons = new QDialogButtonBox(
beb4a823 Poul Sander 2026-05-19 21:53 653
        QDialogButtonBox::Ok | QDialogButtonBox::Cancel, &dlg);
beb4a823 Poul Sander 2026-05-19 21:53 654
    layout->addWidget(buttons);
beb4a823 Poul Sander 2026-05-19 21:53 655
    connect(buttons, &QDialogButtonBox::accepted, &dlg, &QDialog::accept);
beb4a823 Poul Sander 2026-05-19 21:53 656
    connect(buttons, &QDialogButtonBox::rejected, &dlg, &QDialog::reject);
beb4a823 Poul Sander 2026-05-19 21:53 657
beb4a823 Poul Sander 2026-05-19 21:53 658
    if (dlg.exec() != QDialog::Accepted)
beb4a823 Poul Sander 2026-05-19 21:53 659
        return;
beb4a823 Poul Sander 2026-05-19 21:53 660
beb4a823 Poul Sander 2026-05-19 21:53 661
    const QString newCaption = edit->toPlainText();
beb4a823 Poul Sander 2026-05-19 21:53 662
    if (ExifReader::saveCaption(path, newCaption, data)) {
beb4a823 Poul Sander 2026-05-19 21:53 663
        ExifData updated = ExifReader::read(path);
237d2c81 Poul Sander 2026-06-06 14:53 664
        if (m_imageView->currentPath() == path)
237d2c81 Poul Sander 2026-06-06 14:53 665
            m_imageView->setExifData(updated);
beb4a823 Poul Sander 2026-05-19 21:53 666
        updateExifInfo(path);
beb4a823 Poul Sander 2026-05-19 21:53 667
    }
beb4a823 Poul Sander 2026-05-19 21:53 668
}
beb4a823 Poul Sander 2026-05-19 21:53 669
237d2c81 Poul Sander 2026-06-06 14:53 670
void MainWindow::showImageContextMenu(const QPoint &pos)
237d2c81 Poul Sander 2026-06-06 14:53 671
{
237d2c81 Poul Sander 2026-06-06 14:53 672
    const QModelIndex index = m_listView->indexAt(pos);
237d2c81 Poul Sander 2026-06-06 14:53 673
    if (!index.isValid() || m_imageModel->isFolder(index))
237d2c81 Poul Sander 2026-06-06 14:53 674
        return;
237d2c81 Poul Sander 2026-06-06 14:53 675
237d2c81 Poul Sander 2026-06-06 14:53 676
    m_listView->setCurrentIndex(index);
237d2c81 Poul Sander 2026-06-06 14:53 677
    showPreview(index);
237d2c81 Poul Sander 2026-06-06 14:53 678
237d2c81 Poul Sander 2026-06-06 14:53 679
    const QByteArray path = m_imageModel->filePath(index);
d93c1ab0 Poul Sander 2026-06-08 21:44 680
    executeContextMenu(m_listView->viewport()->mapToGlobal(pos), path, index);
d93c1ab0 Poul Sander 2026-06-08 21:44 681
}
237d2c81 Poul Sander 2026-06-06 14:53 682
d93c1ab0 Poul Sander 2026-06-08 21:44 683
void MainWindow::showImageViewContextMenu(const QPoint &pos)
d93c1ab0 Poul Sander 2026-06-08 21:44 684
{
d93c1ab0 Poul Sander 2026-06-08 21:44 685
    const QByteArray path = m_imageView->currentPath();
d93c1ab0 Poul Sander 2026-06-08 21:44 686
    if (path.isEmpty())
d93c1ab0 Poul Sander 2026-06-08 21:44 687
        return;
d93c1ab0 Poul Sander 2026-06-08 21:44 688
d93c1ab0 Poul Sander 2026-06-08 21:44 689
    const QModelIndex index = m_listView->currentIndex();
d93c1ab0 Poul Sander 2026-06-08 21:44 690
    executeContextMenu(m_imageView->mapToGlobal(pos), path, index);
d93c1ab0 Poul Sander 2026-06-08 21:44 691
}
d93c1ab0 Poul Sander 2026-06-08 21:44 692
d93c1ab0 Poul Sander 2026-06-08 21:44 693
void MainWindow::executeContextMenu(const QPoint &globalPos, const QByteArray &path, const QModelIndex &index)
d93c1ab0 Poul Sander 2026-06-08 21:44 694
{
237d2c81 Poul Sander 2026-06-06 14:53 695
    QMenu menu(this);
237d2c81 Poul Sander 2026-06-06 14:53 696
    QAction *copyFilenameAction = menu.addAction(tr("Copy filename"));
237d2c81 Poul Sander 2026-06-06 14:53 697
    QAction *copyFullPathAction = menu.addAction(tr("Copy full path"));
237d2c81 Poul Sander 2026-06-06 14:53 698
    menu.addSeparator();
e2a71efb Poul Sander 2026-06-09 21:42 699
e2a71efb Poul Sander 2026-06-09 21:42 700
    QFileInfo info(QFile::decodeName(path));
e2a71efb Poul Sander 2026-06-09 21:42 701
    QString suffix = info.suffix().toLower();
e2a71efb Poul Sander 2026-06-09 21:42 702
    bool isJpg = (suffix == "jpg" || suffix == "jpeg");
e2a71efb Poul Sander 2026-06-09 21:42 703
e2a71efb Poul Sander 2026-06-09 21:42 704
    QAction *rotateLeftAction = nullptr;
e2a71efb Poul Sander 2026-06-09 21:42 705
    QAction *rotateRightAction = nullptr;
e2a71efb Poul Sander 2026-06-09 21:42 706
    if (isJpg) {
e2a71efb Poul Sander 2026-06-09 21:42 707
        rotateLeftAction = menu.addAction(tr("Rotate Left"));
e2a71efb Poul Sander 2026-06-09 21:42 708
        rotateRightAction = menu.addAction(tr("Rotate Right"));
e2a71efb Poul Sander 2026-06-09 21:42 709
        menu.addSeparator();
e2a71efb Poul Sander 2026-06-09 21:42 710
    }
e2a71efb Poul Sander 2026-06-09 21:42 711
237d2c81 Poul Sander 2026-06-06 14:53 712
    QAction *editDescriptionAction = menu.addAction(tr("Edit description"));
700d6f2d Poul Sander 2026-06-06 15:44 713
    QAction *moveToTrashAction = menu.addAction(tr("Move to trash"));
a686d839 Poul Sander 2026-06-06 22:34 714
    moveToTrashAction->setShortcut(
a686d839 Poul Sander 2026-06-06 22:34 715
        ShortcutManager::instance().shortcut(ShortcutManager::MoveToTrash));
237d2c81 Poul Sander 2026-06-06 14:53 716
d93c1ab0 Poul Sander 2026-06-08 21:44 717
    QAction *chosen = menu.exec(globalPos);
237d2c81 Poul Sander 2026-06-06 14:53 718
    if (chosen == copyFilenameAction) {
237d2c81 Poul Sander 2026-06-06 14:53 719
        QGuiApplication::clipboard()->setText(filenameFromPath(path));
237d2c81 Poul Sander 2026-06-06 14:53 720
    } else if (chosen == copyFullPathAction) {
237d2c81 Poul Sander 2026-06-06 14:53 721
        QGuiApplication::clipboard()->setText(displayPath(path));
e2a71efb Poul Sander 2026-06-09 21:42 722
    } else if (rotateLeftAction && chosen == rotateLeftAction) {
48534775 Poul Sander 2026-06-08 18:57 723
        rotateImage(index, false);
e2a71efb Poul Sander 2026-06-09 21:42 724
    } else if (rotateRightAction && chosen == rotateRightAction) {
48534775 Poul Sander 2026-06-08 18:57 725
        rotateImage(index, true);
237d2c81 Poul Sander 2026-06-06 14:53 726
    } else if (chosen == editDescriptionAction) {
237d2c81 Poul Sander 2026-06-06 14:53 727
        onEditCaption();
700d6f2d Poul Sander 2026-06-06 15:44 728
    } else if (chosen == moveToTrashAction) {
700d6f2d Poul Sander 2026-06-06 15:44 729
        moveImageToTrash(path);
237d2c81 Poul Sander 2026-06-06 14:53 730
    }
237d2c81 Poul Sander 2026-06-06 14:53 731
}
237d2c81 Poul Sander 2026-06-06 14:53 732
48534775 Poul Sander 2026-06-08 18:57 733
void MainWindow::rotateImage(const QModelIndex &index, bool clockwise)
48534775 Poul Sander 2026-06-08 18:57 734
{
48534775 Poul Sander 2026-06-08 18:57 735
    if (!index.isValid() || m_imageModel->isFolder(index))
48534775 Poul Sander 2026-06-08 18:57 736
        return;
48534775 Poul Sander 2026-06-08 18:57 737
48534775 Poul Sander 2026-06-08 18:57 738
    const QByteArray path = m_imageModel->filePath(index);
48534775 Poul Sander 2026-06-08 18:57 739
48534775 Poul Sander 2026-06-08 18:57 740
    if (::access(path.constData(), W_OK) != 0) {
48534775 Poul Sander 2026-06-08 18:57 741
        QMessageBox::warning(this, tr("Cannot Rotate Image"),
48534775 Poul Sander 2026-06-08 18:57 742
            tr("The file is write-protected and cannot be rotated.\n\n%1")
48534775 Poul Sander 2026-06-08 18:57 743
                .arg(displayPath(path)));
48534775 Poul Sander 2026-06-08 18:57 744
        return;
48534775 Poul Sander 2026-06-08 18:57 745
    }
48534775 Poul Sander 2026-06-08 18:57 746
48534775 Poul Sander 2026-06-08 18:57 747
    if (!ExifReader::rotate(path, clockwise)) {
48534775 Poul Sander 2026-06-08 18:57 748
        QMessageBox::warning(this, tr("Cannot Rotate Image"),
48534775 Poul Sander 2026-06-08 18:57 749
            tr("The image orientation could not be changed. The format may not "
48534775 Poul Sander 2026-06-08 18:57 750
               "support EXIF orientation.\n\n%1")
48534775 Poul Sander 2026-06-08 18:57 751
                .arg(displayPath(path)));
48534775 Poul Sander 2026-06-08 18:57 752
        return;
48534775 Poul Sander 2026-06-08 18:57 753
    }
48534775 Poul Sander 2026-06-08 18:57 754
48534775 Poul Sander 2026-06-08 18:57 755
    m_imageModel->refreshThumbnail(index);
48534775 Poul Sander 2026-06-08 18:57 756
    showPreview(index);
48534775 Poul Sander 2026-06-08 18:57 757
48534775 Poul Sander 2026-06-08 18:57 758
    if (m_imageView->currentPath() == path) {
48534775 Poul Sander 2026-06-08 18:57 759
        m_imageView->invalidateCache(path);
48534775 Poul Sander 2026-06-08 18:57 760
        m_imageView->setImage(path);
48534775 Poul Sander 2026-06-08 18:57 761
        m_imageView->setExifData(ExifReader::read(path));
48534775 Poul Sander 2026-06-08 18:57 762
    }
48534775 Poul Sander 2026-06-08 18:57 763
}
48534775 Poul Sander 2026-06-08 18:57 764
700d6f2d Poul Sander 2026-06-06 15:44 765
void MainWindow::moveImageToTrash(const QByteArray &path)
700d6f2d Poul Sander 2026-06-06 15:44 766
{
700d6f2d Poul Sander 2026-06-06 15:44 767
    if (path.isEmpty())
700d6f2d Poul Sander 2026-06-06 15:44 768
        return;
700d6f2d Poul Sander 2026-06-06 15:44 769
700d6f2d Poul Sander 2026-06-06 15:44 770
    const QString fileName = filenameFromPath(path);
700d6f2d Poul Sander 2026-06-06 15:44 771
    const QMessageBox::StandardButton answer = QMessageBox::question(
700d6f2d Poul Sander 2026-06-06 15:44 772
        this,
700d6f2d Poul Sander 2026-06-06 15:44 773
        tr("Move to Trash"),
700d6f2d Poul Sander 2026-06-06 15:44 774
        tr("Move \"%1\" to the trash?").arg(fileName),
700d6f2d Poul Sander 2026-06-06 15:44 775
        QMessageBox::Yes | QMessageBox::Cancel,
700d6f2d Poul Sander 2026-06-06 15:44 776
        QMessageBox::Cancel);
700d6f2d Poul Sander 2026-06-06 15:44 777
    if (answer != QMessageBox::Yes)
700d6f2d Poul Sander 2026-06-06 15:44 778
        return;
700d6f2d Poul Sander 2026-06-06 15:44 779
700d6f2d Poul Sander 2026-06-06 15:44 780
    if (!QFile::moveToTrash(displayPath(path))) {
700d6f2d Poul Sander 2026-06-06 15:44 781
        QMessageBox::warning(this, tr("Cannot Move to Trash"),
700d6f2d Poul Sander 2026-06-06 15:44 782
            tr("The file could not be moved to the trash.\n\n%1")
700d6f2d Poul Sander 2026-06-06 15:44 783
                .arg(displayPath(path)));
700d6f2d Poul Sander 2026-06-06 15:44 784
        return;
700d6f2d Poul Sander 2026-06-06 15:44 785
    }
700d6f2d Poul Sander 2026-06-06 15:44 786
700d6f2d Poul Sander 2026-06-06 15:44 787
    const QByteArray currentDir = m_imageModel->currentDirectory();
700d6f2d Poul Sander 2026-06-06 15:44 788
    m_previewLabel->clear();
700d6f2d Poul Sander 2026-06-06 15:44 789
    m_exifTable->setRowCount(0);
700d6f2d Poul Sander 2026-06-06 15:44 790
    m_imageModel->setDirectory(currentDir);
700d6f2d Poul Sander 2026-06-06 15:44 791
    QTimer::singleShot(0, this, &MainWindow::loadVisibleThumbnails);
700d6f2d Poul Sander 2026-06-06 15:44 792
}
700d6f2d Poul Sander 2026-06-06 15:44 793
a686d839 Poul Sander 2026-06-06 22:34 794
void MainWindow::moveCurrentImageToTrash()
a686d839 Poul Sander 2026-06-06 22:34 795
{
a686d839 Poul Sander 2026-06-06 22:34 796
    const QModelIndex index = m_listView->currentIndex();
a686d839 Poul Sander 2026-06-06 22:34 797
    if (!index.isValid() || m_imageModel->isFolder(index))
a686d839 Poul Sander 2026-06-06 22:34 798
        return;
a686d839 Poul Sander 2026-06-06 22:34 799
a686d839 Poul Sander 2026-06-06 22:34 800
    moveImageToTrash(m_imageModel->filePath(index));
a686d839 Poul Sander 2026-06-06 22:34 801
}
a686d839 Poul Sander 2026-06-06 22:34 802
61136b77 Poul Sander 2026-02-20 22:45 803
void MainWindow::leaveSingleImageMode()
61136b77 Poul Sander 2026-02-20 22:45 804
{
3968465e Poul Sander 2026-02-23 19:13 805
    if (isFullScreen()) {
ccb726b8 Poul Sander 2026-02-23 22:43 806
        m_imageViewWasFullScreen = true;
ccb726b8 Poul Sander 2026-02-23 22:43 807
        setFullScreenMode(false);
61136b77 Poul Sander 2026-02-20 22:45 808
    }
58103277 Poul Sander 2026-03-01 22:53 809
    m_imageView->clearCache();
61136b77 Poul Sander 2026-02-20 22:45 810
    m_stack->setCurrentWidget(m_browserPage);
cbc7d22b Poul Sander 2026-02-24 19:23 811
    m_menuBar->show();
3bee3e3e Poul Sander 2026-03-05 17:07 812
    if (m_rootDockWasVisible)
3bee3e3e Poul Sander 2026-03-05 17:07 813
        m_rootDock->show();
cbc7d22b Poul Sander 2026-02-24 19:23 814
    if (m_folderDockWasVisible)
cbc7d22b Poul Sander 2026-02-24 19:23 815
        m_folderDock->show();
cbc7d22b Poul Sander 2026-02-24 19:23 816
    if (m_previewDockWasVisible)
cbc7d22b Poul Sander 2026-02-24 19:23 817
        m_previewDock->show();
53fdab93 Poul Sander 2026-05-18 18:45 818
    if (m_exifDockWasVisible)
53fdab93 Poul Sander 2026-05-18 18:45 819
        m_exifDock->show();
61136b77 Poul Sander 2026-02-20 22:45 820
    m_listView->setFocus();
61136b77 Poul Sander 2026-02-20 22:45 821
}
61136b77 Poul Sander 2026-02-20 22:45 822
80a9926d Poul Sander 2026-05-19 20:28 823
QList<QByteArray> MainWindow::computeNeighborPaths(const QModelIndex &index) const
58103277 Poul Sander 2026-03-01 22:53 824
{
80a9926d Poul Sander 2026-05-19 20:28 825
    QList<QByteArray> paths;
58103277 Poul Sander 2026-03-01 22:53 826
    int row = index.row();
58103277 Poul Sander 2026-03-01 22:53 827
58103277 Poul Sander 2026-03-01 22:53 828
    // Collect up to CacheKeepBehind non-folder images before current
58103277 Poul Sander 2026-03-01 22:53 829
    int count = 0;
58103277 Poul Sander 2026-03-01 22:53 830
    for (int i = row - 1; i >= 0 && count < ImageViewWidget::CacheKeepBehind; --i) {
58103277 Poul Sander 2026-03-01 22:53 831
        QModelIndex idx = m_imageModel->index(i);
58103277 Poul Sander 2026-03-01 22:53 832
        if (!m_imageModel->isFolder(idx)) {
58103277 Poul Sander 2026-03-01 22:53 833
            paths.prepend(m_imageModel->filePath(idx));
58103277 Poul Sander 2026-03-01 22:53 834
            ++count;
58103277 Poul Sander 2026-03-01 22:53 835
        }
58103277 Poul Sander 2026-03-01 22:53 836
    }
58103277 Poul Sander 2026-03-01 22:53 837
58103277 Poul Sander 2026-03-01 22:53 838
    // Collect up to CacheReadAhead non-folder images after current
58103277 Poul Sander 2026-03-01 22:53 839
    count = 0;
58103277 Poul Sander 2026-03-01 22:53 840
    for (int i = row + 1; i < m_imageModel->rowCount() && count < ImageViewWidget::CacheReadAhead; ++i) {
58103277 Poul Sander 2026-03-01 22:53 841
        QModelIndex idx = m_imageModel->index(i);
58103277 Poul Sander 2026-03-01 22:53 842
        if (!m_imageModel->isFolder(idx)) {
58103277 Poul Sander 2026-03-01 22:53 843
            paths.append(m_imageModel->filePath(idx));
58103277 Poul Sander 2026-03-01 22:53 844
            ++count;
58103277 Poul Sander 2026-03-01 22:53 845
        }
58103277 Poul Sander 2026-03-01 22:53 846
    }
58103277 Poul Sander 2026-03-01 22:53 847
58103277 Poul Sander 2026-03-01 22:53 848
    return paths;
58103277 Poul Sander 2026-03-01 22:53 849
}
58103277 Poul Sander 2026-03-01 22:53 850
ccb726b8 Poul Sander 2026-02-23 22:43 851
void MainWindow::setFullScreenMode(bool fullScreen)
ccb726b8 Poul Sander 2026-02-23 22:43 852
{
ccb726b8 Poul Sander 2026-02-23 22:43 853
    if (fullScreen == isFullScreen())
ccb726b8 Poul Sander 2026-02-23 22:43 854
        return;
ccb726b8 Poul Sander 2026-02-23 22:43 855
ccb726b8 Poul Sander 2026-02-23 22:43 856
    if (fullScreen) {
ccb726b8 Poul Sander 2026-02-23 22:43 857
        m_wasMaximized = isMaximized();
ccb726b8 Poul Sander 2026-02-23 22:43 858
        m_menuBar->hide();
ccb726b8 Poul Sander 2026-02-23 22:43 859
        showFullScreen();
ccb726b8 Poul Sander 2026-02-23 22:43 860
    } else {
cbc7d22b Poul Sander 2026-02-24 19:23 861
        // Only restore menu bar when returning to browse mode
cbc7d22b Poul Sander 2026-02-24 19:23 862
        if (m_stack->currentWidget() != m_imageView)
cbc7d22b Poul Sander 2026-02-24 19:23 863
            m_menuBar->show();
ccb726b8 Poul Sander 2026-02-23 22:43 864
        if (m_wasMaximized)
ccb726b8 Poul Sander 2026-02-23 22:43 865
            showMaximized();
ccb726b8 Poul Sander 2026-02-23 22:43 866
        else
ccb726b8 Poul Sander 2026-02-23 22:43 867
            showNormal();
ccb726b8 Poul Sander 2026-02-23 22:43 868
    }
ccb726b8 Poul Sander 2026-02-23 22:43 869
}
ccb726b8 Poul Sander 2026-02-23 22:43 870
61136b77 Poul Sander 2026-02-20 22:45 871
void MainWindow::keyPressEvent(QKeyEvent *event)
61136b77 Poul Sander 2026-02-20 22:45 872
{
4d6b7476 Poul Sander 2026-05-30 16:39 873
    // THREADING: No race condition with F11 fullscreen toggle.  All cross-thread
4d6b7476 Poul Sander 2026-05-30 16:39 874
    // signal deliveries (thumbnailReady via explicit QueuedConnection,
4d6b7476 Poul Sander 2026-05-30 16:39 875
    // onImageLoaded via AutoConnection resolving to QueuedConnection) execute on
4d6b7476 Poul Sander 2026-05-30 16:39 876
    // the main thread's event loop.  The event loop processes events one at a
4d6b7476 Poul Sander 2026-05-30 16:39 877
    // time, so this handler and any signal slot can never interleave.  All widget
4d6b7476 Poul Sander 2026-05-30 16:39 878
    // state (m_wasMaximized, m_stack, fullscreen flag) is only accessed from the
4d6b7476 Poul Sander 2026-05-30 16:39 879
    // main thread.
83f0e0bc Poul Sander 2026-05-30 17:59 880
    const ShortcutManager &sc = ShortcutManager::instance();
83f0e0bc Poul Sander 2026-05-30 17:59 881
83f0e0bc Poul Sander 2026-05-30 17:59 882
    if (sc.matches(event, ShortcutManager::ToggleFullscreen)
83f0e0bc Poul Sander 2026-05-30 17:59 883
        && m_stack->currentWidget() == m_imageView)
61136b77 Poul Sander 2026-02-20 22:45 884
    {
ccb726b8 Poul Sander 2026-02-23 22:43 885
        setFullScreenMode(!isFullScreen());
61136b77 Poul Sander 2026-02-20 22:45 886
        event->accept();
61136b77 Poul Sander 2026-02-20 22:45 887
        return;
61136b77 Poul Sander 2026-02-20 22:45 888
    }
61136b77 Poul Sander 2026-02-20 22:45 889
83f0e0bc Poul Sander 2026-05-30 17:59 890
    if (sc.matches(event, ShortcutManager::ActivateEntry)
83f0e0bc Poul Sander 2026-05-30 17:59 891
        && m_stack->currentWidget() == m_browserPage)
61136b77 Poul Sander 2026-02-20 22:45 892
    {
61136b77 Poul Sander 2026-02-20 22:45 893
        QModelIndex current = m_listView->currentIndex();
61136b77 Poul Sander 2026-02-20 22:45 894
        if (current.isValid())
61136b77 Poul Sander 2026-02-20 22:45 895
        {
09130ab9 Poul Sander 2026-02-22 22:19 896
            if (m_imageModel->isFolder(current)) {
80a9926d Poul Sander 2026-05-19 20:28 897
                QByteArray path = m_imageModel->filePath(current);
665ac308 Poul Sander 2026-02-23 19:34 898
                navigateToFolder(path);
09130ab9 Poul Sander 2026-02-22 22:19 899
            } else {
09130ab9 Poul Sander 2026-02-22 22:19 900
                enterSingleImageMode(current);
09130ab9 Poul Sander 2026-02-22 22:19 901
            }
61136b77 Poul Sander 2026-02-20 22:45 902
            event->accept();
61136b77 Poul Sander 2026-02-20 22:45 903
            return;
61136b77 Poul Sander 2026-02-20 22:45 904
        }
61136b77 Poul Sander 2026-02-20 22:45 905
    }
61136b77 Poul Sander 2026-02-20 22:45 906
bf525669 Poul Sander 2026-05-30 23:18 907
    if (sc.matches(event, ShortcutManager::PrevFolder)
bf525669 Poul Sander 2026-05-30 23:18 908
        && m_stack->currentWidget() == m_browserPage)
bf525669 Poul Sander 2026-05-30 23:18 909
    {
bf525669 Poul Sander 2026-05-30 23:18 910
        navigateToPrevFolder();
bf525669 Poul Sander 2026-05-30 23:18 911
        event->accept();
bf525669 Poul Sander 2026-05-30 23:18 912
        return;
bf525669 Poul Sander 2026-05-30 23:18 913
    }
bf525669 Poul Sander 2026-05-30 23:18 914
bf525669 Poul Sander 2026-05-30 23:18 915
    if (sc.matches(event, ShortcutManager::NextFolder)
bf525669 Poul Sander 2026-05-30 23:18 916
        && m_stack->currentWidget() == m_browserPage)
bf525669 Poul Sander 2026-05-30 23:18 917
    {
bf525669 Poul Sander 2026-05-30 23:18 918
        navigateToNextFolder();
bf525669 Poul Sander 2026-05-30 23:18 919
        event->accept();
bf525669 Poul Sander 2026-05-30 23:18 920
        return;
bf525669 Poul Sander 2026-05-30 23:18 921
    }
bf525669 Poul Sander 2026-05-30 23:18 922
a686d839 Poul Sander 2026-06-06 22:34 923
    if (sc.matches(event, ShortcutManager::MoveToTrash)
a686d839 Poul Sander 2026-06-06 22:34 924
        && m_stack->currentWidget() == m_browserPage)
a686d839 Poul Sander 2026-06-06 22:34 925
    {
a686d839 Poul Sander 2026-06-06 22:34 926
        moveCurrentImageToTrash();
a686d839 Poul Sander 2026-06-06 22:34 927
        event->accept();
a686d839 Poul Sander 2026-06-06 22:34 928
        return;
a686d839 Poul Sander 2026-06-06 22:34 929
    }
a686d839 Poul Sander 2026-06-06 22:34 930
61136b77 Poul Sander 2026-02-20 22:45 931
    QMainWindow::keyPressEvent(event);
61136b77 Poul Sander 2026-02-20 22:45 932
}
c9e5684a Poul Sander 2026-02-23 19:18 933
c9e5684a Poul Sander 2026-02-23 19:18 934
void MainWindow::closeEvent(QCloseEvent *event)
c9e5684a Poul Sander 2026-02-23 19:18 935
{
c9e5684a Poul Sander 2026-02-23 19:18 936
    if (m_stack->currentWidget() == m_imageView)
c9e5684a Poul Sander 2026-02-23 19:18 937
    {
c9e5684a Poul Sander 2026-02-23 19:18 938
        leaveSingleImageMode();
c9e5684a Poul Sander 2026-02-23 19:18 939
        event->ignore();
c9e5684a Poul Sander 2026-02-23 19:18 940
        return;
c9e5684a Poul Sander 2026-02-23 19:18 941
    }
c9e5684a Poul Sander 2026-02-23 19:18 942
f61bf886 Poul Sander 2026-02-23 19:27 943
    savePreferences();
c9e5684a Poul Sander 2026-02-23 19:18 944
    QMainWindow::closeEvent(event);
c9e5684a Poul Sander 2026-02-23 19:18 945
}
f61bf886 Poul Sander 2026-02-23 19:27 946
4ae57f60 Poul Sander 2026-02-25 21:35 947
void MainWindow::applyDockLocking(bool lock)
4ae57f60 Poul Sander 2026-02-25 21:35 948
{
4ae57f60 Poul Sander 2026-02-25 21:35 949
    const QDockWidget::DockWidgetFeatures movableFeatures =
4ae57f60 Poul Sander 2026-02-25 21:35 950
        QDockWidget::DockWidgetMovable | QDockWidget::DockWidgetFloatable;
4ae57f60 Poul Sander 2026-02-25 21:35 951
    const QDockWidget::DockWidgetFeatures baseFeatures =
4ae57f60 Poul Sander 2026-02-25 21:35 952
        QDockWidget::DockWidgetClosable;
4ae57f60 Poul Sander 2026-02-25 21:35 953
4ae57f60 Poul Sander 2026-02-25 21:35 954
    QDockWidget::DockWidgetFeatures features = lock ? baseFeatures
4ae57f60 Poul Sander 2026-02-25 21:35 955
                                                    : (baseFeatures | movableFeatures);
3bee3e3e Poul Sander 2026-03-05 17:07 956
    m_rootDock->setFeatures(features);
4ae57f60 Poul Sander 2026-02-25 21:35 957
    m_folderDock->setFeatures(features);
4ae57f60 Poul Sander 2026-02-25 21:35 958
    m_previewDock->setFeatures(features);
53fdab93 Poul Sander 2026-05-18 18:45 959
    m_exifDock->setFeatures(features);
4ae57f60 Poul Sander 2026-02-25 21:35 960
}
4ae57f60 Poul Sander 2026-02-25 21:35 961
f61bf886 Poul Sander 2026-02-23 19:27 962
void MainWindow::loadPreferences()
f61bf886 Poul Sander 2026-02-23 19:27 963
{
f61bf886 Poul Sander 2026-02-23 19:27 964
    QSettings settings("SagoImageBrowser", "SagoImageBrowser");
f61bf886 Poul Sander 2026-02-23 19:27 965
    m_backgroundColorPreference = settings.value("backgroundColor", "black").toString();
f61bf886 Poul Sander 2026-02-23 19:27 966
    m_imageView->setBackgroundColor(m_backgroundColorPreference);
4ae57f60 Poul Sander 2026-02-25 21:35 967
    m_lockDockingPreference = settings.value("lockDocking", false).toBool();
4ae57f60 Poul Sander 2026-02-25 21:35 968
    applyDockLocking(m_lockDockingPreference);
459bad0f Poul Sander 2026-05-17 14:11 969
    m_folderCacheSizePreference = settings.value("folderCacheSize", 4).toInt();
459bad0f Poul Sander 2026-05-17 14:11 970
    m_imageModel->setCacheMaxSize(m_folderCacheSizePreference);
ac476ca8 Poul Sander 2026-05-18 16:47 971
    m_thumbnailCacheSizePreference = settings.value("thumbnailCacheSize", "normal").toString();
ac476ca8 Poul Sander 2026-05-18 16:47 972
    m_imageModel->setThumbnailSize(thumbnailSizeFromString(m_thumbnailCacheSizePreference));
8cb93c2f Poul Sander 2026-06-28 19:31 973
    m_rootLabelsPreference = settings.value("rootLabels").toStringList();
8cb93c2f Poul Sander 2026-06-28 19:31 974
    m_rootPathsPreference = settings.value("rootPaths").toStringList();
8cb93c2f Poul Sander 2026-06-28 19:31 975
    if (m_rootLabelsPreference.isEmpty() || m_rootPathsPreference.isEmpty()) {
8cb93c2f Poul Sander 2026-06-28 19:31 976
        m_rootLabelsPreference = {tr("Home"), tr("Pictures"), tr("File System")};
8cb93c2f Poul Sander 2026-06-28 19:31 977
        m_rootPathsPreference = {QDir::homePath(), QStandardPaths::writableLocation(QStandardPaths::PicturesLocation), QDir::rootPath()};
8cb93c2f Poul Sander 2026-06-28 19:31 978
    }
8cb93c2f Poul Sander 2026-06-28 19:31 979
    updateRootList();
8cb93c2f Poul Sander 2026-06-28 19:31 980
83f0e0bc Poul Sander 2026-05-30 17:59 981
    ShortcutManager::instance().load(settings);
cbc7d22b Poul Sander 2026-02-24 19:23 982
    if (settings.contains("windowGeometry"))
cbc7d22b Poul Sander 2026-02-24 19:23 983
        restoreGeometry(settings.value("windowGeometry").toByteArray());
cbc7d22b Poul Sander 2026-02-24 19:23 984
    if (settings.contains("windowState"))
cbc7d22b Poul Sander 2026-02-24 19:23 985
        restoreState(settings.value("windowState").toByteArray());
f61bf886 Poul Sander 2026-02-23 19:27 986
}
f61bf886 Poul Sander 2026-02-23 19:27 987
f61bf886 Poul Sander 2026-02-23 19:27 988
void MainWindow::savePreferences()
f61bf886 Poul Sander 2026-02-23 19:27 989
{
f61bf886 Poul Sander 2026-02-23 19:27 990
    QSettings settings("SagoImageBrowser", "SagoImageBrowser");
f61bf886 Poul Sander 2026-02-23 19:27 991
    settings.setValue("backgroundColor", m_backgroundColorPreference);
4ae57f60 Poul Sander 2026-02-25 21:35 992
    settings.setValue("lockDocking", m_lockDockingPreference);
459bad0f Poul Sander 2026-05-17 14:11 993
    settings.setValue("folderCacheSize", m_folderCacheSizePreference);
ac476ca8 Poul Sander 2026-05-18 16:47 994
    settings.setValue("thumbnailCacheSize", m_thumbnailCacheSizePreference);
8cb93c2f Poul Sander 2026-06-28 19:31 995
    settings.setValue("rootLabels", m_rootLabelsPreference);
8cb93c2f Poul Sander 2026-06-28 19:31 996
    settings.setValue("rootPaths", m_rootPathsPreference);
83f0e0bc Poul Sander 2026-05-30 17:59 997
    ShortcutManager::instance().save(settings);
cbc7d22b Poul Sander 2026-02-24 19:23 998
    settings.setValue("windowGeometry", saveGeometry());
cbc7d22b Poul Sander 2026-02-24 19:23 999
    settings.setValue("windowState", saveState());
f61bf886 Poul Sander 2026-02-23 19:27 1000
}
f61bf886 Poul Sander 2026-02-23 19:27 1001
f61bf886 Poul Sander 2026-02-23 19:27 1002
void MainWindow::onPreferencesTriggered()
f61bf886 Poul Sander 2026-02-23 19:27 1003
{
f61bf886 Poul Sander 2026-02-23 19:27 1004
    PreferencesDialog dialog(this);
f61bf886 Poul Sander 2026-02-23 19:27 1005
    dialog.setBackgroundColor(m_backgroundColorPreference);
4ae57f60 Poul Sander 2026-02-25 21:35 1006
    dialog.setLockDocking(m_lockDockingPreference);
459bad0f Poul Sander 2026-05-17 14:11 1007
    dialog.setCacheSize(m_folderCacheSizePreference);
ac476ca8 Poul Sander 2026-05-18 16:47 1008
    dialog.setThumbnailCacheSize(m_thumbnailCacheSizePreference);
8cb93c2f Poul Sander 2026-06-28 19:31 1009
    dialog.setRootLabels(m_rootLabelsPreference);
8cb93c2f Poul Sander 2026-06-28 19:31 1010
    dialog.setRootPaths(m_rootPathsPreference);
f61bf886 Poul Sander 2026-02-23 19:27 1011
83f0e0bc Poul Sander 2026-05-30 17:59 1012
    // Populate shortcuts tab from current bindings
83f0e0bc Poul Sander 2026-05-30 17:59 1013
    {
83f0e0bc Poul Sander 2026-05-30 17:59 1014
        QMap<int, QKeySequence> scMap;
83f0e0bc Poul Sander 2026-05-30 17:59 1015
        for (int i = 0; i < ShortcutManager::ActionCount; ++i)
83f0e0bc Poul Sander 2026-05-30 17:59 1016
            scMap[i] = ShortcutManager::instance().shortcut(
83f0e0bc Poul Sander 2026-05-30 17:59 1017
                static_cast<ShortcutManager::Action>(i));
83f0e0bc Poul Sander 2026-05-30 17:59 1018
        dialog.setShortcuts(scMap);
83f0e0bc Poul Sander 2026-05-30 17:59 1019
    }
83f0e0bc Poul Sander 2026-05-30 17:59 1020
7370f1c0 Poul Sander 2026-03-01 15:15 1021
    bool shouldResetLayout = false;
7370f1c0 Poul Sander 2026-03-01 15:15 1022
    connect(&dialog, &PreferencesDialog::resetLayoutRequested, this, [&shouldResetLayout]() {
7370f1c0 Poul Sander 2026-03-01 15:15 1023
        shouldResetLayout = true;
7370f1c0 Poul Sander 2026-03-01 15:15 1024
    });
7370f1c0 Poul Sander 2026-03-01 15:15 1025
f61bf886 Poul Sander 2026-02-23 19:27 1026
    if (dialog.exec() == QDialog::Accepted)
f61bf886 Poul Sander 2026-02-23 19:27 1027
    {
f61bf886 Poul Sander 2026-02-23 19:27 1028
        m_backgroundColorPreference = dialog.getBackgroundColor();
f61bf886 Poul Sander 2026-02-23 19:27 1029
        m_imageView->setBackgroundColor(m_backgroundColorPreference);
4ae57f60 Poul Sander 2026-02-25 21:35 1030
        m_lockDockingPreference = dialog.getLockDocking();
4ae57f60 Poul Sander 2026-02-25 21:35 1031
        applyDockLocking(m_lockDockingPreference);
459bad0f Poul Sander 2026-05-17 14:11 1032
        m_folderCacheSizePreference = dialog.getCacheSize();
459bad0f Poul Sander 2026-05-17 14:11 1033
        m_imageModel->setCacheMaxSize(m_folderCacheSizePreference);
ac476ca8 Poul Sander 2026-05-18 16:47 1034
        QString newThumbSize = dialog.getThumbnailCacheSize();
ac476ca8 Poul Sander 2026-05-18 16:47 1035
        if (newThumbSize != m_thumbnailCacheSizePreference)
ac476ca8 Poul Sander 2026-05-18 16:47 1036
        {
ac476ca8 Poul Sander 2026-05-18 16:47 1037
            m_thumbnailCacheSizePreference = newThumbSize;
ac476ca8 Poul Sander 2026-05-18 16:47 1038
            m_imageModel->setThumbnailSize(thumbnailSizeFromString(m_thumbnailCacheSizePreference));
ac476ca8 Poul Sander 2026-05-18 16:47 1039
            loadVisibleThumbnails();
ac476ca8 Poul Sander 2026-05-18 16:47 1040
        }
83f0e0bc Poul Sander 2026-05-30 17:59 1041
8cb93c2f Poul Sander 2026-06-28 19:31 1042
        m_rootLabelsPreference = dialog.getRootLabels();
8cb93c2f Poul Sander 2026-06-28 19:31 1043
        m_rootPathsPreference = dialog.getRootPaths();
8cb93c2f Poul Sander 2026-06-28 19:31 1044
        updateRootList();
8cb93c2f Poul Sander 2026-06-28 19:31 1045
83f0e0bc Poul Sander 2026-05-30 17:59 1046
        // Apply shortcut changes
83f0e0bc Poul Sander 2026-05-30 17:59 1047
        QMap<int, QKeySequence> scMap = dialog.getShortcuts();
83f0e0bc Poul Sander 2026-05-30 17:59 1048
        for (auto it = scMap.cbegin(); it != scMap.cend(); ++it)
83f0e0bc Poul Sander 2026-05-30 17:59 1049
            ShortcutManager::instance().setShortcut(
83f0e0bc Poul Sander 2026-05-30 17:59 1050
                static_cast<ShortcutManager::Action>(it.key()), it.value());
83f0e0bc Poul Sander 2026-05-30 17:59 1051
f61bf886 Poul Sander 2026-02-23 19:27 1052
        savePreferences();
f61bf886 Poul Sander 2026-02-23 19:27 1053
    }
7370f1c0 Poul Sander 2026-03-01 15:15 1054
    
7370f1c0 Poul Sander 2026-03-01 15:15 1055
    // Apply layout reset immediately after dialog closes if requested
7370f1c0 Poul Sander 2026-03-01 15:15 1056
    if (shouldResetLayout) {
7370f1c0 Poul Sander 2026-03-01 15:15 1057
        resetLayoutToDefault();
7370f1c0 Poul Sander 2026-03-01 15:15 1058
    }
7370f1c0 Poul Sander 2026-03-01 15:15 1059
}
7370f1c0 Poul Sander 2026-03-01 15:15 1060
7370f1c0 Poul Sander 2026-03-01 15:15 1061
void MainWindow::resetLayoutToDefault()
7370f1c0 Poul Sander 2026-03-01 15:15 1062
{
7370f1c0 Poul Sander 2026-03-01 15:15 1063
    QSettings settings("SagoImageBrowser", "SagoImageBrowser");
7370f1c0 Poul Sander 2026-03-01 15:15 1064
    settings.remove("windowGeometry");
7370f1c0 Poul Sander 2026-03-01 15:15 1065
    settings.remove("windowState");
7370f1c0 Poul Sander 2026-03-01 15:15 1066
    
7370f1c0 Poul Sander 2026-03-01 15:15 1067
    // Hide and detach docks completely
3bee3e3e Poul Sander 2026-03-05 17:07 1068
    m_rootDock->hide();
7370f1c0 Poul Sander 2026-03-01 15:15 1069
    m_folderDock->hide();
7370f1c0 Poul Sander 2026-03-01 15:15 1070
    m_previewDock->hide();
7370f1c0 Poul Sander 2026-03-01 15:15 1071
    
3bee3e3e Poul Sander 2026-03-05 17:07 1072
    m_rootDock->setFloating(false);
7370f1c0 Poul Sander 2026-03-01 15:15 1073
    m_folderDock->setFloating(false);
7370f1c0 Poul Sander 2026-03-01 15:15 1074
    m_previewDock->setFloating(false);
7370f1c0 Poul Sander 2026-03-01 15:15 1075
    
3bee3e3e Poul Sander 2026-03-05 17:07 1076
    removeDockWidget(m_rootDock);
7370f1c0 Poul Sander 2026-03-01 15:15 1077
    removeDockWidget(m_folderDock);
7370f1c0 Poul Sander 2026-03-01 15:15 1078
    removeDockWidget(m_previewDock);
7370f1c0 Poul Sander 2026-03-01 15:15 1079
    
7370f1c0 Poul Sander 2026-03-01 15:15 1080
    // Re-add in default positions
3bee3e3e Poul Sander 2026-03-05 17:07 1081
    addDockWidget(Qt::LeftDockWidgetArea, m_rootDock);
7370f1c0 Poul Sander 2026-03-01 15:15 1082
    addDockWidget(Qt::LeftDockWidgetArea, m_folderDock);
7370f1c0 Poul Sander 2026-03-01 15:15 1083
    addDockWidget(Qt::RightDockWidgetArea, m_previewDock);
3bee3e3e Poul Sander 2026-03-05 17:07 1084
    splitDockWidget(m_rootDock, m_folderDock, Qt::Vertical);
7370f1c0 Poul Sander 2026-03-01 15:15 1085
    
3bee3e3e Poul Sander 2026-03-05 17:07 1086
    // Show all docks
3bee3e3e Poul Sander 2026-03-05 17:07 1087
    m_rootDock->show();
7370f1c0 Poul Sander 2026-03-01 15:15 1088
    m_folderDock->show();
7370f1c0 Poul Sander 2026-03-01 15:15 1089
    m_previewDock->show();
f61bf886 Poul Sander 2026-02-23 19:27 1090
}
8cb93c2f Poul Sander 2026-06-28 19:31 1091
8cb93c2f Poul Sander 2026-06-28 19:31 1092
void MainWindow::updateRootList()
8cb93c2f Poul Sander 2026-06-28 19:31 1093
{
8cb93c2f Poul Sander 2026-06-28 19:31 1094
    m_rootList->clear();
8cb93c2f Poul Sander 2026-06-28 19:31 1095
    for (int i = 0; i < m_rootLabelsPreference.size() && i < m_rootPathsPreference.size(); ++i) {
8cb93c2f Poul Sander 2026-06-28 19:31 1096
        QListWidgetItem *item = new QListWidgetItem(m_rootLabelsPreference[i], m_rootList);
8cb93c2f Poul Sander 2026-06-28 19:31 1097
        item->setData(Qt::UserRole, m_rootPathsPreference[i]);
8cb93c2f Poul Sander 2026-06-28 19:31 1098
    }
8cb93c2f Poul Sander 2026-06-28 19:31 1099
    if (m_rootList->count() > 0)
8cb93c2f Poul Sander 2026-06-28 19:31 1100
        m_rootList->setCurrentRow(0);
8cb93c2f Poul Sander 2026-06-28 19:31 1101
}
1970-01-01 00:00 1102