Files
dull/src/mainwindow.cc
2025-11-03 12:31:15 +01:00

164 lines
5.1 KiB
C++

#include "mainwindow.h"
#include <QDesktopServices>
#include <QFileDialog>
#include <QInputDialog>
#include <QMessageBox>
#include <QTemporaryFile>
#include <botan/hex.h>
MainWindow::MainWindow(QWidget *parent)
: QMainWindow(parent), ui(std::make_unique<Ui::MainWindow>()) {
ui->setupUi(this);
ui->filePreview->setVisible(false);
connect(ui->actionNew, &QAction::triggered, this, [this]() {
QString path = QFileDialog::getSaveFileName(this, "Choose vault location",
QDir::currentPath(),
"Dull Vaults (*.dull)");
if (path.isEmpty()) {
return;
}
QString password = QInputDialog::getText(
this, "Choose a password", "Choose a password", QLineEdit::Password);
if (password.length() < 5) {
QMessageBox::critical(this, "Error",
"Password must be at least 5 characters long.");
return;
}
std::ofstream create(path.toStdString(), std::ios::binary);
create.write("DULL", 4);
create.write(to_char_ptr(&VERSION), sizeof(VERSION));
create.close();
m_vault =
std::make_unique<Vault>(path.toStdString(), password.toStdString());
reload_fs_tree();
});
connect(ui->actionOpen, &QAction::triggered, this, [this]() {
QString path = QFileDialog::getOpenFileName(this, "Choose vault to open",
QDir::currentPath(),
"Dull Vaults (*.dull)");
if (path.isEmpty()) {
return;
}
QString password = QInputDialog::getText(
this, "Unlock the vault", "Enter vault password", QLineEdit::Password);
if (password.length() < 5) {
QMessageBox::critical(this, "Error",
"Password must be at least 5 characters long.");
return;
}
// TODO: check if password valid
m_vault =
std::make_unique<Vault>(path.toStdString(), password.toStdString());
reload_fs_tree();
});
connect(
ui->fsTreeWidget, &QTreeWidget::itemClicked,
[this](QTreeWidgetItem *, i32) { ui->filePreview->setVisible(false); });
connect(ui->fsTreeWidget, &QTreeWidget::customContextMenuRequested, this,
&MainWindow::file_context_menu);
connect(ui->actionAddFiles, &QAction::triggered, this, [this]() {
if (!m_vault) {
return;
}
QStringList paths =
QFileDialog::getOpenFileNames(this, "Choose files to add");
for (const auto &path : paths) {
std::ifstream file(path.toStdString(), std::ios::binary);
std::string content((std::istreambuf_iterator<char>(file)),
std::istreambuf_iterator<char>());
m_vault->create_file(path_to_filename(path.toStdString()), content);
}
reload_fs_tree();
});
}
void MainWindow::reload_fs_tree() {
ui->menuFiles->setEnabled(true);
ui->fsTreeWidget->clear();
auto headers = m_vault->read_file_headers();
for (const auto &header : headers) {
auto *item = new QTreeWidgetItem(ui->fsTreeWidget);
item->setText(0, QString::fromStdString(header.name));
item->setText(1, QString::number(header.size));
item->setText(2, QString::number(header.offset));
item->setText(3, QString::fromStdString(Botan::hex_encode(header.nonce)));
}
ui->fsTreeWidget->resizeColumnToContents(0);
}
void MainWindow::preview_file(const std::string &filename) {
ui->filePreview->setVisible(true);
auto content = m_vault->read_file(filename);
if (content) {
ui->filePreview->setText(QString::fromStdString(content.value()));
} else {
qWarning() << "File to preview not found";
}
}
void MainWindow::edit_file(const std::string &filename) {
auto content = m_vault->read_file(filename);
if (content) {
QTemporaryFile temp_file;
ASSERT(temp_file.open());
{
QTextStream out(&temp_file);
out << QString::fromStdString(content.value());
}
temp_file.flush();
QDesktopServices::openUrl(QUrl::fromLocalFile(temp_file.fileName()));
QMessageBox::information(this, "Edit",
"Please edit the file in the opened editor and "
"save it. Click OK when done.");
temp_file.seek(0);
QTextStream in(&temp_file);
m_vault->update_file(filename, in.readAll().toStdString());
reload_fs_tree();
// QTemporaryFile gets deleted when it goes out of scope
} else {
qWarning() << "File to edit not found";
}
}
void MainWindow::file_context_menu(const QPoint &pos) {
QTreeWidgetItem *item = ui->fsTreeWidget->itemAt(pos);
if (item == nullptr) {
return;
}
QMenu menu(this);
QAction *preview_action = menu.addAction(
style()->standardIcon(QStyle::SP_FileDialogContentsView), "Preview");
connect(preview_action, &QAction::triggered, this,
[this, item]() { preview_file(item->text(0).toStdString()); });
QAction *edit_action = menu.addAction(
style()->standardIcon(QStyle::SP_FileDialogDetailedView), "Edit");
connect(edit_action, &QAction::triggered, this,
[this, item]() { edit_file(item->text(0).toStdString()); });
menu.exec(ui->fsTreeWidget->mapToGlobal(pos));
}