File indexing completed on 2024-04-21 05:48:33

0001 /*
0002     SPDX-FileCopyrightText: 2019 Farid Boudedja <farid.boudedja@gmail.com>, 2023 Jonathan Esk-Riddell <jr@jriddell.org>
0003     SPDX-License-Identifier: GPL-3.0-or-later
0004 */
0005 
0006 #include "mainwindow.h"
0007 #include "mainapplication.h"
0008 #include "common.h"
0009 #include "fetchisojob.h"
0010 #include "imagewriter.h"
0011 #include "isoverifier.h"
0012 #include "isoimagewriter_debug.h"
0013 
0014 #include <QLabel>
0015 #include <QTimer>
0016 #include <QAction>
0017 #include <QThread>
0018 #include <QMimeData>
0019 #include <QDropEvent>
0020 #include <QVBoxLayout>
0021 #include <QHBoxLayout>
0022 #include <QFileDialog>
0023 #include <QMessageBox>
0024 #include <QInputDialog>
0025 #include <QStandardPaths>
0026 #include <KFormat>
0027 #include <KIconLoader>
0028 #include <KPixmapSequence>
0029 #include <KLocalizedString>
0030 #include "isolineedit.h"
0031 
0032 #include <KPixmapSequenceLoader>
0033 
0034 MainWindow::MainWindow(QWidget *parent)
0035     : QMainWindow(parent),
0036       m_lastOpenedDir(),
0037       m_isWriting(false),
0038       m_enumFlashDevicesWaiting(false),
0039       m_externalProgressBar(this)
0040 {
0041     setupUi();
0042     setAcceptDrops(true);
0043 
0044     // Set initial directory
0045     m_lastOpenedDir = mApp->getInitialDir();
0046     // Get path to ISO image from command line args (if supplied)
0047     QUrl isoImagePath = mApp->getInitialImage();
0048     if (isoImagePath.isLocalFile()) {
0049         const QString path = isoImagePath.toLocalFile();
0050         m_lastOpenedDir = path.left(path.lastIndexOf('/'));
0051         preprocessIsoImage(path);
0052     } else {
0053         m_isoImageLineEdit->setText(isoImagePath.toString());
0054     }
0055 
0056     // Load the list of USB flash devices
0057     QTimer::singleShot(0, this, &MainWindow::enumFlashDevices);
0058 }
0059 
0060 void MainWindow::scheduleEnumFlashDevices()
0061 {
0062     if (m_isWriting)
0063         m_enumFlashDevicesWaiting = true;
0064     else
0065         enumFlashDevices();
0066 }
0067 
0068 void MainWindow::showInputDialog(const QString &title, const QString &body)
0069 {
0070     bool ok;
0071     QString text = QInputDialog::getText(this, title, body, QLineEdit::Normal, "", &ok);
0072 
0073     emit inputTextReady(ok, text);
0074 }
0075 
0076 void MainWindow::setupUi()
0077 {
0078     // Logo
0079     QLabel *logoLabel = new QLabel;
0080     logoLabel->setPixmap(KIconLoader::global()->loadIcon("org.kde.isoimagewriter", KIconLoader::Desktop));
0081     logoLabel->setSizePolicy(QSizePolicy::Fixed, QSizePolicy::Fixed);
0082 
0083     QLabel *titleLabel = new QLabel;
0084     titleLabel->setTextFormat(Qt::RichText);
0085     titleLabel->setText(QStringLiteral("<h2 style='margin-bottom: 0;'>%1</h2>%2").arg(i18n("KDE ISO Image Writer")).arg(i18n("A quick and simple way to create a bootable USB drive.")));
0086 
0087     QHBoxLayout *headerHBoxLayout = new QHBoxLayout;
0088     headerHBoxLayout->addWidget(logoLabel);
0089     headerHBoxLayout->addWidget(titleLabel);
0090 
0091     m_centralStackedWidget = new QStackedWidget;
0092     m_centralStackedWidget->addWidget(createFormWidget());
0093     m_centralStackedWidget->addWidget(createConfirmWidget());
0094     m_centralStackedWidget->addWidget(createProgressWidget());
0095     m_centralStackedWidget->addWidget(createSuccessWidget());
0096 
0097     QVBoxLayout *mainVBoxLayout = new QVBoxLayout;
0098     mainVBoxLayout->addLayout(headerHBoxLayout);
0099     mainVBoxLayout->addSpacing(15);
0100     mainVBoxLayout->addWidget(m_centralStackedWidget);
0101 
0102     QWidget *centralWidget = new QWidget;
0103     centralWidget->setLayout(mainVBoxLayout);
0104 
0105     setCentralWidget(centralWidget);
0106 }
0107 
0108 QWidget* MainWindow::createFormWidget()
0109 {
0110     // Form
0111     m_isoImageLineEdit = new IsoLineEdit;
0112     m_isoImageLineEdit->setReadOnly(true);
0113     m_isoImageLineEdit->setPlaceholderText(i18n("Path to ISO image..."));
0114     connect(m_isoImageLineEdit, &IsoLineEdit::clicked, this, &MainWindow::openIsoImage);
0115 
0116     m_isoImageSizeLabel = new QLabel;
0117 
0118     QAction *openIsoImageAction = m_isoImageLineEdit->addAction(
0119         QIcon::fromTheme("folder-open"), QLineEdit::TrailingPosition);
0120     connect(openIsoImageAction, &QAction::triggered, this, &MainWindow::openIsoImage);
0121 
0122     m_usbDriveComboBox = new QComboBox;
0123 
0124     m_createButton = new QPushButton(i18n("Create"));
0125     connect(m_createButton, &QPushButton::clicked, this, &MainWindow::showConfirmMessage);
0126 
0127     m_busyLabel = new QLabel;
0128     m_busyWidget = new QWidget;
0129     m_busySpinner = new KPixmapSequenceOverlayPainter(this);
0130     m_busySpinner->setAlignment(Qt::AlignHCenter | Qt::AlignVCenter);
0131     m_busySpinner->setWidget(m_busyWidget);
0132     m_busyWidget->setFixedSize(24, 24);
0133 
0134     QHBoxLayout *footerBoxLayout = new QHBoxLayout;
0135     footerBoxLayout->addWidget(m_busyWidget);
0136     footerBoxLayout->addWidget(m_busyLabel);
0137     footerBoxLayout->addWidget(m_createButton, 0, Qt::AlignRight);
0138 
0139     QHBoxLayout *isoImageLayout = new QHBoxLayout;
0140     isoImageLayout->addWidget(m_isoImageLineEdit);
0141     isoImageLayout->addWidget(m_isoImageSizeLabel);
0142 
0143     QVBoxLayout *mainVBoxLayout = new QVBoxLayout;
0144     mainVBoxLayout->addWidget(new QLabel(i18n("Write this ISO image:")));
0145     mainVBoxLayout->addLayout(isoImageLayout);
0146     mainVBoxLayout->addSpacing(5);
0147     mainVBoxLayout->addWidget(new QLabel(i18n("To this USB drive:")));
0148     mainVBoxLayout->addWidget(m_usbDriveComboBox);
0149     mainVBoxLayout->addSpacing(15);
0150     mainVBoxLayout->addStretch();
0151     mainVBoxLayout->addLayout(footerBoxLayout);
0152 
0153     QWidget *formWidget = new QWidget;
0154     formWidget->setLayout(mainVBoxLayout);
0155 
0156     return formWidget;
0157 }
0158 
0159 QWidget* MainWindow::createConfirmWidget()
0160 {
0161     QLabel *iconLabel = new QLabel;
0162     iconLabel->setPixmap(QIcon::fromTheme("dialog-warning").pixmap(QSize(64, 64)));
0163     iconLabel->setSizePolicy(QSizePolicy::Fixed, QSizePolicy::Fixed);
0164 
0165     static const QString overwriteMessage = i18n("Everything on the USB drive will "
0166                                            "be overwritten."
0167                                            "\n\nDo you want to continue?");
0168     QLabel *messageLabel = new QLabel(overwriteMessage);
0169     connect(this, &MainWindow::downloadProgressChanged, messageLabel, [messageLabel, iconLabel, this] {
0170         iconLabel->setPixmap(QIcon::fromTheme("download").pixmap(QSize(64, 64)));
0171         messageLabel->setText(i18n("Downloading %1...", m_fetchIso->fetchUrl().toDisplayString()));
0172     });
0173     connect(this, &MainWindow::verificationResult, messageLabel, [messageLabel, iconLabel] {
0174         messageLabel->setText(overwriteMessage);
0175         iconLabel->setPixmap(QIcon::fromTheme("dialog-warning").pixmap(QSize(64, 64)));
0176     });
0177 
0178     QHBoxLayout *messageHBoxLayout = new QHBoxLayout;
0179     messageHBoxLayout->addWidget(iconLabel, 0, Qt::AlignTop);
0180     messageHBoxLayout->addWidget(messageLabel, 0, Qt::AlignTop);
0181 
0182     QProgressBar *downloadProgressBar = new QProgressBar();
0183     downloadProgressBar->setVisible(false);
0184     downloadProgressBar->setFormat(i18nc("Progress percent value", "%p%"));
0185     downloadProgressBar->setMinimum(0);
0186     downloadProgressBar->setMaximum(100);
0187     connect(this, &MainWindow::downloadProgressChanged, downloadProgressBar, &QProgressBar::show);
0188     connect(this, &MainWindow::downloadProgressChanged, downloadProgressBar, &QProgressBar::setValue);
0189 
0190     QPushButton *abortButton = new QPushButton(i18n("Abort"));
0191     connect(abortButton, &QPushButton::clicked, this, &MainWindow::hideWritingProgress);
0192 
0193     QPushButton *continueButton = new QPushButton(i18n("Continue"));
0194     connect(continueButton, &QPushButton::clicked, this, &MainWindow::writeIsoImage);
0195     connect(this, &MainWindow::downloadProgressChanged, continueButton, [continueButton] { continueButton->setEnabled(false); });
0196     connect(this, &MainWindow::verificationResult, continueButton, [continueButton] { continueButton->setEnabled(true); });
0197 
0198     QHBoxLayout *buttonsHBoxLayout = new QHBoxLayout;
0199     buttonsHBoxLayout->addWidget(abortButton, 0, Qt::AlignLeft);
0200     buttonsHBoxLayout->addWidget(continueButton, 0, Qt::AlignRight);
0201 
0202     QVBoxLayout *mainVBoxLayout = new QVBoxLayout;
0203     mainVBoxLayout->addLayout(messageHBoxLayout);
0204     mainVBoxLayout->addWidget(downloadProgressBar);
0205     mainVBoxLayout->addLayout(buttonsHBoxLayout);
0206 
0207     QWidget *confirmWidget = new QWidget;
0208     confirmWidget->setLayout(mainVBoxLayout);
0209 
0210     return confirmWidget;
0211 }
0212 
0213 QWidget* MainWindow::createProgressWidget()
0214 {
0215     QLabel *messageLabel = new QLabel(i18n("Your USB drive is being created.\n\n"
0216                                            "This may take some time depending "
0217                                            "on the size of the ISO image file "
0218                                            "and the transfer speed."));
0219     messageLabel->setWordWrap(true);
0220 
0221     m_progressBar = new QProgressBar;
0222     m_progressBar->setFormat(i18nc("Progress percent value", "%p%"));
0223     m_cancelButton = new QPushButton(i18n("Cancel"));
0224 
0225     QVBoxLayout *mainVBoxLayout = new QVBoxLayout;
0226     mainVBoxLayout->addWidget(messageLabel, 0, Qt::AlignTop | Qt::AlignHCenter);
0227     mainVBoxLayout->addWidget(m_progressBar);
0228     mainVBoxLayout->addSpacing(15);
0229     mainVBoxLayout->addWidget(m_cancelButton, 0, Qt::AlignLeft);
0230 
0231     QWidget *progressWidget = new QWidget;
0232     progressWidget->setLayout(mainVBoxLayout);
0233 
0234     return progressWidget;
0235 }
0236 
0237 QWidget* MainWindow::createSuccessWidget()
0238 {
0239     QLabel *messageLabel = new QLabel(i18n("Your live USB flash drive is now "
0240                                            "complete and ready to use!"));
0241     messageLabel->setWordWrap(true);
0242 
0243     QLabel *successIconLabel = new QLabel();
0244     successIconLabel->setPixmap(QIcon::fromTheme("emblem-success").pixmap(QSize(64, 64)));
0245 
0246     QPushButton *backButton = new QPushButton(i18n("Back"));
0247     connect(backButton, &QPushButton::clicked, this, &MainWindow::hideWritingProgress);
0248 
0249     QPushButton *closeButton = new QPushButton(i18n("Close"));
0250     connect(closeButton, &QPushButton::clicked, this, &MainWindow::close);
0251 
0252     QHBoxLayout *buttonsHBoxLayout = new QHBoxLayout;
0253     buttonsHBoxLayout->addWidget(backButton, 0, Qt::AlignLeft);
0254     buttonsHBoxLayout->addWidget(closeButton, 0, Qt::AlignRight);
0255 
0256     QVBoxLayout *mainVBoxLayout = new QVBoxLayout;
0257     mainVBoxLayout->addWidget(messageLabel, 0, Qt::AlignCenter);
0258     mainVBoxLayout->addWidget(successIconLabel, 0, Qt::AlignHCenter);
0259     mainVBoxLayout->addSpacing(15);
0260     mainVBoxLayout->addLayout(buttonsHBoxLayout);
0261 
0262     QWidget *successWidget = new QWidget;
0263     successWidget->setLayout(mainVBoxLayout);
0264 
0265     return successWidget;
0266 }
0267 
0268 void MainWindow::preprocessIsoImage(const QString& isoImagePath)
0269 {
0270     QFile file(isoImagePath);
0271     if (!file.open(QIODevice::ReadOnly))
0272     {
0273         QMessageBox::critical(this, "Error",
0274                               i18n("Failed to open the image file:")
0275                               + "\n" + QDir::toNativeSeparators(isoImagePath)
0276                               + "\n" + file.errorString());
0277         return;
0278     }
0279 
0280     m_isoImageSize = file.size();
0281     m_isoImagePath = isoImagePath;
0282     m_isoImageLineEdit->setText(QDir::toNativeSeparators(m_isoImagePath));
0283     m_isoImageSizeLabel->setText(KFormat().formatByteSize(m_isoImageSize));
0284 
0285     file.close();
0286 
0287 #ifdef _USE_GPG
0288     // Verify ISO image
0289     m_busyLabel->setText(i18n("Verifying ISO image"));
0290     m_busyWidget->show();
0291     m_busySpinner->setSequence(KPixmapSequenceLoader::load(
0292         "process-working", KIconLoader::SizeSmallMedium));
0293     m_busySpinner->start();
0294 
0295     IsoVerifier *isoVerifier = new IsoVerifier(m_isoImagePath);
0296     QThread *verifierThread = new QThread(this);
0297 
0298     connect(verifierThread, &QThread::started, isoVerifier, &IsoVerifier::verifyIso);
0299     connect(verifierThread, &QThread::finished, verifierThread, &QThread::deleteLater);
0300 
0301     connect(isoVerifier, &IsoVerifier::finished, verifierThread, &QThread::quit);
0302     connect(isoVerifier, &IsoVerifier::finished, isoVerifier, &IsoVerifier::deleteLater);
0303     connect(isoVerifier, &IsoVerifier::finished, this, &MainWindow::showIsoVerificationResult);
0304     connect(isoVerifier, &IsoVerifier::inputRequested, this, &MainWindow::showInputDialog);
0305 
0306     connect(this, &MainWindow::inputTextReady, isoVerifier, &IsoVerifier::verifyWithInputText);
0307 
0308     isoVerifier->moveToThread(verifierThread);
0309     verifierThread->start();
0310 #endif
0311 
0312     // Enable the Write button (if there are USB flash disks present)
0313     m_createButton->setEnabled(m_usbDriveComboBox->count() > 0);
0314 }
0315 
0316 void MainWindow::cleanUp()
0317 {
0318     // Delete all the allocated UsbDevice objects attached to the combobox
0319     for (int i = 0; i < m_usbDriveComboBox->count(); ++i)
0320     {
0321         delete m_usbDriveComboBox->itemData(i).value<UsbDevice*>();
0322     }
0323 }
0324 
0325 void MainWindow::enumFlashDevices()
0326 {
0327     m_enumFlashDevicesWaiting = false;
0328 
0329     // Remember the currently selected device
0330     QString selectedDevice = "";
0331     int idx = m_usbDriveComboBox->currentIndex();
0332     if (idx >= 0)
0333     {
0334         UsbDevice* dev = m_usbDriveComboBox->itemData(idx).value<UsbDevice*>();
0335         selectedDevice = dev->m_PhysicalDevice;
0336     }
0337 
0338     // Remove the existing entries
0339     cleanUp();
0340     m_usbDriveComboBox->clear();
0341 
0342     // Disable the combobox
0343     m_usbDriveComboBox->setEnabled(false);
0344 
0345     // Add the USB flash devices to the combobox
0346     platformEnumFlashDevices(addFlashDeviceCallback, m_usbDriveComboBox);
0347 
0348     // Restore the previously selected device (if present)
0349     if (!selectedDevice.isEmpty())
0350         for (int i = 0; i < m_usbDriveComboBox->count(); ++i)
0351         {
0352             UsbDevice* dev = m_usbDriveComboBox->itemData(i).value<UsbDevice*>();
0353             if (dev->m_PhysicalDevice == selectedDevice)
0354             {
0355                 m_usbDriveComboBox->setCurrentIndex(i);
0356                 break;
0357             }
0358         }
0359 
0360     // Update the Write button enabled/disabled state
0361     m_createButton->setEnabled(m_usbDriveComboBox->count() > 0
0362                                && m_isoImagePath != "");
0363 
0364     // Enable/disable the usb drive combobox
0365     if (m_usbDriveComboBox->count() < 1) {
0366         m_usbDriveComboBox->setEnabled(false);
0367         m_usbDriveComboBox->setEditable(true);
0368         m_usbDriveComboBox->lineEdit()
0369             ->setPlaceholderText(i18n("Please plug in a USB drive"));
0370     } else {
0371         m_usbDriveComboBox->setEnabled(true);
0372         m_usbDriveComboBox->setEditable(false);
0373     }
0374 }
0375 
0376 void MainWindow::writeToDevice(bool zeroing)
0377 {
0378     UsbDevice* selectedDevice = m_usbDriveComboBox->itemData(
0379         m_usbDriveComboBox->currentIndex()).value<UsbDevice*>();
0380 
0381     ImageWriter* writer = new ImageWriter(zeroing ? "" : m_isoImagePath, selectedDevice);
0382     QThread *writerThread = new QThread(this);
0383 
0384     // Connect start and end signals
0385     connect(writerThread, &QThread::started, writer, &ImageWriter::writeImage);
0386     // When writer finishes its job, quit the thread
0387     connect(writer, &ImageWriter::finished, writerThread, &QThread::quit);
0388     // Guarantee deleting the objects after completion
0389     connect(writer, &ImageWriter::finished, writer, &ImageWriter::deleteLater);
0390     connect(writerThread, &QThread::finished, writerThread, &QThread::deleteLater);
0391     // If the Cancel button is pressed, inform the writer to stop the operation
0392     // Using DirectConnection because the thread does not read its own event queue until completion
0393     connect(m_cancelButton, &QPushButton::clicked, writer, &ImageWriter::cancelWriting, Qt::DirectConnection);
0394     // Each time a block is written, update the progress bar
0395     connect(writer, &ImageWriter::progressChanged, this, &MainWindow::updateProgressBar);
0396     // Show the message about successful completion on success
0397     connect(writer, &ImageWriter::success, this, &MainWindow::showSuccessMessage);
0398     // Show error message if error is sent by the worker
0399     connect(writer, &ImageWriter::error, this, &MainWindow::showErrorMessage);
0400     // Silently return back to normal dialog form if the operation was cancelled
0401     connect(writer, &ImageWriter::cancelled, this, &MainWindow::hideWritingProgress);
0402 
0403     // Now start the writer thread
0404     writer->moveToThread(writerThread);
0405     writerThread->start();
0406 
0407     showWritingProgress();
0408 }
0409 
0410 void MainWindow::dragEnterEvent(QDragEnterEvent* event)
0411 {
0412     // Accept only files with ANSI or Unicode paths (Windows) and URIs (Linux)
0413     if (event->mimeData()->hasFormat("application/x-qt-windows-mime;value=\"FileName\"") ||
0414         event->mimeData()->hasFormat("application/x-qt-windows-mime;value=\"FileNameW\"") ||
0415         event->mimeData()->hasFormat("text/uri-list"))
0416         event->accept();
0417 }
0418 
0419 void MainWindow::dropEvent(QDropEvent* event)
0420 {
0421     QString newImageFile = "";
0422     QByteArray droppedFileName;
0423 
0424     // First, try to use the Unicode file name
0425     droppedFileName = event->mimeData()->data("application/x-qt-windows-mime;value=\"FileNameW\"");
0426     if (!droppedFileName.isEmpty()) {
0427         newImageFile = QString::fromWCharArray(reinterpret_cast<const wchar_t*>(droppedFileName.constData()));
0428     } else {
0429         // If failed, use the ANSI name with the local codepage
0430         droppedFileName = event->mimeData()->data("application/x-qt-windows-mime;value=\"FileName\"");
0431         if (!droppedFileName.isEmpty()) {
0432             newImageFile = QString::fromLocal8Bit(droppedFileName.constData());
0433         } else {
0434             // And, finally, try the URI
0435             droppedFileName = event->mimeData()->data("text/uri-list");
0436             if (!droppedFileName.isEmpty()) {
0437                 // If several files are dropped they are separated by newlines,
0438                 // take the first file
0439                 int newLineIndexLF = droppedFileName.indexOf('\n');
0440                 int newLineIndex = droppedFileName.indexOf("\r\n");
0441                 // Make sure both CRLF and LF are accepted
0442                 if ((newLineIndexLF != -1) && (newLineIndexLF < newLineIndex))
0443                     newLineIndex = newLineIndexLF;
0444                 if (newLineIndex != -1)
0445                     droppedFileName.truncate(newLineIndex);
0446                 // Decode the file path from percent-encoding
0447                 QUrl url = QUrl::fromEncoded(droppedFileName);
0448                 if (url.isLocalFile())
0449                     newImageFile = url.toLocalFile();
0450             }
0451         }
0452     }
0453 }
0454 
0455 void MainWindow::closeEvent(QCloseEvent* event)
0456 {
0457     if (m_isWriting)
0458     {
0459         const int answer = QMessageBox::question(
0460             this,
0461             i18n("Cancel?"),
0462             i18n("Writing is in progress, abort it?"));
0463 
0464         if (answer == QMessageBox::No)
0465             event->ignore();
0466     }
0467 }
0468 
0469 void MainWindow::addFlashDeviceCallback(void* cbParam, UsbDevice* device)
0470 {
0471     auto usbDriveComboBox = (QComboBox*)cbParam;
0472     usbDriveComboBox->addItem(device->formatDisplayName(),
0473                               QVariant::fromValue(device));
0474 }
0475 
0476 void MainWindow::openIsoImage()
0477 {
0478     const QString filter = i18n("Disk Images (%1)", QString("*.iso *.bin *.img *.iso.gz *.iso.xz *.img.zstd *.img.gz *.img.zx *.img.zstd"))
0479         + ";;" + i18n("All Files (%1)", QString("*"));
0480     QUrl isoImageUrl = QFileDialog::getOpenFileUrl(this, i18n("Select image to flash"), QUrl::fromLocalFile(m_lastOpenedDir),
0481                                                         filter, nullptr,
0482                                                         QFileDialog::ReadOnly);
0483     openUrl(isoImageUrl);
0484 }
0485 
0486 void MainWindow::openUrl(const QUrl& url)
0487 {
0488     if (url.isEmpty()) {
0489         return;
0490     }
0491 
0492     if (url.isLocalFile()) {
0493         const QString path = url.toLocalFile();
0494         m_lastOpenedDir = path.left(path.lastIndexOf('/'));
0495         preprocessIsoImage(path);
0496         return;
0497     } else {
0498         m_isoImageLineEdit->setText(url.toString());
0499     }
0500 
0501     delete m_fetchIso;
0502     m_fetchIso = new FetchIsoJob(this);
0503     connect(m_fetchIso, &FetchIsoJob::downloadProgressChanged, this, &MainWindow::downloadProgressChanged);
0504     connect(m_fetchIso, &FetchIsoJob::failed, this, &MainWindow::hideWritingProgress);
0505     connect(m_fetchIso, &FetchIsoJob::finished, this, [this] (const QString &file) {
0506         m_isoImagePath = file;
0507         m_busySpinner->stop();
0508         preprocessIsoImage(file);
0509     });
0510     m_busyLabel->setText(i18n("Downloading ISO image"));
0511     m_busyWidget->show();
0512     m_busySpinner->setSequence(KPixmapSequenceLoader::load(
0513         "process-working", KIconLoader::SizeSmallMedium));
0514     m_busySpinner->start();
0515     m_fetchIso->fetch(url);
0516 }
0517 
0518 void MainWindow::writeIsoImage()
0519 {
0520     if (m_usbDriveComboBox->count() == 0 || m_isoImagePath == "")
0521         return;
0522 
0523     UsbDevice* selectedDevice = m_usbDriveComboBox->itemData(
0524         m_usbDriveComboBox->currentIndex()).value<UsbDevice*>();
0525 
0526     if (selectedDevice->m_Size == 0) {
0527         int warningReturn = QMessageBox::warning(
0528             this,
0529             i18n("Unknown Disk Size"),
0530             i18n("The selected disk is of unknown size, please check the image will fit before writing.\n\n"
0531                  "Image size: %1 (%2 b)",
0532                  KFormat().formatByteSize(m_isoImageSize),
0533                  m_isoImageSize),
0534             QMessageBox::Ok | QMessageBox::Cancel);
0535         if (warningReturn != QMessageBox::Ok) {
0536             return;
0537         }
0538     } else if (m_isoImageSize > selectedDevice->m_Size) {
0539         QMessageBox::critical(
0540             this,
0541             i18n("Error"),
0542             i18n("The image is larger than your selected device!\n\n"
0543                  "Image size: %1 (%2 b)\n"
0544                  "Disk size: %3 (%4 b)",
0545                  KFormat().formatByteSize(m_isoImageSize),
0546                  m_isoImageSize,
0547                  KFormat().formatByteSize(selectedDevice->m_Size),
0548                  selectedDevice->m_Size),
0549             QMessageBox::Ok);
0550         return;
0551     }
0552 
0553     writeToDevice(false);
0554 }
0555 
0556 void MainWindow::updateProgressBar(int percent)
0557 {
0558     m_progressBar->setValue(percent);
0559     m_externalProgressBar.SetProgressValue(percent);
0560 }
0561 
0562 void MainWindow::showWritingProgress()
0563 {
0564     m_isWriting = true;
0565 
0566     // Do not accept dropped files while writing
0567     setAcceptDrops(false);
0568 
0569     // Display and customize the progress bar part
0570     m_progressBar->setMinimum(0);
0571     m_progressBar->setMaximum(100);
0572     m_progressBar->setValue(0);
0573 
0574     // Expose the progress bar state to the OS
0575     m_externalProgressBar.InitProgressBar(100);
0576 
0577     m_centralStackedWidget->setCurrentIndex(2);
0578 }
0579 
0580 void MainWindow::hideWritingProgress()
0581 {
0582     m_isWriting = false;
0583 
0584     // Enable drag & drop
0585     setAcceptDrops(true);
0586 
0587     // Send a signal that progressbar is no longer present
0588     m_externalProgressBar.DestroyProgressBar();
0589 
0590     m_centralStackedWidget->setCurrentIndex(0);
0591 
0592     // If device list changed during writing update it now
0593     if (m_enumFlashDevicesWaiting)
0594         enumFlashDevices();
0595 }
0596 
0597 void MainWindow::showErrorMessage(const QString &message)
0598 {
0599     m_externalProgressBar.ProgressSetError();
0600 
0601     QMessageBox::critical(this, i18n("Error"), message);
0602 
0603     hideWritingProgress();
0604 }
0605 
0606 void MainWindow::showSuccessMessage()
0607 {
0608     m_isWriting = false;
0609 
0610     // Do not accept dropped files
0611     setAcceptDrops(false);
0612 
0613     m_centralStackedWidget->setCurrentIndex(3);
0614 }
0615 
0616 void MainWindow::showConfirmMessage()
0617 {
0618     openUrl(QUrl::fromUserInput(m_isoImageLineEdit->text(), {}, QUrl::AssumeLocalFile));
0619 
0620     // Do not accept dropped files
0621     setAcceptDrops(false);
0622 
0623     m_centralStackedWidget->setCurrentIndex(1);
0624 }
0625 
0626 void MainWindow::showIsoVerificationResult(IsoVerifier::VerifyResult verify, const QString &error)
0627 {
0628     if (verify == IsoVerifier::VerifyResult::Successful) {
0629         m_busyLabel->setText(i18n("The ISO image is valid"));
0630         m_busySpinner->setSequence(KPixmapSequenceLoader::load(
0631             "checkmark", KIconLoader::SizeSmallMedium));
0632     } else {
0633         m_busySpinner->setSequence(
0634             KPixmapSequenceLoader::load("error", KIconLoader::SizeSmallMedium));
0635         if (verify == IsoVerifier::VerifyResult::KeyNotFound) {
0636             m_busyLabel->setText(i18n("Could not find the key to verify the ISO image"));
0637         } else if (verify == IsoVerifier::VerifyResult::Failed) {
0638             m_busyLabel->setText(i18n("Could not verify ISO image"));
0639             QMessageBox::warning(this, i18n("ISO Verification failed"), error);
0640         }
0641     }
0642     Q_EMIT verificationResult(verify == IsoVerifier::VerifyResult::Successful);
0643 }
0644 
0645 #include "moc_mainwindow.cpp"