Files
adfmanager/mainwindow.cpp
T
2026-11-01 15:38:43 +01:00

771 lines
26 KiB
C++
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
// SPDX-License-Identifier: GPL-3.0-or-later
// Copyright (C) 2026 Dany Thinnes
#include "mainwindow.h"
#include <QApplication>
#include <QCheckBox>
#include <QCloseEvent>
#include <QDialog>
#include <QDir>
#include <QFileDialog>
#include <QFileInfo>
#include <QFileSystemModel>
#include <QGridLayout>
#include <QHBoxLayout>
#include <QHeaderView>
#include <QIcon>
#include <QInputDialog>
#include <QLabel>
#include <QLineEdit>
#include <QMenuBar>
#include <QMessageBox>
#include <QPainter>
#include <QPainterPath>
#include <QPlainTextEdit>
#include <QProcess>
#include <QPushButton>
#include <QSettings>
#include <QSplitter>
#include <QStyle>
#include <QTreeView>
#include <QTreeWidget>
#include <QVBoxLayout>
#include <algorithm>
#ifndef APP_VERSION
#define APP_VERSION "0.2.0"
#endif
namespace {
constexpr int RolePath = Qt::UserRole;
constexpr int RoleIsDir = Qt::UserRole + 1;
constexpr int RoleUp = Qt::UserRole + 2;
QString humanSize(qint64 s)
{
if (s < 1024)
return QString::number(s) + QStringLiteral(" B");
return QString::number(s / 1024.0, 'f', 1) + QStringLiteral(" KiB");
}
QString parentOf(const QString &path)
{
const int i = path.lastIndexOf('/');
return i < 0 ? QString() : path.left(i);
}
// Einfaches Diskettensymbol als Platzhalter-Logo (kann spaeter durch ein PNG ersetzt werden)
QPixmap makeLogo(int size)
{
QPixmap pm(size, size);
pm.fill(Qt::transparent);
QPainter p(&pm);
p.setRenderHint(QPainter::Antialiasing);
p.scale(size / 72.0, size / 72.0);
p.setPen(Qt::NoPen);
QPainterPath body;
body.addRoundedRect(QRectF(6, 6, 60, 60), 5, 5);
QPainterPath cut;
cut.moveTo(48, 6);
cut.lineTo(66, 24);
cut.lineTo(66, 6);
cut.closeSubpath();
p.setBrush(QColor("#56b6c2"));
p.drawPath(body.subtracted(cut));
p.setBrush(QColor("#1e1e1e"));
p.drawRect(QRectF(20, 6, 26, 18));
p.setBrush(QColor("#d4d4d4"));
p.drawRect(QRectF(37, 9, 6, 12));
p.drawRoundedRect(QRectF(14, 38, 44, 24), 2, 2);
p.setBrush(QColor("#56b6c2"));
p.drawRect(QRectF(19, 44, 34, 2));
p.drawRect(QRectF(19, 50, 26, 2));
return pm;
}
bool runUdisks(const QStringList &args, QString &output)
{
QProcess p;
p.start(QStringLiteral("udisksctl"), args);
if (!p.waitForStarted(5000)) {
output = QStringLiteral("udisksctl konnte nicht gestartet werden (Paket udisks2 installiert?)");
return false;
}
p.waitForFinished(30000);
output = (QString::fromUtf8(p.readAllStandardOutput()) + QString::fromUtf8(p.readAllStandardError())).trimmed();
return p.exitStatus() == QProcess::NormalExit && p.exitCode() == 0;
}
} // namespace
MainWindow::MainWindow()
{
setWindowTitle(QStringLiteral("ADF Manager"));
setWindowIcon(QIcon(makeLogo(64)));
resize(1200, 700);
QSettings s;
m_tool.setProgram(s.value("xdftool", "xdftool").toString());
// ---- Menue ----
QMenu *fileMenu = menuBar()->addMenu(tr("&Datei"));
fileMenu->addAction(tr("ADF &öffnen …"), QKeySequence::Open, this, &MainWindow::openAdf);
fileMenu->addAction(tr("&Neu laden"), QKeySequence::Refresh, this, &MainWindow::reload);
m_ejectAct = fileMenu->addAction(tr("ADF-Medium &auswerfen"), this, &MainWindow::ejectDevice);
m_ejectAct->setEnabled(false);
fileMenu->addSeparator();
fileMenu->addAction(tr("xdftool-&Pfad …"), this, &MainWindow::setToolPath);
fileMenu->addSeparator();
fileMenu->addAction(tr("&Beenden"), QKeySequence::Quit, this, &QWidget::close);
QMenu *helpMenu = menuBar()->addMenu(tr("&Hilfe"));
helpMenu->addAction(tr("Über ADF Manager"), this, &MainWindow::showAbout);
helpMenu->addAction(tr("Über &Qt"), this, [] { QApplication::aboutQt(); });
// ---- Linke Seite: ADF ----
m_adfLabel = new QLabel(tr("Keine ADF geöffnet"));
auto *btnOpen = new QPushButton(tr("ADF öffnen …"));
connect(btnOpen, &QPushButton::clicked, this, &MainWindow::openAdf);
m_ejectBtn = new QPushButton(tr("⏏ Auswerfen"));
m_ejectBtn->setEnabled(false);
m_ejectBtn->setToolTip(tr("Nur aktiv, wenn die ADF auf einer SD-Karte / einem Wechseldatenträger liegt"));
connect(m_ejectBtn, &QPushButton::clicked, this, &MainWindow::ejectDevice);
m_cwdLabel = new QLabel;
m_adfTree = new QTreeWidget;
m_adfTree->setColumnCount(3);
m_adfTree->setHeaderLabels({ tr("Name"), tr("Größe"), tr("Typ") });
m_adfTree->setSelectionMode(QAbstractItemView::ExtendedSelection);
m_adfTree->setAlternatingRowColors(true);
m_adfTree->setRootIsDecorated(false);
m_adfTree->setUniformRowHeights(true);
m_adfTree->header()->setSectionResizeMode(QHeaderView::Interactive);
m_adfTree->header()->setStretchLastSection(false);
m_adfTree->setColumnWidth(0, 320);
m_adfTree->setColumnWidth(1, 90);
m_adfTree->setColumnWidth(2, 80);
connect(m_adfTree, &QTreeWidget::itemActivated, this, &MainWindow::adfActivated);
auto *btnDelete = new QPushButton(tr("Löschen"));
auto *btnRename = new QPushButton(tr("Umbenennen"));
auto *btnMkdir = new QPushButton(tr("Neuer Ordner"));
connect(btnDelete, &QPushButton::clicked, this, &MainWindow::deleteSelected);
connect(btnRename, &QPushButton::clicked, this, &MainWindow::renameSelected);
connect(btnMkdir, &QPushButton::clicked, this, &MainWindow::makeDir);
auto *leftTop = new QHBoxLayout;
leftTop->addWidget(btnOpen);
leftTop->addWidget(m_adfLabel, 1);
leftTop->addWidget(m_ejectBtn);
auto *leftBottom = new QHBoxLayout;
leftBottom->addWidget(btnDelete);
leftBottom->addWidget(btnRename);
leftBottom->addWidget(btnMkdir);
leftBottom->addStretch(1);
auto *left = new QWidget;
auto *leftLay = new QVBoxLayout(left);
leftLay->addLayout(leftTop);
leftLay->addWidget(m_cwdLabel);
leftLay->addWidget(m_adfTree, 1);
leftLay->addLayout(leftBottom);
// ---- Mitte: Kopier-Buttons ----
auto *btnToPc = new QPushButton(tr("→\nAuf PC\nkopieren"));
auto *btnToAdf = new QPushButton(tr("←\nIn ADF\nkopieren"));
btnToPc->setMinimumHeight(70);
btnToAdf->setMinimumHeight(70);
connect(btnToPc, &QPushButton::clicked, this, &MainWindow::adfToPc);
connect(btnToAdf, &QPushButton::clicked, this, &MainWindow::pcToAdf);
auto *mid = new QWidget;
auto *midLay = new QVBoxLayout(mid);
midLay->addStretch(1);
midLay->addWidget(btnToPc);
midLay->addWidget(btnToAdf);
midLay->addStretch(1);
mid->setFixedWidth(100);
// ---- Rechte Seite: PC-Ordner ----
m_localEdit = new QLineEdit;
m_localEdit->setReadOnly(true);
auto *btnChoose = new QPushButton(tr("Ordner wählen …"));
auto *btnUp = new QPushButton(tr("↑"));
btnUp->setToolTip(tr("Eine Ebene höher"));
btnUp->setFixedWidth(36);
connect(btnChoose, &QPushButton::clicked, this, &MainWindow::chooseLocalDir);
connect(btnUp, &QPushButton::clicked, this, &MainWindow::localUp);
const bool showHidden = s.value("showHidden", false).toBool();
m_localModel = new QFileSystemModel(this);
m_localModel->setFilter(QDir::AllEntries | QDir::NoDotAndDotDot
| (showHidden ? QDir::Hidden : QDir::Filters()));
m_localView = new QTreeView;
m_localView->setModel(m_localModel);
m_localView->setSelectionMode(QAbstractItemView::ExtendedSelection);
m_localView->setRootIsDecorated(false);
m_localView->setSortingEnabled(true);
m_localView->sortByColumn(0, Qt::AscendingOrder);
m_localView->setAlternatingRowColors(true);
m_localView->header()->setSectionResizeMode(QHeaderView::Interactive);
m_localView->header()->setStretchLastSection(false);
m_localView->setColumnWidth(0, 320);
m_localView->setColumnWidth(1, 90);
m_localView->setColumnWidth(2, 90);
m_localView->setColumnWidth(3, 140);
connect(m_localView, &QTreeView::doubleClicked, this, &MainWindow::localActivated);
auto *chkHidden = new QCheckBox(tr("Versteckte Dateien anzeigen"));
chkHidden->setChecked(showHidden);
connect(chkHidden, &QCheckBox::toggled, this, [this](bool on) {
m_localModel->setFilter(QDir::AllEntries | QDir::NoDotAndDotDot
| (on ? QDir::Hidden : QDir::Filters()));
QSettings().setValue("showHidden", on);
});
auto *rightTop = new QHBoxLayout;
rightTop->addWidget(btnChoose);
rightTop->addWidget(btnUp);
rightTop->addWidget(m_localEdit, 1);
auto *right = new QWidget;
auto *rightLay = new QVBoxLayout(right);
rightLay->addLayout(rightTop);
rightLay->addWidget(chkHidden);
rightLay->addWidget(m_localView, 1);
// ---- Zusammenbau ----
auto *top = new QWidget;
auto *topLay = new QHBoxLayout(top);
topLay->setContentsMargins(0, 0, 0, 0);
topLay->addWidget(left, 1);
topLay->addWidget(mid);
topLay->addWidget(right, 1);
m_log = new QPlainTextEdit;
m_log->setReadOnly(true);
m_log->setMaximumBlockCount(2000);
auto *split = new QSplitter(Qt::Vertical);
split->addWidget(top);
split->addWidget(m_log);
split->setStretchFactor(0, 5);
split->setStretchFactor(1, 1);
setCentralWidget(split);
setLocalDir(s.value("localDir", QDir::homePath()).toString());
updatePathLabel();
// Fenstergeometrie und Spaltenbreiten wiederherstellen
if (s.contains("geometry"))
restoreGeometry(s.value("geometry").toByteArray());
if (s.contains("adfHeader"))
m_adfTree->header()->restoreState(s.value("adfHeader").toByteArray());
if (s.contains("localHeader"))
m_localView->header()->restoreState(s.value("localHeader").toByteArray());
}
void MainWindow::closeEvent(QCloseEvent *e)
{
QSettings s;
s.setValue("geometry", saveGeometry());
s.setValue("adfHeader", m_adfTree->header()->saveState());
s.setValue("localHeader", m_localView->header()->saveState());
QMainWindow::closeEvent(e);
}
void MainWindow::log(const QString &text)
{
m_log->appendPlainText(text);
}
bool MainWindow::haveAdf()
{
if (m_adf.isEmpty()) {
QMessageBox::information(this, tr("ADF Manager"), tr("Bitte zuerst eine ADF öffnen."));
return false;
}
return true;
}
// ------------------------------------------------------------------ ADF laden
void MainWindow::openAdf()
{
QSettings s;
const QString file = QFileDialog::getOpenFileName(
this, tr("ADF auswählen"), s.value("adfDir", QDir::homePath()).toString(),
tr("Amiga-Disk-Images (*.adf *.ADF *.adz);;Alle Dateien (*)"));
if (file.isEmpty())
return;
s.setValue("adfDir", QFileInfo(file).absolutePath());
loadAdf(file);
}
void MainWindow::loadAdf(const QString &file)
{
m_adf = file;
m_cwd.clear();
m_adfLabel->setText(QFileInfo(file).fileName());
setWindowTitle(QStringLiteral("ADF Manager – %1").arg(QFileInfo(file).fileName()));
updateEjectState();
reload();
}
void MainWindow::reload()
{
if (m_adf.isEmpty())
return;
QApplication::setOverrideCursor(Qt::WaitCursor);
QList<AdfEntry> entries;
const XdfTool::Result r = m_tool.list(m_adf, entries);
QApplication::restoreOverrideCursor();
log(QStringLiteral("$ ") + r.commandLine);
if (!r.ok) {
log(r.err.isEmpty() ? r.out : r.err);
QMessageBox::warning(this, tr("xdftool"),
tr("Inhalt konnte nicht gelesen werden:\n%1").arg(r.err.isEmpty() ? r.out : r.err));
return;
}
if (entries.isEmpty())
log(tr("(keine Einträge erkannt – Rohausgabe:)\n") + r.out);
m_entries = entries;
m_known.clear();
m_dirs.clear();
for (const AdfEntry &e : m_entries) {
m_known.insert(e.path.toLower());
if (e.isDir)
m_dirs.insert(e.path.toLower());
}
// Aktuellen Ordner beibehalten, falls er noch existiert
while (!m_cwd.isEmpty() && !m_dirs.contains(m_cwd.toLower()))
m_cwd = parentOf(m_cwd);
showDir();
}
// Zeigt den Inhalt des aktuellen ADF-Ordners (m_cwd)
void MainWindow::showDir()
{
m_adfTree->clear();
if (!m_cwd.isEmpty()) {
auto *up = new QTreeWidgetItem(m_adfTree);
up->setText(0, QStringLiteral(".."));
up->setText(2, tr("Ordner"));
up->setIcon(0, style()->standardIcon(QStyle::SP_FileDialogToParent));
up->setData(0, RoleUp, true);
up->setData(0, RoleIsDir, true);
up->setData(0, RolePath, parentOf(m_cwd));
up->setFlags(Qt::ItemIsEnabled); // nicht auswaehlbar, aber aktivierbar
}
QList<AdfEntry> children;
for (const AdfEntry &e : m_entries) {
if (parentOf(e.path) == m_cwd)
children << e;
}
std::sort(children.begin(), children.end(), [](const AdfEntry &a, const AdfEntry &b) {
if (a.isDir != b.isDir)
return a.isDir;
return a.name.compare(b.name, Qt::CaseInsensitive) < 0;
});
for (const AdfEntry &e : children) {
auto *item = new QTreeWidgetItem(m_adfTree);
item->setText(0, e.name);
item->setToolTip(0, e.name);
item->setText(1, e.isDir ? QString() : humanSize(e.size));
item->setText(2, e.isDir ? tr("Ordner") : tr("Datei"));
item->setTextAlignment(1, Qt::AlignRight | Qt::AlignVCenter);
item->setIcon(0, style()->standardIcon(e.isDir ? QStyle::SP_DirIcon : QStyle::SP_FileIcon));
item->setData(0, RolePath, e.path);
item->setData(0, RoleIsDir, e.isDir);
item->setData(0, RoleUp, false);
}
updatePathLabel();
}
void MainWindow::updatePathLabel()
{
if (m_adf.isEmpty())
m_cwdLabel->setText(tr("Ziel/Quelle in der ADF: –"));
else
m_cwdLabel->setText(tr("Aktueller ADF-Ordner: <b>/%1</b> (Doppelklick öffnet Ordner)").arg(m_cwd.toHtmlEscaped()));
}
void MainWindow::adfActivated(QTreeWidgetItem *item, int)
{
if (!item || !item->data(0, RoleIsDir).toBool())
return;
m_cwd = item->data(0, RolePath).toString();
showDir();
}
QList<QTreeWidgetItem *> MainWindow::selectedEntries() const
{
QList<QTreeWidgetItem *> result;
for (QTreeWidgetItem *item : m_adfTree->selectedItems()) {
if (!item->data(0, RoleUp).toBool())
result << item;
}
return result;
}
// ------------------------------------------------------------------ xdftool ausfuehren
bool MainWindow::runBatch(const QList<QStringList> &cmds)
{
QApplication::setOverrideCursor(Qt::WaitCursor);
const XdfTool::Result r = m_tool.runBatch(m_adf, cmds);
QApplication::restoreOverrideCursor();
log(QStringLiteral("$ ") + r.commandLine);
if (!r.out.trimmed().isEmpty())
log(r.out.trimmed());
if (!r.err.trimmed().isEmpty())
log(r.err.trimmed());
if (!r.ok) {
QMessageBox::warning(this, tr("xdftool"),
tr("xdftool meldet einen Fehler:\n%1").arg(r.err.isEmpty() ? r.out : r.err));
}
return r.ok;
}
// ------------------------------------------------------------------ ADF -> PC
void MainWindow::adfToPc()
{
if (!haveAdf())
return;
const QList<QTreeWidgetItem *> items = selectedEntries();
if (items.isEmpty()) {
QMessageBox::information(this, tr("ADF Manager"), tr("Links nichts ausgewählt."));
return;
}
QList<QStringList> cmds;
for (QTreeWidgetItem *item : items) {
const QString path = item->data(0, RolePath).toString();
cmds << QStringList{ QStringLiteral("read"), path, QDir(m_localDir).filePath(item->text(0)) };
}
runBatch(cmds);
}
// ------------------------------------------------------------------ PC -> ADF
void MainWindow::pcToAdf()
{
if (!haveAdf())
return;
const QModelIndexList rows = m_localView->selectionModel()->selectedRows(0);
if (rows.isEmpty()) {
QMessageBox::information(this, tr("ADF Manager"), tr("Rechts nichts ausgewählt."));
return;
}
// Ziel ist immer der links angezeigte ADF-Ordner
const QString dir = m_cwd;
QList<QStringList> cmds;
QStringList overwritten;
for (const QModelIndex &idx : rows) {
const QString local = m_localModel->filePath(idx);
const QString name = QFileInfo(local).fileName();
const QString ami = dir.isEmpty() ? name : dir + QLatin1Char('/') + name;
if (m_known.contains(ami.toLower())) {
overwritten << ami;
cmds << QStringList{ QStringLiteral("delete"), ami, QStringLiteral("all") };
}
cmds << QStringList{ QStringLiteral("write"), local, ami };
}
if (!overwritten.isEmpty()) {
const auto answer = QMessageBox::question(
this, tr("Vorhandene Einträge ersetzen"),
tr("Diese Einträge existieren bereits in der ADF und werden vorher gelöscht:\n\n%1\n\nFortfahren?")
.arg(overwritten.join('\n')));
if (answer != QMessageBox::Yes)
return;
}
runBatch(cmds);
reload();
}
// ------------------------------------------------------------------ Loeschen / Umbenennen / Ordner
void MainWindow::deleteSelected()
{
if (!haveAdf())
return;
const QList<QTreeWidgetItem *> items = selectedEntries();
if (items.isEmpty())
return;
QStringList names;
QList<QStringList> cmds;
for (QTreeWidgetItem *item : items) {
const QString path = item->data(0, RolePath).toString();
names << path;
cmds << QStringList{ QStringLiteral("delete"), path, QStringLiteral("all") };
}
if (QMessageBox::question(this, tr("Löschen"),
tr("Wirklich aus der ADF löschen?\n\n%1").arg(names.join('\n')))
!= QMessageBox::Yes)
return;
runBatch(cmds);
reload();
}
void MainWindow::renameSelected()
{
if (!haveAdf())
return;
const QList<QTreeWidgetItem *> items = selectedEntries();
if (items.size() != 1) {
QMessageBox::information(this, tr("Umbenennen"), tr("Bitte genau einen Eintrag auswählen."));
return;
}
QTreeWidgetItem *item = items.first();
bool ok = false;
const QString newName = QInputDialog::getText(this, tr("Umbenennen"), tr("Neuer Name:"),
QLineEdit::Normal, item->text(0), &ok);
if (!ok || newName.isEmpty() || newName == item->text(0))
return;
const QString oldPath = item->data(0, RolePath).toString();
const QString parent = parentOf(oldPath);
const QString newPath = parent.isEmpty() ? newName : parent + QLatin1Char('/') + newName;
runBatch({ QStringList{ QStringLiteral("rename"), oldPath, newPath } });
reload();
}
void MainWindow::makeDir()
{
if (!haveAdf())
return;
bool ok = false;
const QString name = QInputDialog::getText(this, tr("Neuer Ordner"), tr("Name:"),
QLineEdit::Normal, QString(), &ok);
if (!ok || name.isEmpty())
return;
const QString path = m_cwd.isEmpty() ? name : m_cwd + QLatin1Char('/') + name;
runBatch({ QStringList{ QStringLiteral("makedir"), path } });
reload();
}
// ------------------------------------------------------------------ PC-Seite
void MainWindow::setLocalDir(const QString &dir)
{
if (!QFileInfo(dir).isDir())
return;
m_localDir = QDir(dir).absolutePath();
m_localEdit->setText(m_localDir);
m_localModel->setRootPath(m_localDir);
m_localView->setRootIndex(m_localModel->index(m_localDir));
QSettings().setValue("localDir", m_localDir);
}
void MainWindow::chooseLocalDir()
{
const QString dir = QFileDialog::getExistingDirectory(this, tr("Ordner auf dem PC"), m_localDir);
if (!dir.isEmpty())
setLocalDir(dir);
}
void MainWindow::localUp()
{
QDir d(m_localDir);
if (d.cdUp())
setLocalDir(d.absolutePath());
}
void MainWindow::localActivated(const QModelIndex &idx)
{
if (m_localModel->isDir(idx))
setLocalDir(m_localModel->filePath(idx));
}
void MainWindow::setToolPath()
{
bool ok = false;
const QString p = QInputDialog::getText(this, tr("xdftool"), tr("Programm / Pfad zu xdftool:"),
QLineEdit::Normal, m_tool.program(), &ok);
if (ok && !p.isEmpty()) {
m_tool.setProgram(p);
QSettings().setValue("xdftool", p);
}
}
// ------------------------------------------------------------------ Auswerfen
void MainWindow::updateEjectState()
{
m_removable = m_adf.isEmpty() ? RemovableInfo() : detectRemovable(m_adf);
m_ejectBtn->setEnabled(m_removable.removable);
m_ejectAct->setEnabled(m_removable.removable);
if (m_removable.removable) {
const QString tip = tr("%1 auswerfen (%2)")
.arg(m_removable.label.isEmpty() ? m_removable.partition : m_removable.label,
m_removable.partition);
m_ejectBtn->setToolTip(tip);
log(tr("ADF liegt auf Wechseldatenträger: %1 (%2)").arg(m_removable.partition, m_removable.mountPoint));
} else {
m_ejectBtn->setToolTip(tr("Nur aktiv, wenn die ADF auf einer SD-Karte / einem Wechseldatenträger liegt"));
}
}
void MainWindow::ejectDevice()
{
if (!m_removable.removable)
return;
const RemovableInfo dev = m_removable;
// PC-Ansicht verlassen, falls sie auf dem Medium steht
if (m_localDir == dev.mountPoint || m_localDir.startsWith(dev.mountPoint + QLatin1Char('/')))
setLocalDir(QDir::homePath());
QApplication::setOverrideCursor(Qt::WaitCursor);
QString out;
const bool unmounted = runUdisks({ QStringLiteral("unmount"), QStringLiteral("-b"), dev.partition }, out);
log(QStringLiteral("$ udisksctl unmount -b %1\n%2").arg(dev.partition, out));
bool poweredOff = false;
QString out2;
if (unmounted) {
poweredOff = runUdisks({ QStringLiteral("power-off"), QStringLiteral("-b"), dev.disk }, out2);
log(QStringLiteral("$ udisksctl power-off -b %1\n%2").arg(dev.disk, out2));
}
QApplication::restoreOverrideCursor();
if (!unmounted) {
QMessageBox::warning(this, tr("Auswerfen"), tr("Das Medium konnte nicht ausgehängt werden:\n%1").arg(out));
return;
}
// ADF-Ansicht leeren, die Datei ist nicht mehr erreichbar
m_adf.clear();
m_entries.clear();
m_known.clear();
m_dirs.clear();
m_cwd.clear();
m_adfTree->clear();
m_adfLabel->setText(tr("Keine ADF geöffnet"));
setWindowTitle(QStringLiteral("ADF Manager"));
updatePathLabel();
updateEjectState();
if (poweredOff) {
QMessageBox::information(this, tr("Auswerfen"), tr("Das Medium wurde ausgehängt und kann entfernt werden."));
} else {
QMessageBox::information(this, tr("Auswerfen"),
tr("Das Medium wurde ausgehängt.\nAbschalten war nicht möglich:\n%1").arg(out2));
}
}
// ------------------------------------------------------------------ Ueber-Dialog
void MainWindow::showAbout()
{
auto *dlg = new QDialog(this);
dlg->setWindowTitle(tr("Über ADF Manager"));
dlg->setFixedWidth(400);
auto *root = new QVBoxLayout(dlg);
root->setContentsMargins(0, 0, 0, 0);
root->setSpacing(0);
auto *header = new QWidget(dlg);
header->setFixedHeight(110);
header->setStyleSheet("background-color: #1e1e1e;");
auto *headerLayout = new QHBoxLayout(header);
headerLayout->setContentsMargins(20, 14, 24, 14);
headerLayout->setSpacing(16);
auto *logoLabel = new QLabel(header);
logoLabel->setPixmap(makeLogo(72));
logoLabel->setStyleSheet("background: transparent;");
logoLabel->setFixedSize(72, 72);
auto *titleBlock = new QVBoxLayout();
titleBlock->setSpacing(4);
auto *titleLabel = new QLabel("ADF Manager", header);
titleLabel->setStyleSheet(
"color: #ffffff; font-size: 26px; font-weight: bold; background: transparent;");
auto *subtitleLabel = new QLabel(tr("Frontend für Amiga-Disk-Images (xdftool)"), header);
subtitleLabel->setStyleSheet(
"color: #56b6c2; font-size: 11px; background: transparent;");
titleBlock->addWidget(titleLabel);
titleBlock->addWidget(subtitleLabel);
headerLayout->addWidget(logoLabel);
headerLayout->addLayout(titleBlock);
root->addWidget(header);
auto *body = new QWidget(dlg);
body->setStyleSheet("background-color: #252526;");
auto *grid = new QGridLayout(body);
grid->setContentsMargins(24, 20, 24, 20);
grid->setVerticalSpacing(10);
grid->setHorizontalSpacing(16);
grid->setColumnMinimumWidth(0, 90);
const QString labelStyle = "color: #888888; font-weight: bold; background: transparent;";
const QString valueStyle = "color: #d4d4d4; background: transparent;";
auto addRow = [&](int row, const QString &label, const QString &value) {
auto *l = new QLabel(label, body);
auto *v = new QLabel(value, body);
l->setStyleSheet(labelStyle);
v->setStyleSheet(valueStyle);
l->setAlignment(Qt::AlignRight | Qt::AlignVCenter);
grid->addWidget(l, row, 0);
grid->addWidget(v, row, 1);
};
addRow(0, tr("Version"), APP_VERSION);
addRow(1, tr("Entwickler"), "Dany Thinnes");
addRow(2, tr("Projekt"), "Projekt Hirnfrei");
addRow(3, tr("Framework"), QString("Qt %1").arg(QT_VERSION_STR));
addRow(4, tr("Sprache"), "C++17");
root->addWidget(body, 1);
auto *footer = new QWidget(dlg);
footer->setStyleSheet("background-color: #252526; border-top: 1px solid #3c3c3c;");
auto *footerLayout = new QHBoxLayout(footer);
footerLayout->setContentsMargins(16, 10, 16, 10);
auto *closeBtn = new QPushButton(tr("Schließen"), footer);
closeBtn->setDefault(true);
closeBtn->setMinimumWidth(90);
connect(closeBtn, &QPushButton::clicked, dlg, &QDialog::accept);
footerLayout->addStretch();
footerLayout->addWidget(closeBtn);
root->addWidget(footer);
dlg->exec();
dlg->deleteLater();
}