Warning, file /utilities/isoimagewriter/isoimagewriter/mainwindow.cpp was not indexed or was modified since last indexation (in which case cross-reference links may be missing, inaccurate or erroneous).

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