diff --git a/core/kget.cpp b/core/kget.cpp index 01723f0f..193b647e 100644 --- a/core/kget.cpp +++ b/core/kget.cpp @@ -1,1589 +1,1589 @@ /* This file is part of the KDE project Copyright (C) 2005 Dario Massarin Copyright (C) 2007-2009 Lukas Appelhans Copyright (C) 2008 Urs Wolfer Copyright (C) 2008 Dario Freddi Copyright (C) 2009 Matthias Fuchs 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 2 of the License, or (at your option) any later version. */ #include "core/kget.h" #include "mainwindow.h" #include "core/mostlocalurl.h" #include "core/transfer.h" #include "core/transferdatasource.h" #include "core/transfergroup.h" #include "core/transfergrouphandler.h" #include "core/transfertreemodel.h" #include "core/transfertreeselectionmodel.h" #include "core/plugin/plugin.h" #include "core/plugin/transferfactory.h" #include "core/kuiserverjobs.h" #include "core/transfergroupscheduler.h" #include "settings.h" #include "core/transferhistorystore.h" #include "kget_debug.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include -#include +#include #ifdef HAVE_KWORKSPACE #include #include #include #include #endif KGet::TransferData::TransferData(const QUrl &source, const QUrl &destination, const QString& group, bool doStart, const QDomElement *element) : src(source), dest(destination), groupName(group), start(doStart), e(element) { } /** * This is our KGet class. This is where the user's transfers and searches are * stored and organized. * Use this class from the views to add or remove transfers or searches * In order to organize the transfers inside categories we have a TransferGroup * class. By definition, a transfer must always belong to a TransferGroup. If we * don't want it to be displayed by the gui inside a specific group, we will put * it in the group named "Not grouped" (better name?). **/ KGet* KGet::self( MainWindow * mainWindow ) { if(mainWindow) { m_mainWindow = mainWindow; m_jobManager = new KUiServerJobs(m_mainWindow); } static KGet *m = new KGet(); return m; } bool KGet::addGroup(const QString& groupName) { qCDebug(KGET_DEBUG); // Check if a group with that name already exists if (m_transferTreeModel->findGroup(groupName)) return false; TransferGroup * group = new TransferGroup(m_transferTreeModel, m_scheduler, groupName); m_transferTreeModel->addGroup(group); return true; } void KGet::delGroup(TransferGroupHandler *group, bool askUser) { TransferGroup *g = group->m_group; if (askUser) { QWidget *configDialog = KConfigDialog::exists("preferences"); if (KMessageBox::warningYesNo(configDialog ? configDialog : m_mainWindow, i18n("Are you sure that you want to remove the group named %1?", g->name()), i18n("Remove Group"), KStandardGuiItem::remove(), KStandardGuiItem::cancel()) != KMessageBox::Yes) return; } m_transferTreeModel->delGroup(g); g->deleteLater(); } void KGet::delGroups(QList groups, bool askUser) { if (groups.isEmpty()) return; if (groups.count() == 1) { KGet::delGroup(groups.first(), askUser); return; } bool del = !askUser; if (askUser) { QStringList names; foreach (TransferGroupHandler * handler, groups) names << handler->name(); QWidget * configDialog = KConfigDialog::exists("preferences"); del = KMessageBox::warningYesNoList(configDialog ? configDialog : m_mainWindow, i18n("Are you sure that you want to remove the following groups?"), names, i18n("Remove groups"), KStandardGuiItem::remove(), KStandardGuiItem::cancel()) == KMessageBox::Yes; } if (del) { foreach (TransferGroupHandler * handler, groups) KGet::delGroup(handler, false); } } void KGet::renameGroup(const QString& oldName, const QString& newName) { TransferGroup *group = m_transferTreeModel->findGroup(oldName); if(group) { group->handler()->setName(newName); } } QStringList KGet::transferGroupNames() { QStringList names; foreach(TransferGroup *group, m_transferTreeModel->transferGroups()) { names << group->name(); } return names; } TransferHandler * KGet::addTransfer(QUrl srcUrl, QString destDir, QString suggestedFileName, // krazy:exclude=passbyvalue QString groupName, bool start) { srcUrl = mostLocalUrl(srcUrl); // Note: destDir may actually be a full path to a file :-( qCDebug(KGET_DEBUG) << "Source:" << srcUrl.url() << ", dest: " << destDir << ", sugg file: " << suggestedFileName << endl; QUrl destUrl; // the final destination, including filename if ( srcUrl.isEmpty() ) { //No src location: we let the user insert it manually srcUrl = urlInputDialog(); if( srcUrl.isEmpty() ) return nullptr; } if ( !isValidSource( srcUrl ) ) return nullptr; // when we get a destination directory and suggested filename, we don't // need to ask for it again bool confirmDestination = false; if (destDir.isEmpty()) { confirmDestination = true; QList list = groupsFromExceptions(srcUrl); if (!list.isEmpty()) { destDir = list.first()->defaultFolder(); groupName = list.first()->name(); } } else { // check whether destDir is actually already the path to a file QUrl targetUrl = QUrl(destDir); QString directory = targetUrl.adjusted(QUrl::RemoveFilename).toString(); QString fileName = targetUrl.fileName(QUrl::PrettyDecoded); if (QFileInfo(directory).isDir() && !fileName.isEmpty()) { destDir = directory; suggestedFileName = fileName; } } if (suggestedFileName.isEmpty()) { confirmDestination = true; suggestedFileName = srcUrl.fileName(QUrl::PrettyDecoded); if (suggestedFileName.isEmpty()) { // simply use the full url as filename suggestedFileName = QUrl::toPercentEncoding( srcUrl.toDisplayString(), "/" ); } } // now ask for confirmation of the entire destination url (dir + filename) if (confirmDestination || !isValidDestDirectory(destDir)) { do { destUrl = destFileInputDialog(destDir, suggestedFileName); if (destUrl.isEmpty()) return nullptr; destDir = destUrl.adjusted(QUrl::RemoveFilename).toString(); } while (!isValidDestDirectory(destDir)); } else { destUrl = QUrl(); destUrl.setPath(destDir + suggestedFileName); } destUrl = getValidDestUrl(destUrl, srcUrl); if (destUrl == QUrl()) return nullptr; TransferHandler *transfer = createTransfer(srcUrl, destUrl, groupName, start); if (transfer) { KGet::showNotification(m_mainWindow, "added", i18n("

The following transfer has been added to the download list:

%1

", transfer->source().toString()), "kget", i18n("Download added")); } return transfer; } QList KGet::addTransfers(const QList &elements, const QString &groupName) { QList data; foreach(const QDomElement &e, elements) { //We need to read these attributes now in order to know which transfer //plugin to use. QUrl srcUrl = QUrl(e.attribute("Source")); QUrl destUrl = QUrl(e.attribute("Dest")); data << TransferData(srcUrl, destUrl, groupName, false, &e); qCDebug(KGET_DEBUG) << "src=" << srcUrl << " dest=" << destUrl << " group=" << groupName; } return createTransfers(data); } const QList KGet::addTransfer(QList srcUrls, QString destDir, QString groupName, bool start) { QList urlsToDownload; QList::iterator it = srcUrls.begin(); QList::iterator itEnd = srcUrls.end(); QList addedTransfers; for(; it!=itEnd ; ++it) { *it = mostLocalUrl(*it); if ( isValidSource( *it ) ) urlsToDownload.append( *it ); } if ( urlsToDownload.count() == 0 ) return addedTransfers; if ( urlsToDownload.count() == 1 ) { // just one file -> ask for filename TransferHandler * newTransfer = addTransfer(srcUrls.first(), destDir, srcUrls.first().fileName(), groupName, start); if (newTransfer) { addedTransfers.append(newTransfer); } return addedTransfers; } QUrl destUrl; // multiple files -> ask for directory, not for every single filename if (!isValidDestDirectory(destDir))//TODO: Move that after the for-loop destDir = destDirInputDialog(); it = urlsToDownload.begin(); itEnd = urlsToDownload.end(); QList data; for ( ; it != itEnd; ++it ) { if (destDir.isEmpty()) { //TODO only use groupsFromExceptions if that is allowed in the settings QList list = groupsFromExceptions(*it); if (!list.isEmpty()) { destDir = list.first()->defaultFolder(); groupName = list.first()->name(); } } destUrl = getValidDestUrl(QUrl(destDir), *it); if (destUrl == QUrl()) continue; data << TransferData(*it, destUrl, groupName, start); } QList transfers = createTransfers(data); if (!transfers.isEmpty()) { QString urls = transfers[0]->source().toString(); for (int i = 1; i < transfers.count(); ++i) { urls += '\n' + transfers[i]->source().toString(); } QString message; if (transfers.count() == 1) { message = i18n("

The following transfer has been added to the download list:

"); } else { message = i18n("

The following transfers have been added to the download list:

"); } const QString content = QString("

%1

").arg(urls); KGet::showNotification(m_mainWindow, "added", message + content, "kget", i18n("Download added")); } return transfers; } bool KGet::delTransfer(TransferHandler * transfer, DeleteMode mode) { return delTransfers(QList() << transfer, mode); } bool KGet::delTransfers(const QList &handlers, DeleteMode mode) { if (!m_store) { m_store = TransferHistoryStore::getStore(); } QList transfers; QList historyItems; foreach (TransferHandler *handler, handlers) { Transfer *transfer = handler->m_transfer; transfers << transfer; historyItems << TransferHistoryItem(*transfer); // TransferHandler deinitializations handler->destroy(); // Transfer deinitializations (the deinit function is called by the destroy() function) if (mode == AutoDelete) { Transfer::DeleteOptions o = Transfer::DeleteTemporaryFiles; if (transfer->status() != Job::Finished && transfer->status() != Job::FinishedKeepAlive) o |= Transfer::DeleteFiles; transfer->destroy(o); } else { transfer->destroy((Transfer::DeleteTemporaryFiles | Transfer::DeleteFiles)); } } m_store->saveItems(historyItems); m_transferTreeModel->delTransfers(transfers); qDeleteAll(transfers); return true; } void KGet::moveTransfer(TransferHandler * transfer, const QString& groupName) { Q_UNUSED(transfer) Q_UNUSED(groupName) } void KGet::redownloadTransfer(TransferHandler * transfer) { QString group = transfer->group()->name(); QString src = transfer->source().url(); QString dest = transfer->dest().url(); QString destFile = transfer->dest().fileName(); KGet::delTransfer(transfer); KGet::addTransfer(src, dest, destFile, group, true); } QList KGet::selectedTransfers() { // qCDebug(KGET_DEBUG) << "KGet::selectedTransfers"; QList selectedTransfers; QModelIndexList selectedIndexes = m_selectionModel->selectedRows(); //sort the indexes as this can speed up operations like deleting etc. qSort(selectedIndexes.begin(), selectedIndexes.end()); foreach(const QModelIndex ¤tIndex, selectedIndexes) { ModelItem * item = m_transferTreeModel->itemFromIndex(currentIndex); if (!item->isGroup()) selectedTransfers.append(item->asTransfer()->transferHandler()); } return selectedTransfers; // This is the code that was used in the old selectedTransfers function /* QList::const_iterator it = m_transferTreeModel->transferGroups().begin(); QList::const_iterator itEnd = m_transferTreeModel->transferGroups().end(); for( ; it!=itEnd ; ++it ) { TransferGroup::iterator it2 = (*it)->begin(); TransferGroup::iterator it2End = (*it)->end(); for( ; it2!=it2End ; ++it2 ) { Transfer * transfer = (Transfer*) *it2; if( transfer->isSelected() ) selectedTransfers.append( transfer->handler() ); } } return selectedTransfers;*/ } QList KGet::finishedTransfers() { QList finishedTransfers; foreach(TransferHandler *transfer, allTransfers()) { if (transfer->status() == Job::Finished) { finishedTransfers << transfer; } } return finishedTransfers; } QList KGet::selectedTransferGroups() { QList selectedTransferGroups; QModelIndexList selectedIndexes = m_selectionModel->selectedRows(); foreach(const QModelIndex ¤tIndex, selectedIndexes) { ModelItem * item = m_transferTreeModel->itemFromIndex(currentIndex); if (item->isGroup()) { TransferGroupHandler *group = item->asGroup()->groupHandler(); selectedTransferGroups.append(group); } } return selectedTransferGroups; } TransferTreeModel * KGet::model() { return m_transferTreeModel; } TransferTreeSelectionModel * KGet::selectionModel() { return m_selectionModel; } void KGet::load( QString filename ) // krazy:exclude=passbyvalue { qCDebug(KGET_DEBUG) << "(" << filename << ")"; if(filename.isEmpty()) filename = KStandardDirs::locateLocal("appdata", "transfers.kgt"); QTemporaryFile tmpFile; QUrl url = QUrl(filename); if (url.scheme().isEmpty()) url.setScheme("file"); KIO::StoredTransferJob * job = KIO::storedGet(url); job->exec(); if (job->data().isEmpty() || !tmpFile.open()) { qCDebug(KGET_DEBUG) << "Transferlist empty or cannot open temporary file"; if (m_transferTreeModel->transferGroups().isEmpty()) //Create the default group addGroup(i18n("My Downloads")); return; } tmpFile.write(job->data()); tmpFile.close(); QDomDocument doc; qCDebug(KGET_DEBUG) << "file:" << tmpFile.fileName(); if(doc.setContent(&tmpFile)) { QDomElement root = doc.documentElement(); QDomNodeList nodeList = root.elementsByTagName("TransferGroup"); int nItems = nodeList.length(); for( int i = 0 ; i < nItems ; i++ ) { TransferGroup * foundGroup = m_transferTreeModel->findGroup( nodeList.item(i).toElement().attribute("Name") ); qCDebug(KGET_DEBUG) << "KGet::load -> group = " << nodeList.item(i).toElement().attribute("Name"); if( !foundGroup ) { qCDebug(KGET_DEBUG) << "KGet::load -> group not found"; TransferGroup * newGroup = new TransferGroup(m_transferTreeModel, m_scheduler); m_transferTreeModel->addGroup(newGroup); newGroup->load(nodeList.item(i).toElement()); } else { qCDebug(KGET_DEBUG) << "KGet::load -> group found"; //A group with this name already exists. //Integrate the group's transfers with the ones read from file foundGroup->load(nodeList.item(i).toElement()); } } } else { qCWarning(KGET_DEBUG) << "Error reading the transfers file"; } if (m_transferTreeModel->transferGroups().isEmpty()) //Create the default group addGroup(i18n("My Downloads")); new GenericObserver(m_mainWindow); } void KGet::save( QString filename, bool plain ) // krazy:exclude=passbyvalue { if ( !filename.isEmpty() && QFile::exists( filename ) && (KMessageBox::questionYesNoCancel(nullptr, i18n("The file %1 already exists.\nOverwrite?", filename), i18n("Overwrite existing file?"), KStandardGuiItem::yes(), KStandardGuiItem::no(), KStandardGuiItem::cancel(), "QuestionFilenameExists" ) != KMessageBox::Yes) ) return; if(filename.isEmpty()) filename = KStandardDirs::locateLocal("appdata", "transfers.kgt"); qCDebug(KGET_DEBUG) << "Save transferlist to " << filename; QSaveFile file(filename); if ( !file.open( QIODevice::WriteOnly ) ) { //qCWarning(KGET_DEBUG)<<"Unable to open output file when saving"; KGet::showNotification(m_mainWindow, "error", i18n("Unable to save to: %1", filename)); return; } if (plain) { QTextStream out(&file); foreach(TransferHandler *handler, allTransfers()) { out << handler->source().toString() << endl; } } else { QDomDocument doc(QString("KGetTransfers")); QDomElement root = doc.createElement("Transfers"); doc.appendChild(root); foreach (TransferGroup * group, m_transferTreeModel->transferGroups()) { QDomElement e = doc.createElement("TransferGroup"); root.appendChild(e); group->save(e); //KGet::delGroup((*it)->name()); } QTextStream stream( &file ); doc.save( stream, 2 ); } file.commit(); } QList KGet::factories() { return m_transferFactories; } KPluginInfo::List KGet::pluginInfos() { return m_pluginInfoList; } TransferFactory * KGet::factory(TransferHandler * transfer) { return transfer->m_transfer->factory(); } KActionCollection * KGet::actionCollection() { return m_mainWindow->actionCollection(); } void KGet::setSchedulerRunning(bool running) { if(running) { m_scheduler->stop(); //stopall first, to have a clean startingpoint m_scheduler->start(); } else m_scheduler->stop(); } bool KGet::schedulerRunning() { return (m_scheduler->hasRunningJobs()); } void KGet::setSuspendScheduler(bool isSuspended) { m_scheduler->setIsSuspended(isSuspended); } QList KGet::allTransfers() { QList transfers; foreach (TransferGroup *group, KGet::m_transferTreeModel->transferGroups()) { transfers << group->handler()->transfers(); } return transfers; } QList KGet::allTransferGroups() { QList transfergroups; foreach (TransferGroup *group, KGet::m_transferTreeModel->transferGroups()) { kDebug() << group->name(); transfergroups << group->handler(); } return transfergroups; } TransferHandler * KGet::findTransfer(const QUrl &src) { Transfer *transfer = KGet::m_transferTreeModel->findTransfer(src); if (transfer) { return transfer->handler(); } return nullptr; } TransferGroupHandler * KGet::findGroup(const QString &name) { TransferGroup *group = KGet::m_transferTreeModel->findGroup(name); if (group) { return group->handler(); } return nullptr; } void KGet::checkSystemTray() { qCDebug(KGET_DEBUG); bool running = false; foreach (TransferHandler *handler, KGet::allTransfers()) { if (handler->status() == Job::Running) { running = true; break; } } m_mainWindow->setSystemTrayDownloading(running); } void KGet::settingsChanged() { qCDebug(KGET_DEBUG); foreach (TransferFactory *factory, m_transferFactories) { factory->settingsChanged(); } m_jobManager->settingsChanged(); m_scheduler->settingsChanged(); } QList KGet::groupsFromExceptions(const QUrl &filename) { QList handlers; foreach (TransferGroupHandler * handler, allTransferGroups()) { const QStringList patterns = handler->regExp().pattern().split(',');//FIXME 4.5 add a tooltip: "Enter a list of foo separated by ," and then do split(i18nc("used as separator in a list, translate to the same thing you translated \"Enter a list of foo separated by ,\"", ",")) if (matchesExceptions(filename, patterns)) { handlers.append(handler); } } return handlers; } bool KGet::matchesExceptions(const QUrl &sourceUrl, const QStringList &patterns) { foreach (const QString &pattern, patterns) { const QString trimmedPattern = pattern.trimmed(); if (trimmedPattern.isEmpty()) { continue; } QRegExp regExp = QRegExp(trimmedPattern); //try with Regular Expression first regExp.setPatternSyntax(QRegExp::RegExp2); regExp.setCaseSensitivity(Qt::CaseInsensitive); if (regExp.exactMatch(sourceUrl.url())) { return true; } //now try with wildcards if (!regExp.pattern().isEmpty() && !regExp.pattern().contains('*')) { regExp.setPattern('*' + regExp.pattern()); } regExp.setPatternSyntax(QRegExp::Wildcard); regExp.setCaseSensitivity(Qt::CaseInsensitive); if (regExp.exactMatch(sourceUrl.url())) { return true; } } return false; } void KGet::setGlobalDownloadLimit(int limit) { m_scheduler->setDownloadLimit(limit); } void KGet::setGlobalUploadLimit(int limit) { m_scheduler->setUploadLimit(limit); } void KGet::calculateGlobalSpeedLimits() { //if (m_scheduler->downloadLimit())//TODO: Remove this and the both other hacks in the 2 upper functions with a better replacement m_scheduler->calculateDownloadLimit(); //if (m_scheduler->uploadLimit()) m_scheduler->calculateUploadLimit(); } void KGet::calculateGlobalDownloadLimit() { m_scheduler->calculateDownloadLimit(); } void KGet::calculateGlobalUploadLimit() { m_scheduler->calculateUploadLimit(); } // ------ STATIC MEMBERS INITIALIZATION ------ TransferTreeModel * KGet::m_transferTreeModel; TransferTreeSelectionModel * KGet::m_selectionModel; QList KGet::m_transferFactories; KPluginInfo::List KGet::m_pluginInfoList; TransferGroupScheduler * KGet::m_scheduler = nullptr; MainWindow * KGet::m_mainWindow = nullptr; KUiServerJobs * KGet::m_jobManager = nullptr; TransferHistoryStore * KGet::m_store = nullptr; bool KGet::m_hasConnection = true; // ------ PRIVATE FUNCTIONS ------ KGet::KGet() { m_scheduler = new TransferGroupScheduler(); m_transferTreeModel = new TransferTreeModel(m_scheduler); m_selectionModel = new TransferTreeSelectionModel(m_transferTreeModel); QObject::connect(m_transferTreeModel, SIGNAL(transfersAddedEvent(QList)), m_jobManager, SLOT(slotTransfersAdded(QList))); QObject::connect(m_transferTreeModel, SIGNAL(transfersAboutToBeRemovedEvent(QList)), m_jobManager, SLOT(slotTransfersAboutToBeRemoved(QList))); QObject::connect(m_transferTreeModel, SIGNAL(transfersChangedEvent(QMap)), m_jobManager, SLOT(slotTransfersChanged(QMap))); //Load all the available plugins loadPlugins(); } KGet::~KGet() { kDebug(); delete m_transferTreeModel; delete m_jobManager; //This one must always be before the scheduler otherwise the job manager can't remove the notifications when deleting. delete m_scheduler; delete m_store; } TransferHandler * KGet::createTransfer(const QUrl &src, const QUrl &dest, const QString& groupName, bool start, const QDomElement * e) { QList transfer = createTransfers(QList() << TransferData(src, dest, groupName, start, e)); return (transfer.isEmpty() ? nullptr : transfer.first()); } QList KGet::createTransfers(const QList &dataItems) { QList handlers; if (dataItems.isEmpty()) { return handlers; } QList start; QHash > groups; QStringList urlsFailed; foreach (const TransferData &data, dataItems) { qCDebug(KGET_DEBUG) << "srcUrl=" << data.src << " destUrl=" << data.dest << " group=" << data.groupName; TransferGroup *group = m_transferTreeModel->findGroup(data.groupName); if (!group) { qCDebug(KGET_DEBUG) << "KGet::createTransfer -> group not found"; group = m_transferTreeModel->transferGroups().first(); } Transfer *newTransfer = nullptr; foreach (TransferFactory *factory, m_transferFactories) { qCDebug(KGET_DEBUG) << "Trying plugin n.plugins=" << m_transferFactories.size() << factory->displayName(); if ((newTransfer = factory->createTransfer(data.src, data.dest, group, m_scheduler, data.e))) { // qCDebug(KGET_DEBUG) << "KGet::createTransfer -> CREATING NEW TRANSFER ON GROUP: _" << group->name() << "_"; newTransfer->create(); newTransfer->load(data.e); handlers << newTransfer->handler(); groups[group] << newTransfer; start << data.start; break; } } if (!newTransfer) { urlsFailed << data.src.url(); qCWarning(KGET_DEBUG) << "Warning! No plugin found to handle" << data.src; } } //show urls that failed if (!urlsFailed.isEmpty()) { QString message = i18np("

The following URL cannot be downloaded, its protocol is not supported by KGet:

", "

The following URLs cannot be downloaded, their protocols are not supported by KGet:

", urlsFailed.count()); QString content = urlsFailed.takeFirst(); foreach (const QString &url, urlsFailed) { content += '\n' + url; } content = QString("

%1

").arg(content); KGet::showNotification(m_mainWindow, "error", message + content, "dialog-error", i18n("Protocol unsupported")); } //add the created transfers to the model and start them if specified QHash >::const_iterator it; QHash >::const_iterator itEnd = groups.constEnd(); for (it = groups.constBegin(); it != itEnd; ++it) { KGet::model()->addTransfers(it.value(), it.key()); } for (int i = 0; i < handlers.count(); ++i) { if (start[i]) { handlers[i]->start(); } } return handlers;//TODO implement error message if it is 0, or should the addTransfers stuff do that, in case if the numer of returned items does not match the number of sent data? } TransferDataSource * KGet::createTransferDataSource(const QUrl &src, const QDomElement &type, QObject *parent) { qCDebug(KGET_DEBUG); TransferDataSource *dataSource; foreach (TransferFactory *factory, m_transferFactories) { dataSource = factory->createTransferDataSource(src, type, parent); if(dataSource) return dataSource; } return nullptr; } QString KGet::generalDestDir(bool preferXDGDownloadDir) { QString dir = Settings::lastDirectory(); if (preferXDGDownloadDir) { dir = QStandardPaths::writableLocation(QStandardPaths::DownloadLocation); } return dir; } QUrl KGet::urlInputDialog() { QString newtransfer; bool ok = false; QUrl clipboardUrl = QUrl(QApplication::clipboard()->text(QClipboard::Clipboard).trimmed()); if (clipboardUrl.isValid()) newtransfer = clipboardUrl.url(); while (!ok) { newtransfer = QInputDialog::getText(nullptr, i18n("New Download"), i18n("Enter URL:"), QLineEdit::Normal, newtransfer, &ok); newtransfer = newtransfer.trimmed(); //Remove any unnecessary space at the beginning and/or end if (!ok) { //user pressed cancel return QUrl(); } QUrl src = QUrl(newtransfer); if(src.isValid()) return src; else ok = false; } return QUrl(); } QString KGet::destDirInputDialog() { QString destDir = KFileDialog::getExistingDirectory(generalDestDir()); Settings::setLastDirectory(destDir); return destDir; } QUrl KGet::destFileInputDialog(QString destDir, const QString& suggestedFileName) // krazy:exclude=passbyvalue { if (destDir.isEmpty()) destDir = generalDestDir(); // Use the destination name if not empty... QUrl startLocation; if (!suggestedFileName.isEmpty()) { startLocation.setPath(destDir + suggestedFileName); } else { startLocation.setPath(destDir); } QUrl destUrl = KFileDialog::getSaveUrl(startLocation, QString(), m_mainWindow, i18n("Save As")); if (!destUrl.isEmpty()) { Settings::setLastDirectory(destUrl.adjusted(QUrl::RemoveFilename).toString()); } return destUrl; } bool KGet::isValidSource(const QUrl &source) { // Check if the URL is well formed if (!source.isValid()) { KGet::showNotification(m_mainWindow, "error", i18n("Malformed URL:\n%1", source.toString())); return false; } // Check if the URL contains the protocol if (source.scheme().isEmpty()){ KGet::showNotification(m_mainWindow, "error", i18n("Malformed URL, protocol missing:\n%1", source.toString())); return false; } // Check if a transfer with the same url already exists Transfer * transfer = m_transferTreeModel->findTransfer( source ); if (transfer) { if (transfer->status() == Job::Finished) { // transfer is finished, ask if we want to download again if (KMessageBox::questionYesNoCancel(nullptr, i18n("You have already completed a download from the location: \n\n%1\n\nDownload it again?", source.toString()), i18n("Download it again?"), KStandardGuiItem::yes(), KStandardGuiItem::no(), KStandardGuiItem::cancel()) == KMessageBox::Yes) { transfer->stop(); KGet::delTransfer(transfer->handler()); return true; } else return false; } else { if (KMessageBox::warningYesNoCancel(nullptr, i18n("You have a download in progress from the location: \n\n%1\n\nDelete it and download again?", source.toString()), i18n("Delete it and download again?"), KStandardGuiItem::yes(), KStandardGuiItem::no(), KStandardGuiItem::cancel()) == KMessageBox::Yes) { transfer->stop(); KGet::delTransfer(transfer->handler()); return true; } else return false; } return false; } return true; } bool KGet::isValidDestDirectory(const QString & destDir) { qCDebug(KGET_DEBUG) << destDir; if (!QFileInfo(destDir).isDir()) { if (QFileInfo(QUrl(destDir).adjusted(QUrl::RemoveFilename).toString()).isWritable()) return (!destDir.isEmpty()); if (!QFileInfo(QUrl(destDir).adjusted(QUrl::RemoveFilename).toString()).isWritable() && !destDir.isEmpty()) KMessageBox::error(nullptr, i18n("Directory is not writable")); } else { if (QFileInfo(destDir).isWritable()) return (!destDir.isEmpty()); if (!QFileInfo(destDir).isWritable() && !destDir.isEmpty()) KMessageBox::error(nullptr, i18n("Directory is not writable")); } return false; } QUrl KGet::getValidDestUrl(const QUrl& destDir, const QUrl &srcUrl) { kDebug() << "Source Url" << srcUrl << "Destination" << destDir; if ( !isValidDestDirectory(destDir.toLocalFile()) ) return QUrl(); QUrl destUrl = destDir; if (QFileInfo(destUrl.toLocalFile()).isDir()) { QString filename = srcUrl.fileName(); if (filename.isEmpty()) filename = QUrl::toPercentEncoding( srcUrl.toString(), "/" ); destUrl.adjusted( QUrl::RemoveFilename ); destUrl.setPath(destUrl.path() + filename); } Transfer * existingTransferDest = m_transferTreeModel->findTransferByDestination(destUrl); QPointer dlg = nullptr; if (existingTransferDest) { if (existingTransferDest->status() == Job::Finished) { if (KMessageBox::questionYesNoCancel(nullptr, i18n("You have already downloaded that file from another location.\n\nDownload and delete the previous one?"), i18n("File already downloaded. Download anyway?"), KStandardGuiItem::yes(), KStandardGuiItem::no(), KStandardGuiItem::cancel()) == KMessageBox::Yes) { existingTransferDest->stop(); KGet::delTransfer(existingTransferDest->handler()); //start = true; } else return QUrl(); } else { dlg = new KIO::RenameDialog( m_mainWindow, i18n("You are already downloading the same file"/*, destUrl.prettyUrl()*/), srcUrl, destUrl, KIO::RenameDialog_MultipleItems ); } } else if (srcUrl == destUrl) { dlg = new KIO::RenameDialog(m_mainWindow, i18n("File already exists"), srcUrl, destUrl, KIO::RenameDialog_MultipleItems); } else if (destUrl.isLocalFile() && QFile::exists(destUrl.toLocalFile())) { dlg = new KIO::RenameDialog( m_mainWindow, i18n("File already exists"), srcUrl, destUrl, KIO::RenameDialog_Overwrite ); } if (dlg) { int result = dlg->exec(); if (result == KIO::R_RENAME || result == KIO::R_OVERWRITE) destUrl = dlg->newDestUrl(); else { delete(dlg); return QUrl(); } delete(dlg); } return destUrl; } void KGet::loadPlugins() { m_transferFactories.clear(); m_pluginInfoList.clear(); // TransferFactory plugins const QVector offers = KPluginLoader::findPlugins(QStringLiteral("kget"), [](const KPluginMetaData& md) { return md.serviceTypes().contains(QStringLiteral("KGet/Plugin")) && md.value(QStringLiteral("X-KDE-KGet-framework-version")) == QString::number(FrameworkVersion) && md.value(QStringLiteral("X-KDE-KGet-rank")).toInt() > 0 && md.value(QStringLiteral("X-KDE-KGet-plugintype")) == QStringLiteral("TransferFactory"); }); qCDebug(KGET_DEBUG) << "Found" << offers.size() << "plugins"; //Here we use a QMap only to easily sort the plugins by rank QMap sortedOffers; for (const KPluginMetaData& md : offers) { sortedOffers[md.value("X-KDE-KGet-rank").toInt()] = md; qCDebug(KGET_DEBUG) << " TransferFactory plugin found:" << endl << " rank = " << md.value("X-KDE-KGet-rank").toInt() << endl << " plugintype = " << md.value("X-KDE-KGet-plugintype") << endl; } //I must fill this pluginList before and my m_transferFactories list after. //This because calling the KLibLoader::globalLibrary() erases the static //members of this class (why?), such as the m_transferFactories list. QList pluginList; const KConfigGroup plugins = KConfigGroup(KSharedConfig::openConfig(), "Plugins"); for (const KPluginMetaData& md : sortedOffers) { KPluginInfo info(md); info.load(plugins); m_pluginInfoList.prepend(info); if (!info.isPluginEnabled()) { qCDebug(KGET_DEBUG) << "TransferFactory plugin (" << md.fileName() << ") found, but not enabled"; continue; } KGetPlugin* plugin = loadPlugin(md); if (plugin != nullptr) { const QString pluginName = info.name(); pluginList.prepend(plugin); qCDebug(KGET_DEBUG) << "TransferFactory plugin (" << md.fileName() << ") found and added to the list of available plugins"; } else { qCDebug(KGET_DEBUG) << "Error loading TransferFactory plugin (" << md.fileName() << ")"; } } foreach (KGetPlugin* plugin, pluginList) { m_transferFactories.append(qobject_cast(plugin)); } qCDebug(KGET_DEBUG) << "Number of factories = " << m_transferFactories.size(); } void KGet::setHasNetworkConnection(bool hasConnection) { qCDebug(KGET_DEBUG) << "Existing internet connection:" << hasConnection << "old:" << m_hasConnection; if (hasConnection == m_hasConnection) { return; } m_hasConnection = hasConnection; const bool initialState = m_scheduler->hasRunningJobs(); m_scheduler->setHasNetworkConnection(hasConnection); const bool finalState = m_scheduler->hasRunningJobs(); if (initialState != finalState) { if (hasConnection) { KGet::showNotification(m_mainWindow, "notification", i18n("Internet connection established, resuming transfers."), "dialog-info"); } else { KGet::showNotification(m_mainWindow, "notification", i18n("No internet connection, stopping transfers."), "dialog-info"); } } } KGetPlugin* KGet::loadPlugin(const KPluginMetaData& md) { KPluginFactory* factory = KPluginLoader(md.fileName()).factory(); if (factory) { return factory->create(KGet::m_mainWindow); } else { KGet::showNotification(m_mainWindow, "error", i18n("Plugin loader could not load the plugin: %1.", md.fileName()), "dialog-info"); qCCritical(KGET_DEBUG) << "KPluginFactory could not load the plugin:" << md.fileName(); return nullptr; } } bool KGet::safeDeleteFile( const QUrl& url ) { if ( url.isLocalFile() ) { QFileInfo info( url.toLocalFile() ); if ( info.isDir() ) { KGet::showNotification(m_mainWindow, "notification", i18n("Not deleting\n%1\nas it is a directory.", url.toString()), "dialog-info"); return false; } KIO::DeleteJob * del = KIO::del(url); del->exec(); return true; } else KGet::showNotification(m_mainWindow, "notification", i18n("Not deleting\n%1\nas it is not a local file.", url.toString()), "dialog-info"); return false; } KNotification *KGet::showNotification(QWidget *parent, const QString &eventType, const QString &text, const QString &icon, const QString &title, const KNotification::NotificationFlags &flags) { return KNotification::event(eventType, title, text, QIcon::fromTheme(icon).pixmap(KIconLoader::SizeMedium), parent, flags); } GenericObserver::GenericObserver(QObject *parent) : QObject(parent), m_save(nullptr), m_finishAction(nullptr) { //check if there is a connection KGet::setHasNetworkConnection(m_networkConfig.isOnline()); connect(KGet::model(), SIGNAL(groupRemovedEvent(TransferGroupHandler*)), SLOT(groupRemovedEvent(TransferGroupHandler*))); connect(KGet::model(), SIGNAL(transfersAddedEvent(QList)), SLOT(transfersAddedEvent(QList))); connect(KGet::model(), SIGNAL(groupAddedEvent(TransferGroupHandler*)), SLOT(groupAddedEvent(TransferGroupHandler*))); connect(KGet::model(), SIGNAL(transfersRemovedEvent(QList)), SLOT(transfersRemovedEvent(QList))); connect(KGet::model(), SIGNAL(transfersChangedEvent(QMap)), SLOT(transfersChangedEvent(QMap))); connect(KGet::model(), SIGNAL(groupsChangedEvent(QMap)), SLOT(groupsChangedEvent(QMap))); connect(KGet::model(), SIGNAL(transferMovedEvent(TransferHandler*,TransferGroupHandler*)), SLOT(transferMovedEvent(TransferHandler*,TransferGroupHandler*))); connect(&m_networkConfig, SIGNAL(onlineStateChanged(bool)), this, SLOT(slotNetworkStatusChanged(bool))); } GenericObserver::~GenericObserver() { } void GenericObserver::groupAddedEvent(TransferGroupHandler *handler) { Q_UNUSED(handler) KGet::save(); } void GenericObserver::groupRemovedEvent(TransferGroupHandler *handler) { Q_UNUSED(handler) KGet::save(); } void GenericObserver::transfersAddedEvent(const QList &handlers) { Q_UNUSED(handlers) requestSave(); KGet::calculateGlobalSpeedLimits(); KGet::checkSystemTray(); } void GenericObserver::transfersRemovedEvent(const QList &handlers) { Q_UNUSED(handlers) requestSave(); KGet::calculateGlobalSpeedLimits(); KGet::checkSystemTray(); } void GenericObserver::transferMovedEvent(TransferHandler *transfer, TransferGroupHandler *group) { Q_UNUSED(transfer) Q_UNUSED(group) requestSave(); KGet::calculateGlobalSpeedLimits(); } void GenericObserver::requestSave() { if (!m_save) { m_save = new QTimer(this); m_save->setInterval(5000); connect(m_save, SIGNAL(timeout()), this, SLOT(slotSave())); } //save regularly if there are running jobs m_save->setSingleShot(!KGet::m_scheduler->hasRunningJobs()); if (!m_save->isActive()) { m_save->start(); } } void GenericObserver::slotSave() { KGet::save(); } void GenericObserver::transfersChangedEvent(QMap transfers) { bool checkSysTray = false; bool allFinished = true; QMap::const_iterator it; QMap::const_iterator itEnd = transfers.constEnd(); for (it = transfers.constBegin(); it != itEnd; ++it) { TransferHandler::ChangesFlags transferFlags = *it; TransferHandler *transfer = it.key(); if (transferFlags & Transfer::Tc_Status) { if ((transfer->status() == Job::Finished) && (transfer->startStatus() != Job::Finished)) { KGet::showNotification(KGet::m_mainWindow, "finished", i18n("

The following file has finished downloading:

%1

", transfer->dest().fileName()), "kget", i18n("Download completed")); } else if (transfer->status() == Job::Running) { KGet::showNotification(KGet::m_mainWindow, "started", i18n("

The following transfer has been started:

%1

", transfer->source().toString()), "kget", i18n("Download started")); } else if (transfer->status() == Job::Aborted && transfer->error().type != Job::AutomaticRetry) { KNotification * notification = KNotification::event("error", i18n("Error"), i18n("

There has been an error in the following transfer:

%1

" "

The error message is:

%2

", transfer->source().toString(), transfer->error().text), transfer->error().pixmap, KGet::m_mainWindow, KNotification::CloseOnTimeout); if (transfer->error().type == Job::ManualSolve) { m_notifications.insert(notification, transfer); notification->setActions(QStringList() << i18n("Resolve")); connect(notification, SIGNAL(action1Activated()), SLOT(slotResolveTransferError())); connect(notification, SIGNAL(closed()), SLOT(slotNotificationClosed())); } } } if (transferFlags & Transfer::Tc_Status) { checkSysTray = true; requestSave(); } if (transferFlags & Transfer::Tc_Percent) { transfer->group()->setGroupChange(TransferGroup::Gc_Percent, true); transfer->checkShareRatio(); } if (transferFlags & Transfer::Tc_DownloadSpeed) { transfer->group()->setGroupChange(TransferGroup::Gc_DownloadSpeed, true); } if (transferFlags & Transfer::Tc_UploadSpeed) { transfer->group()->setGroupChange(TransferGroup::Gc_UploadSpeed, true); } if ((transfer->status() == Job::Finished) || (transfer->status() == Job::FinishedKeepAlive)) { requestSave(); } else { allFinished = false; } } allFinished = allFinished && allTransfersFinished(); if (checkSysTray) KGet::checkSystemTray(); //only perform after finished actions if actually the status changed (that is the //case if checkSysTray is set to true) if (checkSysTray && Settings::afterFinishActionEnabled() && allFinished) { qCDebug(KGET_DEBUG) << "All finished"; KNotification *notification = nullptr; if (!m_finishAction) { m_finishAction = new QTimer(this); m_finishAction->setSingleShot(true); m_finishAction->setInterval(10000); connect(m_finishAction, SIGNAL(timeout()), this, SLOT(slotAfterFinishAction())); } switch (Settings::afterFinishAction()) { case KGet::Quit: notification = KGet::showNotification(KGet::m_mainWindow, "notification", i18n("KGet is now closing, as all downloads have completed."), "kget", "KGet", KNotification::Persistent | KNotification::CloseWhenWidgetActivated); break; #ifdef HAVE_KWORKSPACE case KGet::Shutdown: notification = KGet::showNotification(KGet::m_mainWindow, "notification", i18n("The computer will now turn off, as all downloads have completed."), "system-shutdown", i18nc("Shutting down computer", "Shutdown"), KNotification::Persistent | KNotification::CloseWhenWidgetActivated); break; case KGet::Hibernate: notification = KGet::showNotification(KGet::m_mainWindow, "notification", i18n("The computer will now suspend to disk, as all downloads have completed."), "system-suspend-hibernate", i18nc("Hibernating computer", "Hibernating"), KNotification::Persistent | KNotification::CloseWhenWidgetActivated); break; case KGet::Suspend: notification = KGet::showNotification(KGet::m_mainWindow, "notification", i18n("The computer will now suspend to RAM, as all downloads have completed."), "system-suspend", i18nc("Suspending computer", "Suspending"), KNotification::Persistent | KNotification::CloseWhenWidgetActivated); break; #endif default: break; } if (notification) { notification->setActions(QStringList() << i18nc("abort the proposed action", "Abort")); connect(notification, SIGNAL(action1Activated()), this, SLOT(slotAbortAfterFinishAction())); connect(m_finishAction, SIGNAL(timeout()), notification, SLOT(close())); if (!m_finishAction->isActive()) { m_finishAction->start(); } } } else if (allFinished) { KGet::showNotification(KGet::m_mainWindow, "finishedall", i18n("

All transfers have been finished.

"), "kget", i18n("Downloads completed")); } } void GenericObserver::slotResolveTransferError() { KNotification * notification = static_cast(QObject::sender()); if (notification) { TransferHandler * handler = m_notifications[notification]; kDebug() << "Resolve error for" << handler->source().toString() << "with id" << handler->error().id; handler->resolveError(handler->error().id); m_notifications.remove(notification); } } void GenericObserver::slotNotificationClosed() { kDebug() << "Remove notification"; KNotification * notification = static_cast(QObject::sender()); if (notification) m_notifications.remove(notification); } void GenericObserver::slotNetworkStatusChanged(bool online) { KGet::setHasNetworkConnection(online); } void GenericObserver::groupsChangedEvent(QMap groups) { bool recalculate = false; foreach (const TransferGroup::ChangesFlags &flags, groups) { if (flags & TransferGroup::Gc_Percent || flags & TransferGroup::Gc_Status) { recalculate = true; break; } } kDebug() << "Recalculate limits?" << recalculate; if (recalculate) KGet::calculateGlobalSpeedLimits(); } bool GenericObserver::allTransfersFinished() { bool quitFlag = true; // if all the downloads had state finished from // the beginning bool allWereFinished = true; foreach(TransferGroup *transferGroup, KGet::model()->transferGroups()) { foreach(TransferHandler *transfer, transferGroup->handler()->transfers()) { if ((transfer->status() != Job::Finished) && (transfer->status() != Job::FinishedKeepAlive)) { quitFlag = false; } if ((transfer->status() == Job::Finished || transfer->status() == Job::FinishedKeepAlive) && (transfer->startStatus() != Job::Finished && transfer->startStatus() != Job::FinishedKeepAlive)) { allWereFinished = false; } } } // if the only downloads in the queue // are those that are already finished // before the current KGet instance // we don't want to quit if (allWereFinished) { return false; } // otherwise, we did some downloads right now, let quitFlag decide return quitFlag; } void GenericObserver::slotAfterFinishAction() { qCDebug(KGET_DEBUG); switch (Settings::afterFinishAction()) { case KGet::Quit: qCDebug(KGET_DEBUG) << "Quit Kget."; QTimer::singleShot(0, KGet::m_mainWindow, SLOT(slotQuit())); break; #ifdef HAVE_KWORKSPACE case KGet::Shutdown: QTimer::singleShot(0, KGet::m_mainWindow, SLOT(slotQuit())); KWorkSpace::requestShutDown(KWorkSpace::ShutdownConfirmNo, KWorkSpace::ShutdownTypeHalt, KWorkSpace::ShutdownModeForceNow); break; case KGet::Hibernate: { QDBusMessage call; call = QDBusMessage::createMethodCall("org.kde.Solid.PowerManagement", "/org/kde/Solid/PowerManagement", "org.kde.Solid.PowerManagement", "suspendToRam"); QDBusConnection::sessionBus().asyncCall(call); break; } case KGet::Suspend: { QDBusMessage call; call = QDBusMessage::createMethodCall("org.kde.Solid.PowerManagement", "/org/kde/Solid/PowerManagement", "org.kde.Solid.PowerManagement", "suspendToDisk"); QDBusConnection::sessionBus().asyncCall(call); break; } #endif default: break; } } void GenericObserver::slotAbortAfterFinishAction() { qCDebug(KGET_DEBUG); m_finishAction->stop(); } diff --git a/plasma/applet/common/kgetappletutils.cpp b/plasma/applet/common/kgetappletutils.cpp index cfb719de..1ee3bb66 100644 --- a/plasma/applet/common/kgetappletutils.cpp +++ b/plasma/applet/common/kgetappletutils.cpp @@ -1,132 +1,131 @@ /*************************************************************************** * * * 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 2 of the License, or * * (at your option) any later version. * * * * Copyright (C) 2007 by Javier Goday * * Copyright (C) 2009 by Matthias Fuchs * * * * 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, write to the * * Free Software Foundation, Inc., * * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA . * ***************************************************************************/ #include "kgetappletutils.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include -#include const int KGetAppletUtils::SPACING = 4; void KGetAppletUtils::paintTitle(QPainter *p, Plasma::Svg *svg, const QRect &rect) { Q_UNUSED(svg) p->setRenderHint(QPainter::SmoothPixmapTransform); QFont font = Plasma::Theme::defaultTheme()->font(Plasma::Theme::DefaultFont); font.setBold(true); font.setPointSize(15); QFontMetrics metrics(font); p->setFont(font); p->setPen(Plasma::Theme::defaultTheme()->color(Plasma::Theme::TextColor)); QRect iconRect(QPoint(rect.x() + SPACING + 10, rect.y() + SPACING + 10), QSize(metrics.height(), metrics.height())); QIcon::fromTheme("kget").paint(p, iconRect); //p->drawPixmap(QPointF(rect.x() + SPACING + 10, rect.y() + SPACING + 10), QIcon::fromTheme("kget").pixmap(iconRect.width(), iconRect.height()), iconRect); //svg->paint(p, QRect(rect.x() + SPACING + 10, // rect.y() + SPACING + 10, 111, 35), "title"); //p->setPen(Qt::black); p->drawText(QRectF(rect.x() + SPACING * 2 + 10 + iconRect.width(), rect.y() + SPACING + 10, metrics.width(i18n("KGet")), metrics.height()), i18n("KGet")); p->drawLine(QPointF(rect.x() + SPACING + 10, rect.y() + SPACING * 2 + 10 + metrics.height()), QPointF(rect.width() - SPACING - 10, rect.y() + SPACING * 2 + 10 + metrics.height())); //svg->paint(p, QRect(rect.x() + SPACING + 10, // rect.y() + SPACING + 45, // rect.width() - (SPACING + 10) * 2, 1), "line"); } QGraphicsWidget *KGetAppletUtils::createErrorWidget(const QString &message, QGraphicsWidget *parent) { return new ErrorWidget(message, parent); } /** Error widget **/ ErrorWidget::ErrorWidget(const QString &message, QGraphicsWidget *parent) : QGraphicsProxyWidget(parent) { m_interface = QDBusConnection::sessionBus().interface(); m_layout = new QGraphicsLinearLayout(this); m_layout->setOrientation(Qt::Vertical); m_errorLabel = new Plasma::Label(this); m_errorLabel->setText(message); m_errorLabel->nativeWidget()->setAlignment(Qt::AlignCenter); m_icon = new Plasma::IconWidget(QIcon::fromTheme("dialog-warning"),"", this); m_launchButton = new Plasma::PushButton(this); m_launchButton->setText(i18n("Launch KGet")); m_launchButton->nativeWidget()->setIcon(QIcon::fromTheme("kget")); m_layout->addItem(m_errorLabel); m_layout->addItem(m_icon); m_layout->addItem(m_launchButton); setLayout(m_layout); connect(m_launchButton, SIGNAL(clicked()), SLOT(launchKGet())); } ErrorWidget::~ErrorWidget() { delete m_errorLabel; delete m_icon; delete m_launchButton; } void ErrorWidget::launchKGet() { QProcess kgetProcess; kgetProcess.startDetached("kget"); checkKGetStatus(); } void ErrorWidget::checkKGetStatus() { if (m_interface->isServiceRegistered("org.kde.kget")) { emit kgetStarted(); } else { QTimer::singleShot(1000, this, SLOT(checkKGetStatus())); } }