diff --git a/isoimagewriter/imagewriter.cpp b/isoimagewriter/imagewriter.cpp index f8b4e1d..b44ab3c 100644 --- a/isoimagewriter/imagewriter.cpp +++ b/isoimagewriter/imagewriter.cpp @@ -1,263 +1,269 @@ /* * Copyright 2016 ROSA * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License as * published by the Free Software Foundation; either version 3 of * the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ //////////////////////////////////////////////////////////////////////////////// // Implementation of ImageWriter #include "imagewriter.h" #include #if defined(Q_OS_LINUX) #include #endif #include #include "common.h" #include "physicaldevice.h" ImageWriter::ImageWriter(const QString& ImageFile, UsbDevice* Device, QObject *parent) : QObject(parent), m_Device(Device), m_ImageFile(ImageFile), m_CancelWriting(false) { } // The main method that writes the image void ImageWriter::writeImage() { qDebug() << "XX writeImage()"; const qint64 TRANSFER_BLOCK_SIZE = 1024 * 1024; void* buffer = NULL; bool isError = false; bool cancelRequested = false; bool zeroing = (m_ImageFile == ""); // Using try-catch for processing errors // Invalid values are used for indication non-initialized objects; // after the try-catch block all the initialized objects are freed try { #if defined(Q_OS_WIN32) // Using VirtualAlloc so that the buffer was properly aligned (required for // direct access to devices and for unbuffered reading/writing) buffer = VirtualAlloc(NULL, TRANSFER_BLOCK_SIZE, MEM_COMMIT | MEM_RESERVE, PAGE_READWRITE); if (buffer == NULL) throw formatErrorMessageFromCode(i18n("Failed to allocate memory for buffer:")); #elif defined(Q_OS_LINUX) || defined(Q_OS_MAC) buffer = malloc(TRANSFER_BLOCK_SIZE); if (buffer == NULL) throw i18n("Failed to allocate memory for buffer."); #endif QFile imageFile; if (zeroing) { // Prepare zero-filled buffer memset(buffer, 0, TRANSFER_BLOCK_SIZE); } else { // Open the source image file for reading imageFile.setFileName(m_ImageFile); if (!imageFile.open(QIODevice::ReadOnly)) throw i18n("Failed to open the image file: %1", imageFile.errorString()); } // Unmount volumes that belong to the selected target device // TODO: Check first if they are used and show warning // (problem: have to show request in the GUI thread and return reply back here) QStringList errMessages; #if defined(Q_OS_WIN32) for (int i = 0; i < m_Device->m_Volumes.size(); ++i) { DWORD bret; HANDLE volume = CreateFile( reinterpret_cast(("\\\\.\\" + m_Device->m_Volumes[i]).utf16()), GENERIC_READ | GENERIC_WRITE, FILE_SHARE_READ | FILE_SHARE_WRITE, NULL, OPEN_EXISTING, 0, NULL ); if (volume == INVALID_HANDLE_VALUE) { errMessages << formatErrorMessageFromCode(i18n("Failed to open the drive %1", m_Device->m_Volumes[i])); continue; } // Trying to lock the volume but ignore if we failed (such call seems to be required for // dismounting the volume on WinXP) DeviceIoControl(volume, FSCTL_LOCK_VOLUME, NULL, 0, NULL, 0, &bret, NULL); if (!DeviceIoControl(volume, FSCTL_DISMOUNT_VOLUME, NULL, 0, NULL, 0, &bret, NULL)) errMessages << formatErrorMessageFromCode(i18n("Failed to unmount the drive %1", m_Device->m_Volumes[i])); CloseHandle(volume); volume = INVALID_HANDLE_VALUE; } #elif defined(Q_OS_MAC) struct statfs* mntEntries = NULL; int mntEntriesNum = getmntinfo(&mntEntries, MNT_WAIT); for (int i = 0; i < mntEntriesNum; ++i) { for (int j = 0; j < m_Device->m_Volumes.size(); ++j) { // Check that the mount point is either our target device itself or a partition on it if ((mntEntries[i].f_mntfromname == m_Device->m_Volumes[j]) || QString(mntEntries[i].f_mntfromname).startsWith(m_Device->m_Volumes[j] + 's')) { // Mount point is the selected device or one of its partitions - try to unmount it if (unmount(mntEntries[i].f_mntonname, MNT_FORCE) != 0) errMessages << i18n("Failed to unmount the volume %1\n%2", m_Device->m_Volumes[i], strerror(errno)); } } } #endif if (errMessages.size() > 0) throw errMessages.join("\n\n"); // Open the target USB device for writing and lock it PhysicalDevice deviceFile(m_Device->m_PhysicalDevice); if (!deviceFile.open()) throw i18n("Failed to open the target device:\n%1", deviceFile.errorString()); qint64 readBytes; qint64 writtenBytes; // Start reading/writing cycle for (;;) { qDebug() << "For Loop3"; #if defined(Q_OS_LINUX) if (KAuth::HelperSupport::isStopped()) { qDebug() << "isStopped"; } else { qDebug() << "not isStopped"; } #endif if (zeroing) { readBytes = TRANSFER_BLOCK_SIZE; } else { if ((readBytes = imageFile.read(static_cast(buffer), TRANSFER_BLOCK_SIZE)) <= 0) break; } // Align the number of bytes to the sector size readBytes = alignNumber(readBytes, (qint64)m_Device->m_SectorSize); writtenBytes = deviceFile.write(static_cast(buffer), readBytes); if (writtenBytes < 0) throw i18n("Failed to write to the device:\n%1", deviceFile.errorString()); if (writtenBytes != readBytes) throw i18n("The last block was not fully written (%1 of %2 bytes)!\nAborting.", writtenBytes, readBytes); #if defined(Q_OS_LINUX) || defined(Q_OS_MAC) // In Linux/MacOS the USB device is opened with buffering. Using forced sync to validate progress bar. // For unknown reason, deviceFile.flush() does not work as intended here. fsync(deviceFile.handle()); #endif // Inform the GUI thread that next block was written // TODO: Make sure that when TRANSFER_BLOCK_SIZE is not a multiple of DEFAULT_UNIT // this still works or at least fails compilation emit blockWritten(TRANSFER_BLOCK_SIZE / DEFAULT_UNIT); #if defined(Q_OS_LINUX) KAuth::HelperSupport::progressStep(TRANSFER_BLOCK_SIZE / DEFAULT_UNIT); // FIXME why doesn't this work? QVariantMap progressArgs; progressArgs[QStringLiteral("progress")] = TRANSFER_BLOCK_SIZE / DEFAULT_UNIT; KAuth::HelperSupport::progressStep(progressArgs); // used because above doesn't work #endif // Check for the cancel request (using temporary variable to avoid multiple unlock calls in the code) m_Mutex.lock(); #if defined(Q_OS_LINUX) cancelRequested = KAuth::HelperSupport::isStopped(); #else cancelRequested = m_CancelWriting; #endif m_Mutex.unlock(); if (cancelRequested) { +#if defined(Q_OS_LINUX) + QVariantMap progressArgs; + progressArgs[QStringLiteral("cancel")] = true; + KAuth::HelperSupport::progressStep(progressArgs); +#endif + qDebug() << "cancelRequested"; // The cancel request was issued emit cancelled(); break; } if (zeroing) { // In zeroing mode only write 1 block - 1 MB is enough to clear both MBR and GPT break; } } if (!zeroing) { if (readBytes < 0) throw i18n("Failed to read the image file:\n%1", imageFile.errorString()); imageFile.close(); } deviceFile.close(); } catch (QString msg) { // Something went wrong :-( #if defined(Q_OS_LINUX) QVariantMap args; args[QStringLiteral("error")] = msg; KAuth::HelperSupport::progressStep(args); #endif emit error(msg); isError = true; } if (buffer != NULL) #if defined(Q_OS_WIN32) VirtualFree(buffer, TRANSFER_BLOCK_SIZE, MEM_DECOMMIT | MEM_RELEASE); #elif defined(Q_OS_LINUX) || defined(Q_OS_MAC) free(buffer); #endif // If no errors occurred and user did not stop the operation, it means everything went fine if (!isError && !cancelRequested) { QString message = i18n("The operation completed successfully.") + "

" + (zeroing ? i18n("Now you need to format your device.") : i18n("To be able to store data on this device again, please, use the button \"Wipe USB Disk\".")); #if defined(Q_OS_LINUX) QVariantMap args; args[QStringLiteral("success")] = message; KAuth::HelperSupport::progressStep(args); #endif emit success(message); } // In any case the operation is finished emit finished(); } // Implements reaction to the cancel request from user void ImageWriter::cancelWriting() { m_Mutex.lock(); m_CancelWriting = true; m_Mutex.unlock(); } diff --git a/isoimagewriter/mainwindow.cpp b/isoimagewriter/mainwindow.cpp index 76a2da2..f850bf2 100644 --- a/isoimagewriter/mainwindow.cpp +++ b/isoimagewriter/mainwindow.cpp @@ -1,656 +1,656 @@ /* * Copyright 2019 Farid Boudedja * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License as * published by the Free Software Foundation; either version 3 of * the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ #include "mainwindow.h" #include "mainapplication.h" #include "common.h" #include "imagewriter.h" #include "isoverifier.h" #include "isoimagewriter_debug.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include MainWindow::MainWindow(QWidget *parent) : QMainWindow(parent), m_lastOpenedDir(""), m_isWriting(false), m_enumFlashDevicesWaiting(false), m_externalProgressBar(this) { setupUi(); setAcceptDrops(true); // Set initial directory m_lastOpenedDir = mApp->getInitialDir(); // Get path to ISO image from command line args (if supplied) QString isoImagePath = mApp->getInitialImage(); if (!isoImagePath.isEmpty()) { if (isoImagePath.left(7) == "file://") isoImagePath = QUrl(isoImagePath).toLocalFile(); if (!isoImagePath.isEmpty()) { isoImagePath = QDir(isoImagePath).absolutePath(); // Update the default open dir m_lastOpenedDir = isoImagePath.left(isoImagePath.lastIndexOf('/')); preprocessIsoImage(isoImagePath); } } // Load the list of USB flash devices QTimer::singleShot(0, this, &MainWindow::enumFlashDevices); } void MainWindow::scheduleEnumFlashDevices() { if (m_isWriting) m_enumFlashDevicesWaiting = true; else enumFlashDevices(); } void MainWindow::showInputDialog(const QString &title, const QString &body) { bool ok; QString text = QInputDialog::getText(this, title, body, QLineEdit::Normal, "", &ok); emit inputTextReady(ok, text); } void MainWindow::setupUi() { // Logo QLabel *logoLabel = new QLabel; logoLabel->setPixmap(QIcon::fromTheme("drive-removable-media").pixmap(QSize(50, 50))); logoLabel->setSizePolicy(QSizePolicy::Fixed, QSizePolicy::Fixed); QLabel *titleLabel = new QLabel; titleLabel->setTextFormat(Qt::RichText); titleLabel->setText("

KDE ISO Image Writer

" "A quick and simple way to create a bootable USB drive."); QHBoxLayout *headerHBoxLayout = new QHBoxLayout; headerHBoxLayout->addWidget(logoLabel); headerHBoxLayout->addWidget(titleLabel); m_centralStackedWidget = new QStackedWidget; m_centralStackedWidget->addWidget(createFormWidget()); m_centralStackedWidget->addWidget(createConfirmWidget()); m_centralStackedWidget->addWidget(createProgressWidget()); m_centralStackedWidget->addWidget(createSuccessWidget()); QVBoxLayout *mainVBoxLayout = new QVBoxLayout; mainVBoxLayout->addLayout(headerHBoxLayout); mainVBoxLayout->addSpacing(15); mainVBoxLayout->addWidget(m_centralStackedWidget); QWidget *centralWidget = new QWidget; centralWidget->setLayout(mainVBoxLayout); setCentralWidget(centralWidget); } QWidget* MainWindow::createFormWidget() { // Form m_isoImageLineEdit = new QLineEdit; m_isoImageLineEdit->setReadOnly(true); m_isoImageLineEdit->setPlaceholderText(i18n("Path to ISO image...")); QAction *openIsoImageAction = m_isoImageLineEdit->addAction( QIcon::fromTheme("folder-open"), QLineEdit::TrailingPosition); connect(openIsoImageAction, &QAction::triggered, this, &MainWindow::openIsoImage); m_usbDriveComboBox = new QComboBox; m_createButton = new QPushButton(i18n("Create")); m_createButton->setEnabled(false); connect(m_createButton, &QPushButton::clicked, this, &MainWindow::showConfirmMessage); m_busyLabel = new QLabel; m_busyWidget = new QWidget; m_busySpinner = new KPixmapSequenceOverlayPainter(this); m_busySpinner->setAlignment(Qt::AlignHCenter | Qt::AlignVCenter); m_busySpinner->setWidget(m_busyWidget); m_busyWidget->setFixedSize(24, 24); QHBoxLayout *footerBoxLayout = new QHBoxLayout; footerBoxLayout->addWidget(m_busyWidget); footerBoxLayout->addWidget(m_busyLabel); footerBoxLayout->addWidget(m_createButton, 0, Qt::AlignRight); QVBoxLayout *mainVBoxLayout = new QVBoxLayout; mainVBoxLayout->addWidget(new QLabel(i18n("Write this ISO image:"))); mainVBoxLayout->addWidget(m_isoImageLineEdit); mainVBoxLayout->addSpacing(5); mainVBoxLayout->addWidget(new QLabel(i18n("To this USB drive:"))); mainVBoxLayout->addWidget(m_usbDriveComboBox); mainVBoxLayout->addSpacing(15); mainVBoxLayout->addStretch(); mainVBoxLayout->addLayout(footerBoxLayout); QWidget *formWidget = new QWidget; formWidget->setLayout(mainVBoxLayout); return formWidget; } QWidget* MainWindow::createConfirmWidget() { QLabel *iconLabel = new QLabel; iconLabel->setPixmap(QIcon::fromTheme("dialog-warning").pixmap(QSize(64, 64))); iconLabel->setSizePolicy(QSizePolicy::Fixed, QSizePolicy::Fixed); QLabel *messageLabel = new QLabel(i18n("Everything on the USB drive will " "be overwritten." "\n\nDo you want to continue?")); QHBoxLayout *messageHBoxLayout = new QHBoxLayout; messageHBoxLayout->addWidget(iconLabel, 0, Qt::AlignTop); messageHBoxLayout->addWidget(messageLabel, 0, Qt::AlignTop); QPushButton *abortButton = new QPushButton(i18n("Abort")); connect(abortButton, &QPushButton::clicked, this, &MainWindow::hideWritingProgress); QPushButton *continueButton = new QPushButton(i18n("Continue")); connect(continueButton, &QPushButton::clicked, this, &MainWindow::writeIsoImage); QHBoxLayout *buttonsHBoxLayout = new QHBoxLayout; buttonsHBoxLayout->addWidget(abortButton, 0, Qt::AlignLeft); buttonsHBoxLayout->addWidget(continueButton, 0, Qt::AlignRight); QVBoxLayout *mainVBoxLayout = new QVBoxLayout; mainVBoxLayout->addLayout(messageHBoxLayout); mainVBoxLayout->addLayout(buttonsHBoxLayout); QWidget *confirmWidget = new QWidget; confirmWidget->setLayout(mainVBoxLayout); return confirmWidget; } QWidget* MainWindow::createProgressWidget() { QLabel *messageLabel = new QLabel(i18n("Your USB drive is being created.\n\n" "This may take some time depending " "on the size of the ISO image file " "and the transfer speed.")); messageLabel->setWordWrap(true); m_progressBar = new QProgressBar; m_cancelButton = new QPushButton(i18n("Cancel")); QVBoxLayout *mainVBoxLayout = new QVBoxLayout; mainVBoxLayout->addWidget(messageLabel, 0, Qt::AlignTop | Qt::AlignHCenter); mainVBoxLayout->addWidget(m_progressBar); mainVBoxLayout->addSpacing(15); mainVBoxLayout->addWidget(m_cancelButton, 0, Qt::AlignLeft); QWidget *progressWidget = new QWidget; progressWidget->setLayout(mainVBoxLayout); return progressWidget; } QWidget* MainWindow::createSuccessWidget() { QLabel *messageLabel = new QLabel(i18n("Your live USB flash drive is now " "complete and ready to use!")); messageLabel->setWordWrap(true); QLabel *successIconLabel = new QLabel(); successIconLabel->setPixmap(QIcon::fromTheme("emblem-success").pixmap(QSize(64, 64))); QPushButton *backButton = new QPushButton(i18n("Back")); connect(backButton, &QPushButton::clicked, this, &MainWindow::hideWritingProgress); QPushButton *closeButton = new QPushButton(i18n("Close")); connect(closeButton, &QPushButton::clicked, this, &MainWindow::close); QHBoxLayout *buttonsHBoxLayout = new QHBoxLayout; buttonsHBoxLayout->addWidget(backButton, 0, Qt::AlignLeft); buttonsHBoxLayout->addWidget(closeButton, 0, Qt::AlignRight); QVBoxLayout *mainVBoxLayout = new QVBoxLayout; mainVBoxLayout->addWidget(messageLabel, 0, Qt::AlignCenter); mainVBoxLayout->addWidget(successIconLabel, 0, Qt::AlignHCenter); mainVBoxLayout->addSpacing(15); mainVBoxLayout->addLayout(buttonsHBoxLayout); QWidget *successWidget = new QWidget; successWidget->setLayout(mainVBoxLayout); return successWidget; } void MainWindow::preprocessIsoImage(const QString& isoImagePath) { QFile file(isoImagePath); if (!file.open(QIODevice::ReadOnly)) { QMessageBox::critical(this, "Error", i18n("Failed to open the image file:") + "\n" + QDir::toNativeSeparators(isoImagePath) + "\n" + file.errorString()); return; } m_isoImageSize = file.size(); m_isoImagePath = isoImagePath; m_isoImageLineEdit->setText(QDir::toNativeSeparators(m_isoImagePath) + " (" + KFormat().formatByteSize(m_isoImageSize) + ")"); file.close(); // Verify ISO image m_busyLabel->setText(i18n("Verifying ISO image")); m_busyWidget->show(); m_busySpinner->setSequence(KIconLoader::global()->loadPixmapSequence("process-working", KIconLoader::SizeSmallMedium)); m_busySpinner->start(); IsoVerifier *isoVerifier = new IsoVerifier(m_isoImagePath); QThread *verifierThread = new QThread(this); connect(verifierThread, &QThread::started, isoVerifier, &IsoVerifier::verifyIso); connect(verifierThread, &QThread::finished, verifierThread, &QThread::deleteLater); connect(isoVerifier, &IsoVerifier::finished, verifierThread, &QThread::quit); connect(isoVerifier, &IsoVerifier::finished, isoVerifier, &IsoVerifier::deleteLater); connect(isoVerifier, &IsoVerifier::finished, this, &MainWindow::showIsoVerificationResult); connect(isoVerifier, &IsoVerifier::inputRequested, this, &MainWindow::showInputDialog); connect(this, &MainWindow::inputTextReady, isoVerifier, &IsoVerifier::verifyWithInputText); isoVerifier->moveToThread(verifierThread); verifierThread->start(); // Enable the Write button (if there are USB flash disks present) m_createButton->setEnabled(m_usbDriveComboBox->count() > 0); } void MainWindow::cleanUp() { // Delete all the allocated UsbDevice objects attached to the combobox for (int i = 0; i < m_usbDriveComboBox->count(); ++i) { delete m_usbDriveComboBox->itemData(i).value(); } } void MainWindow::enumFlashDevices() { m_enumFlashDevicesWaiting = false; // Remember the currently selected device QString selectedDevice = ""; int idx = m_usbDriveComboBox->currentIndex(); if (idx >= 0) { UsbDevice* dev = m_usbDriveComboBox->itemData(idx).value(); selectedDevice = dev->m_PhysicalDevice; } // Remove the existing entries cleanUp(); m_usbDriveComboBox->clear(); // Disable the combobox m_usbDriveComboBox->setEnabled(false); // Add the USB flash devices to the combobox platformEnumFlashDevices(addFlashDeviceCallback, m_usbDriveComboBox); // Restore the previously selected device (if present) if (!selectedDevice.isEmpty()) for (int i = 0; i < m_usbDriveComboBox->count(); ++i) { UsbDevice* dev = m_usbDriveComboBox->itemData(i).value(); if (dev->m_PhysicalDevice == selectedDevice) { m_usbDriveComboBox->setCurrentIndex(i); break; } } // Update the Write button enabled/disabled state m_createButton->setEnabled(m_usbDriveComboBox->count() > 0 && m_isoImagePath != ""); // Enable/disable the usb drive combobox if (m_usbDriveComboBox->count() < 1) { m_usbDriveComboBox->setEnabled(false); m_usbDriveComboBox->setEditable(true); m_usbDriveComboBox->lineEdit() ->setPlaceholderText(i18n("Please plug in a USB drive")); } else { m_usbDriveComboBox->setEnabled(true); m_usbDriveComboBox->setEditable(false); } } void MainWindow::writeToDevice(bool zeroing) { UsbDevice* selectedDevice = m_usbDriveComboBox->itemData( m_usbDriveComboBox->currentIndex()).value(); // Use KAuth to get required previleges in supported platforms #if defined(Q_OS_LINUX) connect(m_cancelButton, &QPushButton::clicked, this, &MainWindow::cancelWriting); KAuth::Action action("org.kde.isoimagewriter.write"); action.setHelperId("org.kde.isoimagewriter"); QVariantMap args; args[QStringLiteral("zeroing")] = QVariant(zeroing); args[QStringLiteral("imagefile")] = m_isoImagePath; args[QStringLiteral("usbdevice_visiblename")] = selectedDevice->m_VisibleName; args[QStringLiteral("usbdevice_volumes")] = selectedDevice->m_Volumes[0]; args[QStringLiteral("usbdevice_size")] = QString("%1").arg(selectedDevice->m_Size); args[QStringLiteral("usbdevice_sectorsize")] = selectedDevice->m_SectorSize; args[QStringLiteral("usbdevice_physicaldevice")] = selectedDevice->m_PhysicalDevice; action.setArguments(args); action.setTimeout(3600000); // an hour m_job = action.execute(); connect(m_job, SIGNAL(percent(KJob*,ulong)), this, SLOT(progressStep(KJob*,ulong)), Qt::DirectConnection); connect(m_job, SIGNAL(newData(QVariantMap)), this, SLOT(progressStep(QVariantMap))); connect(m_job, &KAuth::ExecuteJob::statusChanged, this, &MainWindow::statusChanged); connect(m_job, &KAuth::ExecuteJob::result, this, &MainWindow::finished); m_job->start(); #else ImageWriter* writer = new ImageWriter(zeroing ? "" : m_isoImagePath, selectedDevice); QThread *writerThread = new QThread(this); // Connect start and end signals connect(writerThread, &QThread::started, writer, &ImageWriter::writeImage); // When writer finishes its job, quit the thread connect(writer, &ImageWriter::finished, writerThread, &QThread::quit); // Guarantee deleting the objects after completion connect(writer, &ImageWriter::finished, writer, &ImageWriter::deleteLater); connect(writerThread, &QThread::finished, writerThread, &QThread::deleteLater); // If the Cancel button is pressed, inform the writer to stop the operation // Using DirectConnection because the thread does not read its own event queue until completion connect(m_cancelButton, &QPushButton::clicked, writer, &ImageWriter::cancelWriting, Qt::DirectConnection); // Each time a block is written, update the progress bar connect(writer, &ImageWriter::blockWritten, this, &MainWindow::updateProgressBar); // Show the message about successful completion on success connect(writer, &ImageWriter::success, this, &MainWindow::showSuccessMessage); // Show error message if error is sent by the worker connect(writer, &ImageWriter::error, this, &MainWindow::showErrorMessage); // Silently return back to normal dialog form if the operation was cancelled connect(writer, &ImageWriter::cancelled, this, &MainWindow::hideWritingProgress); // Now start the writer thread writer->moveToThread(writerThread); writerThread->start(); #endif showWritingProgress(alignNumberDiv((zeroing ? DEFAULT_UNIT : m_isoImageSize), DEFAULT_UNIT)); } void MainWindow::dragEnterEvent(QDragEnterEvent* event) { // Accept only files with ANSI or Unicode paths (Windows) and URIs (Linux) if (event->mimeData()->hasFormat("application/x-qt-windows-mime;value=\"FileName\"") || event->mimeData()->hasFormat("application/x-qt-windows-mime;value=\"FileNameW\"") || event->mimeData()->hasFormat("text/uri-list")) event->accept(); } void MainWindow::dropEvent(QDropEvent* event) { QString newImageFile = ""; QByteArray droppedFileName; // First, try to use the Unicode file name droppedFileName = event->mimeData()->data("application/x-qt-windows-mime;value=\"FileNameW\""); if (!droppedFileName.isEmpty()) { newImageFile = QString::fromWCharArray(reinterpret_cast(droppedFileName.constData())); } else { // If failed, use the ANSI name with the local codepage droppedFileName = event->mimeData()->data("application/x-qt-windows-mime;value=\"FileName\""); if (!droppedFileName.isEmpty()) { newImageFile = QString::fromLocal8Bit(droppedFileName.constData()); } else { // And, finally, try the URI droppedFileName = event->mimeData()->data("text/uri-list"); if (!droppedFileName.isEmpty()) { // If several files are dropped they are separated by newlines, // take the first file int newLineIndexLF = droppedFileName.indexOf('\n'); int newLineIndex = droppedFileName.indexOf("\r\n"); // Make sure both CRLF and LF are accepted if ((newLineIndexLF != -1) && (newLineIndexLF < newLineIndex)) newLineIndex = newLineIndexLF; if (newLineIndex != -1) droppedFileName = droppedFileName.left(newLineIndex); // Decode the file path from percent-encoding QUrl url = QUrl::fromEncoded(droppedFileName); if (url.isLocalFile()) newImageFile = url.toLocalFile(); } } } if (!newImageFile.isEmpty()) { // If something was really received update the information preprocessIsoImage(newImageFile); } } void MainWindow::closeEvent(QCloseEvent* event) { if (m_isWriting) { const int answer = QMessageBox::question( this, i18n("Cancel?"), i18n("Writing is in progress, abort it?")); if (answer == QMessageBox::No) event->ignore(); } } void MainWindow::addFlashDeviceCallback(void* cbParam, UsbDevice* device) { auto usbDriveComboBox = (QComboBox*)cbParam; usbDriveComboBox->addItem(device->formatDisplayName(), QVariant::fromValue(device)); } void MainWindow::openIsoImage() { const QString filter = i18n("Disk Images (%1)", QString("*.iso *.bin *.img")) + ";;" + i18n("All Files (%1)", QString("*")); QString isoImagePath = QFileDialog::getOpenFileName(this, "", m_lastOpenedDir, filter, nullptr, QFileDialog::ReadOnly); if (!isoImagePath.isEmpty()) { m_lastOpenedDir = isoImagePath.left(isoImagePath.lastIndexOf('/')); preprocessIsoImage(isoImagePath); } } void MainWindow::writeIsoImage() { if (m_usbDriveComboBox->count() == 0 || m_isoImagePath == "") return; UsbDevice* selectedDevice = m_usbDriveComboBox->itemData( m_usbDriveComboBox->currentIndex()).value(); if (m_isoImageSize > selectedDevice->m_Size) { QMessageBox::critical( this, i18n("Error"), i18n("The image is larger than your selected device!\n\n" "Image size: %1 (%2 b)\n" "Disk size: %3 (%4 b)", KFormat().formatByteSize(m_isoImageSize), m_isoImageSize, KFormat().formatByteSize(selectedDevice->m_Size), selectedDevice->m_Size), QMessageBox::Ok); return; } writeToDevice(false); } void MainWindow::updateProgressBar(int increment) { int newValue = m_progressBar->value() + increment; m_progressBar->setValue(newValue); m_externalProgressBar.SetProgressValue(newValue); } void MainWindow::showWritingProgress(int maxValue) { m_isWriting = true; // Do not accept dropped files while writing setAcceptDrops(false); // Display and customize the progress bar part m_progressBar->setMinimum(0); m_progressBar->setMaximum(maxValue); m_progressBar->setValue(0); // Expose the progress bar state to the OS m_externalProgressBar.InitProgressBar(maxValue); m_centralStackedWidget->setCurrentIndex(2); } void MainWindow::hideWritingProgress() { m_isWriting = false; // Enable drag & drop setAcceptDrops(true); // Send a signal that progressbar is no longer present m_externalProgressBar.DestroyProgressBar(); m_centralStackedWidget->setCurrentIndex(0); // If device list changed during writing update it now if (m_enumFlashDevicesWaiting) enumFlashDevices(); } void MainWindow::showErrorMessage(const QString &message) { m_externalProgressBar.ProgressSetError(); QMessageBox::critical(this, i18n("Error"), message); hideWritingProgress(); } void MainWindow::showSuccessMessage() { m_isWriting = false; // Do not accept dropped files setAcceptDrops(false); m_centralStackedWidget->setCurrentIndex(3); } void MainWindow::showConfirmMessage() { // Do not accept dropped files setAcceptDrops(false); m_centralStackedWidget->setCurrentIndex(1); } void MainWindow::showIsoVerificationResult(const bool &isIsoValid, const QString &error) { if (isIsoValid) { m_busyLabel->setText(i18n("The ISO image is valid")); m_busySpinner->setSequence(KIconLoader::global()->loadPixmapSequence("checkmark", KIconLoader::SizeSmallMedium)); } else { m_busyLabel->setText(i18n("Could not verify ISO image")); m_busySpinner->setSequence(KIconLoader::global()->loadPixmapSequence("error", KIconLoader::SizeSmallMedium)); QMessageBox::warning(this, i18n("ISO Verification failed"), error); } } #if defined(Q_OS_LINUX) void MainWindow::cancelWriting() { qCDebug(ISOIMAGEWRITER_LOG) << "cancelWriting()"; m_job->kill(); qCDebug(ISOIMAGEWRITER_LOG) << "cancelWriting() done"; } void MainWindow::progressStep(KJob* job, unsigned long step) { Q_UNUSED(job) qCDebug(ISOIMAGEWRITER_LOG) << "progressStep %() " << step; updateProgressBar(step); } void MainWindow::progressStep(const QVariantMap & data) { qCDebug(ISOIMAGEWRITER_LOG) << "progressStep(QVariantMap) ";// << step; if (data[QStringLiteral("progress")].isValid()) { int step = data[QStringLiteral("progress")].toInt(); updateProgressBar(step); } else if (data[QStringLiteral("error")].isValid()) { showErrorMessage(data[QStringLiteral("error")].toString()); } else if (data[QStringLiteral("success")].isValid()) { showSuccessMessage(); + } else if (data[QStringLiteral("cancel")].isValid()) { + hideWritingProgress(); } } void MainWindow::statusChanged(KAuth::Action::AuthStatus status) { qCDebug(ISOIMAGEWRITER_LOG) << "statusChanged: " << status; } void MainWindow::finished(KJob* job) { qCDebug(ISOIMAGEWRITER_LOG) << "finished() " << job->error(); KAuth::ExecuteJob *job2 = (KAuth::ExecuteJob *)job; qCDebug(ISOIMAGEWRITER_LOG) << "finished() " << job2->data(); - if (job2->data()[QStringLiteral("success")].isValid()) - showSuccessMessage(); - else + if (job->error() != 0) hideWritingProgress(); } #endif