diff --git a/CMakeLists.txt b/CMakeLists.txt index a35d32b4..95c01c49 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -1,335 +1,333 @@ cmake_minimum_required(VERSION 3.0) # KDE Application Version, managed by release script set(KDE_APPLICATIONS_VERSION_MAJOR "19") set(KDE_APPLICATIONS_VERSION_MINOR "07") set(KDE_APPLICATIONS_VERSION_MICRO "70") set(KDE_APPLICATIONS_VERSION "${KDE_APPLICATIONS_VERSION_MAJOR}.${KDE_APPLICATIONS_VERSION_MINOR}.${KDE_APPLICATIONS_VERSION_MICRO}") project(kget VERSION ${KDE_APPLICATIONS_VERSION}) set(KF5_VERSION "5.44.0") set(REQUIRED_QT_VERSION "5.7.0") find_package(ECM ${KF5_VERSION} REQUIRED NO_MODULE) set(CMAKE_MODULE_PATH ${CMAKE_MODULE_PATH} ${ECM_MODULE_PATH} ${ECM_KDE_MODULE_DIR} "${CMAKE_SOURCE_DIR}/cmake") find_package(Qt5 ${REQUIRED_QT_VERSION} REQUIRED CONFIG COMPONENTS DBus Gui Network Sql Test Widgets Xml) include(KDEInstallDirs) include(KDECMakeSettings) include(KDECompilerSettings NO_POLICY_SCOPE) include(ECMInstallIcons) include(ECMMarkAsTest) include(GenerateExportHeader) include(ECMQtDeclareLoggingCategory) include(ECMSetupVersion) ecm_setup_version(PROJECT VARIABLE_PREFIX KGET VERSION_HEADER "kget_version.h") find_package(KF5 ${KF5_VERSION} REQUIRED Completion Config ConfigWidgets CoreAddons + Crash DBusAddons DocTools I18n IconThemes ItemViews KCMUtils - KDELibs4Support KIO Notifications NotifyConfig Parts Service - Solid TextWidgets Wallet WidgetsAddons WindowSystem XmlGui + KDELibs4Support # KLocale ) -#TODO: Check if we need all the dependencies - #include(CheckIncludeFile) #include(CheckIncludeFiles) #include(CheckSymbolExists) #include(CheckFunctionExists) #include(CheckLibraryExists) #include(CheckPrototypeExists) #include(CheckTypeSize) #set(CMAKE_REQUIRED_DEFINITIONS ${_KDE4_PLATFORM_DEFINITIONS}) if(WIN32) set(CMAKE_REQUIRED_LIBRARIES ${KDEWIN32_LIBRARIES}) set(CMAKE_REQUIRED_INCLUDES ${KDEWIN32_INCLUDES}) endif(WIN32) #add_definitions(${QT_DEFINITIONS} ${QT_QTDBUS_DEFINITIONS} ${KDE4_DEFINITIONS}) #add_definitions(-DQT_USE_FAST_CONCATENATION -DQT_USE_FAST_OPERATOR_PLUS) include_directories(${CMAKE_SOURCE_DIR} ${CMAKE_BINARY_DIR}) set(CMAKE_MODULE_PATH ${CMAKE_CURRENT_SOURCE_DIR}/cmake/modules ${CMAKE_MODULE_PATH}) find_package(Sqlite QUIET) set_package_properties(SQLITE_FOUND PROPERTIES DESCRIPTION "SQLite is a Binary-Database" TYPE RECOMMENDED PURPOSE "Needed for the SQLite-Backend of the KGet-History") find_package(Qca-qt5 2.1.0) set_package_properties(Qca PROPERTIES DESCRIPTION "Support for encryption" URL "http://delta.affinix.com/qca" TYPE REQUIRED) find_package(Gpgmepp 1.7.0) set_package_properties(Gpgmepp_FOUND PROPERTIES DESCRIPTION "The GpgME++ library" URL "http://www.gnupg.org" TYPE RECOMMENDED PURPOSE "GpgME++ is required to have signature verifying support in KGet.") find_package(KF5Torrent 2.1) set_package_properties(KF5Torrent_FOUND PROPERTIES DESCRIPTION "Backend Library of KTorrent" TYPE RECOMMENDED PURPOSE "Needed to build KGet bittorrent support.") find_package(LibMms QUIET) set_package_properties(LIBMMS_FOUND PROPERTIES DESCRIPTION "Library to read mms streams" TYPE RECOMMENDED PURPOSE "Needed to build KGet mms support.") set(kget_adaptor_SRCS dbus/dbuskgetwrapper.cpp ) # set(kget_transfer_adaptor_SRCS # core/transferhandler.cpp # core/transfertreemodel.cpp # ) #add_definitions(-DKDE_DEFAULT_DEBUG_AREA=5001) //TODO: PORT THIS!! if(CMAKE_BUILD_TYPE MATCHES debugfull) add_definitions(-DDEBUG) endif(CMAKE_BUILD_TYPE MATCHES debugfull) if (Qca-qt5_FOUND) add_definitions(-DHAVE_QCA2) MESSAGE(STATUS "QCA found") endif() find_package(Boost REQUIRED) if(Gpgmepp_FOUND) find_package(QGpgme REQUIRED) add_definitions(-DHAVE_QGPGME) endif() set (KGET_PLUGIN_INSTALL_DIR ${PLUGIN_INSTALL_DIR}/kget) add_subdirectory(conf) add_subdirectory(core) add_subdirectory(ui) add_subdirectory(transfer-plugins) add_subdirectory(extensions) add_subdirectory(sounds) add_subdirectory(desktop) #add_subdirectory(plasma) TODO PORT TO KF5/QML add_subdirectory(tests) add_subdirectory(doc) find_package(LibKWorkspace CONFIG) set_package_properties(LibKWorkspace PROPERTIES TYPE OPTIONAL PURPOSE "Allows 'shutdown after downloads completed' in kget") if(LibKWorkspace_FOUND) add_definitions(-DHAVE_KWORKSPACE) endif() include_directories( ${CMAKE_CURRENT_SOURCE_DIR} ${CMAKE_CURRENT_SOURCE_DIR}/core/ ${CMAKE_CURRENT_BINARY_DIR}/core/ ${CMAKE_CURRENT_BINARY_DIR} ) if(SQLITE_FOUND) add_definitions(-DHAVE_SQLITE) endif(SQLITE_FOUND) remove_definitions(-DQT_NO_HTTP) # kgetcore set(kgetcore_SRCS core/job.cpp core/jobqueue.cpp core/kget.cpp core/scheduler.cpp core/transfertreemodel.cpp core/transfertreeselectionmodel.cpp core/transfer.cpp core/transfergroup.cpp core/transfergrouphandler.cpp core/transferhandler.cpp core/handler.cpp core/transfergroupscheduler.cpp core/plugin/plugin.cpp core/plugin/transferfactory.cpp core/transferdatasource.cpp core/datasourcefactory.cpp core/kgetkjobadapter.cpp core/kuiserverjobs.cpp core/kgetglobaljob.cpp core/bitset.cpp core/download.cpp core/transferhistorystore.cpp core/transferhistorystore_xml.cpp core/transferhistorystore_sqlite.cpp core/linkimporter.cpp dbus/dbustransferwrapper.cpp dbus/dbusverifierwrapper.cpp core/filemodel.cpp core/verifier.cpp core/verificationthread.cpp core/verificationmodel.cpp core/verificationdelegate.cpp core/signature.cpp core/signaturethread.cpp core/keydownloader.cpp core/urlchecker.cpp core/basedialog.cpp core/mostlocalurl.cpp core/filedeleter.cpp ) ecm_qt_declare_logging_category(kgetcore_SRCS HEADER kget_debug.h IDENTIFIER KGET_DEBUG CATEGORY_NAME kget) kconfig_add_kcfg_files(kgetcore_SRCS conf/settings.kcfgc) qt5_add_dbus_adaptor(kgetcore_SRCS dbus/org.kde.kget.transfer.xml dbus/dbustransferwrapper.h DBusTransferWrapper) qt5_add_dbus_adaptor(kgetcore_SRCS dbus/org.kde.kget.verifier.xml dbus/dbusverifierwrapper.h DBusVerifierWrapper) add_library(kgetcore SHARED ${kgetcore_SRCS}) generate_export_header(kgetcore BASE_NAME kget) -target_link_libraries(kgetcore Qt5::Core Qt5::Widgets Qt5::Sql Qt5::Network Qt5::DBus KF5::DBusAddons KF5::I18n KF5::CoreAddons KF5::IconThemes KF5::KDELibs4Support) +target_link_libraries(kgetcore Qt5::Core Qt5::Widgets Qt5::Sql Qt5::Network Qt5::DBus KF5::ConfigGui KF5::CoreAddons KF5::DBusAddons KF5::I18n KF5::IconThemes KF5::KIOCore KF5::KIOWidgets KF5::Notifications KF5::Service KF5::XmlGui) if(LibKWorkspace_FOUND) target_link_libraries(kgetcore PW::KWorkspace) endif() if (SQLITE_FOUND) target_link_libraries(kgetcore ${QT_QTSQL_LIBRARY}) endif (SQLITE_FOUND) if (Qca-qt5_FOUND) target_link_libraries(kgetcore qca-qt5) endif () if (Gpgmepp_FOUND) target_link_libraries(kgetcore QGpgme Gpgmepp) kde_enable_exceptions() endif() set_target_properties(kgetcore PROPERTIES VERSION 5.0.0 SOVERSION 5 ) install(TARGETS kgetcore ${INSTALL_TARGETS_DEFAULT_ARGS}) # kget set(kget_SRCS ${kget_adaptor_SRCS} ${kget_transfer_adaptor_SRCS} kget_debug.cpp conf/autopastemodel.cpp conf/integrationpreferences.cpp conf/dlgwebinterface.cpp conf/preferencesdialog.cpp conf/transfersgrouptree.cpp conf/transfersgroupwidget.cpp conf/pluginselector.cpp conf/verificationpreferences.cpp ui/droptarget.cpp ui/transfersview.cpp ui/transfersviewdelegate.cpp ui/transferdetails.cpp ui/viewscontainer.cpp ui/newtransferdialog.cpp ui/groupsettingsdialog.cpp ui/transfersettingsdialog.cpp ui/contextmenu.cpp ui/tray.cpp ui/history/rangetreewidget.cpp ui/history/transferhistory.cpp ui/history/transferhistoryitemdelegate.cpp ui/history/transferhistorycategorizeddelegate.cpp ui/history/transferhistorycategorizedview.cpp ui/linkview/kget_linkview.cpp ui/linkview/kget_sortfilterproxymodel.cpp ui/mirror/mirrorsettings.cpp ui/mirror/mirrormodel.cpp ui/renamefile.cpp ui/verificationdialog.cpp ui/metalinkcreator/metalinkcreator.cpp ui/metalinkcreator/generalwidget.cpp ui/metalinkcreator/metalinker.cpp ui/metalinkcreator/filedlg.cpp ui/metalinkcreator/localemodels.cpp ui/metalinkcreator/dragdlg.cpp ui/metalinkcreator/urlwidget.cpp ui/metalinkcreator/filehandler.cpp ui/signaturedlg.cpp mainwindow.cpp main.cpp #extensions/webinterface/httpserver.cpp ) qt5_add_dbus_adaptor(kget_SRCS dbus/org.kde.kget.main.xml dbus/dbuskgetwrapper.h DBusKGetWrapper) ki18n_wrap_ui(kget_SRCS conf/dlgadvanced.ui conf/dlgappearance.ui conf/dlggroups.ui conf/dlgintegration.ui conf/dlgwebinterface.ui conf/dlgnetwork.ui conf/verificationpreferences.ui ui/transferdetailsfrm.ui ui/newtransferwidget.ui ui/history/transferhistory.ui ui/groupsettingsdialog.ui ui/transfersettingsdialog.ui ui/linkview/importlinkdialog.ui ui/mirror/mirrorsettings.ui ui/mirror/mirroradddlg.ui ui/renamefile.ui ui/verificationdialog.ui ui/verificationadddlg.ui ui/metalinkcreator/introduction.ui ui/metalinkcreator/generalwidget.ui ui/metalinkcreator/files.ui ui/metalinkcreator/filedlg.ui ui/metalinkcreator/dragdlg.ui ui/metalinkcreator/urlwidget.ui ui/metalinkcreator/commondata.ui ui/signaturedlg.ui ) #kde4_add_app_icon(kget_SRCS "ui/icons/hi*-apps-kget.png") add_executable(kget ${kget_SRCS}) -target_link_libraries(kget Qt5::Core Qt5::Widgets Qt5::Sql Qt5::Network Qt5::DBus KF5::Wallet KF5::DBusAddons KF5::I18n KF5::CoreAddons KF5::IconThemes KF5::KCMUtils KF5::NotifyConfig KF5::KDELibs4Support kgetcore) +target_link_libraries(kget Qt5::Core Qt5::Widgets Qt5::Sql Qt5::Network Qt5::DBus KF5::Crash KF5::Wallet KF5::DBusAddons KF5::I18n KF5::Completion KF5::CoreAddons KF5::IconThemes KF5::ItemViews KF5::KCMUtils KF5::NotifyConfig KF5::WindowSystem KF5::KDELibs4Support kgetcore) if(Qca_FOUND) target_link_libraries(kget qca) endif() if(CMAKE_BUILD_TYPE MATCHES debugfull) target_link_libraries(kget Qt5::Test) endif() if (QGpgme_FOUND) target_link_libraries(kget QGpgme) endif() install(TARGETS kget ${INSTALL_TARGETS_DEFAULT_ARGS}) install(FILES kget.categories DESTINATION ${KDE_INSTALL_CONFDIR}) install(FILES org.kde.kget.appdata.xml DESTINATION ${CMAKE_INSTALL_METAINFODIR}) feature_summary(WHAT ALL FATAL_ON_MISSING_REQUIRED_PACKAGES) diff --git a/conf/dlgadvanced.ui b/conf/dlgadvanced.ui index 4ec5ce55..a893836e 100644 --- a/conf/dlgadvanced.ui +++ b/conf/dlgadvanced.ui @@ -1,163 +1,163 @@ DlgAdvanced 0 0 585 443 Enable system tray icon Execute action after all downloads have been finished: Quit KGet At startup: Restore Download State Start All Downloads Stop All Downloads History backend: Enable KDE Global Progress Tracking true false Show every single download Show overall progress Handle existing Files/Transfers Always ask Automatic rename Overwrite Qt::Vertical QSizePolicy::Expanding 200 20 KComboBox QComboBox -
kcombobox.h
+
KComboBox
diff --git a/conf/dlgintegration.ui b/conf/dlgintegration.ui index 8ae2291c..2bbeb939 100644 --- a/conf/dlgintegration.ui +++ b/conf/dlgintegration.ui @@ -1,140 +1,140 @@ DlgIntegration 0 0 558 324 Use as download manager for Konqueror Monitor Clipboard for Files to Download true false Case sensitive: true true QAbstractItemView::ExtendedSelection false true &Increase Priority &Decrease Priority Qt::Vertical 20 40 KComboBox QComboBox -
kcombobox.h
+
KComboBox
KLineEdit QLineEdit -
klineedit.h
+
KLineEdit
diff --git a/conf/dlgwebinterface.ui b/conf/dlgwebinterface.ui index effd7f89..0912a1df 100644 --- a/conf/dlgwebinterface.ui +++ b/conf/dlgwebinterface.ui @@ -1,101 +1,101 @@ DlgWebinterface 0 0 400 389 0 Enable Web Interface true false Port: 100000 User: true Password: true true Qt::Vertical 20 40 KLineEdit QLineEdit -
klineedit.h
+
KLineEdit
diff --git a/conf/kget.kcfg b/conf/kget.kcfg index ebdc81ad..f21ab80a 100644 --- a/conf/kget.kcfg +++ b/conf/kget.kcfg @@ -1,200 +1,198 @@ - KGlobalSettings - true false false false true false false false * 0 0 false QDateTime::currentDateTime() false QDateTime::currentDateTime() true false 0 false true false 1 0 true false false 2 false 20 0 0 true 5 60 false 8080 admin false true 0 QPoint(-1, -1) 0 true QStandardPaths::writableLocation(QStandardPaths::DownloadLocation) 0 1 1 1 1 http://keys.gnupg.net,http://stinkfoot.org,http://pgp.mit.edu,http://pgp.surfnet.nl,http://keyserver.gingerbear.net diff --git a/conf/preferencesdialog.cpp b/conf/preferencesdialog.cpp index d91743d1..35303163 100644 --- a/conf/preferencesdialog.cpp +++ b/conf/preferencesdialog.cpp @@ -1,106 +1,105 @@ /* This file is part of the KDE project Copyright (C) 2004 - 2007 KGet Developers 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 "preferencesdialog.h" #include "core/kget.h" #include "core/transferhistorystore.h" #include "ui_dlgappearance.h" #include "ui_dlgnetwork.h" #include "dlgwebinterface.h" #include "integrationpreferences.h" #include "transfersgroupwidget.h" #include "pluginselector.h" #include "verificationpreferences.h" #include -#include -#include -#include +#include +#include PreferencesDialog::PreferencesDialog(QWidget * parent, KConfigSkeleton * skeleton) : KConfigDialog(parent, "preferences", skeleton) { QWidget *appearance = new QWidget(this); TransfersGroupWidget *groups = new TransfersGroupWidget(this); DlgWebinterface *webinterface = new DlgWebinterface(this); connect(webinterface, &DlgWebinterface::changed, this, &PreferencesDialog::enableApplyButton); connect(webinterface, &DlgWebinterface::saved, this, &PreferencesDialog::settingsChangedSlot); QWidget *network = new QWidget(this); QWidget *advanced = new QWidget(this); IntegrationPreferences *integration = new IntegrationPreferences(this); connect(integration, &IntegrationPreferences::changed, this, &PreferencesDialog::enableApplyButton); VerificationPreferences *verification = new VerificationPreferences(this); connect(verification, &VerificationPreferences::changed, this, &PreferencesDialog::enableApplyButton); PluginSelector * pluginSelector = new PluginSelector(this); connect(pluginSelector, &PluginSelector::changed, this, &PreferencesDialog::enableApplyButton); Ui::DlgAppearance dlgApp; Ui::DlgNetwork dlgNet; dlgApp.setupUi(appearance); dlgNet.setupUi(network); dlgAdv.setupUi(advanced); // history backend entries dlgAdv.kcfg_HistoryBackend->addItem(i18n("Xml"), QVariant(TransferHistoryStore::Xml)); #ifdef HAVE_SQLITE dlgAdv.kcfg_HistoryBackend->addItem(i18n("Sqlite"), QVariant(TransferHistoryStore::SQLite)); #endif #ifdef HAVE_KWORKSPACE dlgAdv.kcfg_AfterFinishAction->addItem(i18n("Turn Off Computer"), QVariant(KGet::Shutdown)); dlgAdv.kcfg_AfterFinishAction->addItem(i18n("Hibernate Computer"), QVariant(KGet::Hibernate)); dlgAdv.kcfg_AfterFinishAction->addItem(i18n("Suspend Computer"), QVariant(KGet::Suspend)); #endif // enable or disable the AfterFinishAction depends on the AfterFinishActionEnabled checkbox state dlgAdv.kcfg_AfterFinishAction->setEnabled(dlgAdv.kcfg_AfterFinishActionEnabled->checkState () == Qt::Checked); connect(dlgAdv.kcfg_AfterFinishActionEnabled, &QCheckBox::stateChanged, this, &PreferencesDialog::slotToggleAfterFinishAction); // TODO: remove the following lines as soon as these features are ready dlgNet.lb_per_transfer->setVisible(false); dlgNet.kcfg_TransferSpeedLimit->setVisible(false); addPage(appearance, i18n("Appearance"), "preferences-desktop-theme", i18n("Change appearance settings")); addPage(groups, i18n("Groups"), "bookmarks", i18n("Manage the groups")); addPage(network, i18n("Network"), "network-workgroup", i18n("Network and Downloads")); addPage(webinterface, i18n("Web Interface"), "network-workgroup", i18n("Control KGet over a Network or the Internet")); addPage(verification, i18n("Verification"), "document-encrypt", i18n("Verification")); addPage(integration, i18nc("integration of KGet with other applications", "Integration"), "konqueror", i18nc("integration of KGet with other applications", "Integration")); addPage(advanced, i18nc("Advanced Options", "Advanced"), "preferences-other", i18n("Advanced Options")); addPage(pluginSelector, i18n("Plugins"), "preferences-plugin", i18n("Transfer Plugins")); connect(this, &PreferencesDialog::accepted, this, &PreferencesDialog::disableApplyButton); connect(this, &PreferencesDialog::rejected, this, &PreferencesDialog::disableApplyButton); } void PreferencesDialog::disableApplyButton() { button(QDialogButtonBox::Apply)->setEnabled(false); } void PreferencesDialog::enableApplyButton() { button(QDialogButtonBox::Apply)->setEnabled(true); } void PreferencesDialog::slotToggleAfterFinishAction(int state) { dlgAdv.kcfg_AfterFinishAction->setEnabled(state == Qt::Checked); } void PreferencesDialog::updateWidgetsDefault() { emit resetDefaults(); KConfigDialog::updateWidgetsDefault(); } diff --git a/conf/transfersgrouptree.cpp b/conf/transfersgrouptree.cpp index ee8fe85a..ca104f04 100644 --- a/conf/transfersgrouptree.cpp +++ b/conf/transfersgrouptree.cpp @@ -1,154 +1,154 @@ /* This file is part of the KDE project Copyright (C) 2005 Dario Massarin Copyright (C) 2007 Urs Wolfer Copyright (C) 2007 Javier Goday Copyright (C) 2009 Lukas Appelhans Copyright (C) 2010 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 "transfersgrouptree.h" #include "core/kget.h" #include "core/transfertreemodel.h" #include "core/transfertreeselectionmodel.h" #include "kget_debug.h" -#include +#include #include #include TransfersGroupDelegate::TransfersGroupDelegate(QAbstractItemView *parent) : BasicTransfersViewDelegate(parent) { } QWidget *TransfersGroupDelegate::createEditor(QWidget *parent, const QStyleOptionViewItem &option, const QModelIndex &index) const { if (index.column() == TransferTreeModel::Name) { return new KLineEdit(parent); } else { return BasicTransfersViewDelegate::createEditor(parent, option, index); } } void TransfersGroupDelegate::setEditorData(QWidget *editor, const QModelIndex &index) const { if (index.column() == TransferTreeModel::Name) { KLineEdit *groupEditor = static_cast(editor); groupEditor->setText(index.data().toString()); } else { BasicTransfersViewDelegate::setEditorData(editor, index); } } void TransfersGroupDelegate::setModelData(QWidget *editor, QAbstractItemModel *model, const QModelIndex &index) const { if (index.column() == TransferTreeModel::Name) { KLineEdit *groupEditor = static_cast(editor); const QString newName = groupEditor->text(); const QString oldName = index.data().toString(); if (!newName.isEmpty()) { foreach (const QString &groupName, KGet::transferGroupNames()) { if (groupName == newName && groupName != oldName) { groupEditor->setText(oldName); return; } } KGet::renameGroup(oldName, newName); } } else { BasicTransfersViewDelegate::setModelData(editor, model, index); } } TransfersGroupTree::TransfersGroupTree(QWidget *parent) : QTreeView(parent) { setItemDelegate(new TransfersGroupDelegate(this)); } void TransfersGroupTree::setModel(QAbstractItemModel *model) { QTreeView::setModel(model); int nGroups = model->rowCount(QModelIndex()); for (int i = 0; i < nGroups; i++) { qCDebug(KGET_DEBUG) << "openEditor for row " << i; openPersistentEditor(model->index(i, TransferTreeModel::Status, QModelIndex())); } setColumnWidth(0 , 250); } void TransfersGroupTree::rowsInserted(const QModelIndex &parent, int start, int end) { if (!parent.isValid()) { for (int i = start; i <= end; ++i) { qCDebug(KGET_DEBUG) << "openEditor for row " << i; openPersistentEditor(model()->index(i, TransferTreeModel::Status, parent)); } } QTreeView::rowsInserted(parent, start, end); } void TransfersGroupTree::editCurrent() { QTreeView::edit(currentIndex()); } void TransfersGroupTree::addGroup() { QString groupName(i18n("New Group")); int i=0; while(KGet::transferGroupNames().contains(groupName)) { groupName = i18n("New Group") + QString::number(++i); } if (KGet::addGroup(groupName)) { QModelIndex index = model()->index(model()->rowCount() - 1, 0); setCurrentIndex(index); editCurrent(); } } void TransfersGroupTree::deleteSelectedGroup() { KGet::delGroups(KGet::selectedTransferGroups()); } void TransfersGroupTree::renameSelectedGroup() { if(currentIndex().isValid()) editCurrent(); } void TransfersGroupTree::changeIcon(const QString &icon) { qCDebug(KGET_DEBUG); TransferTreeSelectionModel *selModel = KGet::selectionModel(); QModelIndexList indexList = selModel->selectedRows(); if (!icon.isEmpty()) { foreach (TransferGroupHandler *group, KGet::selectedTransferGroups()) { group->setIconName(icon); } } emit dataChanged(indexList.first(),indexList.last()); } diff --git a/core/download.cpp b/core/download.cpp index f689e190..842d20d2 100644 --- a/core/download.cpp +++ b/core/download.cpp @@ -1,75 +1,75 @@ /* This file is part of the KDE project Copyright (C) 2007 Lukas Appelhans 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 "download.h" #include #include "kget_debug.h" -#include +#include Download::Download(const QUrl &srcUrl, const QUrl &destUrl) : m_srcUrl(srcUrl), m_destUrl(destUrl) { qCDebug(KGET_DEBUG) << "DownloadFile: " << m_srcUrl.url() << " to dest: " << m_destUrl.url(); m_copyJob = KIO::get(m_srcUrl, KIO::NoReload, KIO::HideProgressInfo); connect(m_copyJob, SIGNAL(data(KIO::Job*,QByteArray)), SLOT(slotData(KIO::Job*,QByteArray))); connect(m_copyJob, SIGNAL(result(KJob*)), SLOT(slotResult(KJob*))); } Download::~Download() { } void Download::slotData(KIO::Job *job, const QByteArray& data) { Q_UNUSED(job) qCDebug(KGET_DEBUG); /**if (data.size() == 0) { slotResult(job); return; }**/ m_data.append(data); } void Download::slotResult(KJob * job) { qCDebug(KGET_DEBUG); switch (job->error()) { case 0://The download has finished { qCDebug(KGET_DEBUG) << "Downloading successfully finished" << m_destUrl.url(); QFile torrentFile(m_destUrl.toLocalFile()); if (!torrentFile.open(QIODevice::WriteOnly | QIODevice::Text)) {} //TODO: Do a Message box here torrentFile.write(m_data); torrentFile.close(); emit finishedSuccessfully(m_destUrl, m_data); m_data = 0; break; } case KIO::ERR_FILE_ALREADY_EXIST: { qCDebug(KGET_DEBUG) << "ERROR - File already exists"; QFile file(m_destUrl.toLocalFile()); emit finishedSuccessfully(m_destUrl, file.readAll()); m_data = 0; break; } default: qCDebug(KGET_DEBUG) << "We are sorry to say you, that there were errors while downloading :("; m_data = 0; emit finishedWithError(); break; } } diff --git a/core/job.cpp b/core/job.cpp index aef08b03..d2c400d6 100644 --- a/core/job.cpp +++ b/core/job.cpp @@ -1,77 +1,77 @@ /* This file is part of the KDE project Copyright (C) 2005 Dario Massarin Copyright (C) 2009 Lukas Appelhans 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/job.h" #include "core/jobqueue.h" #include "core/scheduler.h" #include "kget_debug.h" -#include +#include Job::Job(Scheduler * scheduler, JobQueue * parent) : QObject(parent), m_jobQueue(parent), m_scheduler(scheduler), m_status(Stopped), m_policy(None) { m_error.id = -1; m_error.type = AutomaticRetry; } Job::~Job() { } void Job::setStatus(Status jobStatus) { if(jobStatus == m_status) return; if (m_status == Aborted) { m_error.id = -1; m_error.text.clear(); m_error.pixmap = QPixmap(); m_error.type = AutomaticRetry; } m_status = jobStatus; m_scheduler->jobChangedEvent(this, m_status); } void Job::setStartStatus(Status jobStatus) { qCDebug(KGET_DEBUG) << "Setting start status to " << jobStatus; m_startStatus = jobStatus; } void Job::setPolicy(Policy jobPolicy) { if(jobPolicy == m_policy) return; qCDebug(KGET_DEBUG) << "Job::setPolicy(" << jobPolicy << ")"; m_policy = jobPolicy; m_scheduler->jobChangedEvent(this, m_policy); } void Job::setError(const QString &text, const QPixmap &pixmap, ErrorType type, int errorId) { setStatus(Job::Aborted); m_error.id = errorId; m_error.text = text; m_error.pixmap = pixmap; m_error.type = type; } void Job::resolveError(int errorId) { Q_UNUSED(errorId) } diff --git a/core/jobqueue.cpp b/core/jobqueue.cpp index 9b51036b..2712020e 100644 --- a/core/jobqueue.cpp +++ b/core/jobqueue.cpp @@ -1,149 +1,149 @@ /* This file is part of the KDE project Copyright (C) 2004 Dario Massarin 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/jobqueue.h" #include "core/scheduler.h" #include "settings.h" #include "kget_debug.h" -#include +#include JobQueue::JobQueue(Scheduler * parent) : QObject(parent), m_maxSimultaneousJobs(2), m_scheduler(parent), m_status(Running) { } JobQueue::~JobQueue() { } const QList JobQueue::runningJobs() { QList jobs; iterator it = begin(); iterator itEnd = end(); for( ; it!=itEnd ; ++it ) { if( (*it)->status() == Job::Running ) jobs.append(*it); } return jobs; } void JobQueue::setStatus(Status queueStatus) { m_status = queueStatus; // Now make sure to reset all the job policy that shouldn't // be applied anymore. iterator it = begin(); iterator itEnd = end(); for( ; it!=itEnd ; ++it ) { if( ( m_status == JobQueue::Running ) && ( (*it)->status() == Job::Running ) ) { (*it)->setPolicy(Job::None); } if( ( m_status == JobQueue::Stopped ) && ( (*it)->status() == Job::Stopped ) ) { (*it)->setPolicy(Job::None); } } m_scheduler->jobQueueChangedEvent(this, m_status); } int JobQueue::maxSimultaneousJobs() const { const int maxConnections = Settings::maxConnections(); return (maxConnections ? maxConnections : 1000);// High value just to indicate no limit } void JobQueue::append(Job * job) { m_jobs.append(job); m_scheduler->jobQueueAddedJobEvent(this, job); } void JobQueue::append(const QList &jobs) { m_jobs.append(jobs); m_scheduler->jobQueueAddedJobsEvent(this, jobs); } void JobQueue::prepend(Job * job) { m_jobs.prepend(job); m_scheduler->jobQueueAddedJobEvent(this, job); } void JobQueue::insert(Job * job, Job * after) { if((job->jobQueue() == this) || ((after) && (after->jobQueue() != this))) return; m_jobs.insert(m_jobs.indexOf(after) +1, job); m_scheduler->jobQueueAddedJobEvent(this, job); } void JobQueue::remove(Job * job) { m_jobs.removeAll(job); m_scheduler->jobQueueRemovedJobEvent(this, job); } void JobQueue::remove(const QList jobs) { foreach (Job *job, jobs) { m_jobs.removeAll(job); } m_scheduler->jobQueueRemovedJobsEvent(this, jobs); } void JobQueue::move(Job * job, Job * after) { qCDebug(KGET_DEBUG) << "JobQueue::move"; if( (m_jobs.removeAll(job) == 0) || (job == after) || ((after) && (after->jobQueue() != this)) ) { //The job doesn't belong to this JobQueue or the requested //operations doesn't make any sense since job==after return; } if(!after) { //The job must be inserted in front of the list m_jobs.prepend(job); } else { m_jobs.insert(m_jobs.indexOf(after) + 1, job); } m_scheduler->jobQueueMovedJobEvent(this, job); } diff --git a/core/keydownloader.cpp b/core/keydownloader.cpp index f1da0fcb..afb23751 100644 --- a/core/keydownloader.cpp +++ b/core/keydownloader.cpp @@ -1,186 +1,186 @@ /************************************************************************** * Copyright (C) 2009-2011 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. * * * * 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 "keydownloader.h" #include "settings.h" #include "signature_p.h" #include "kget_debug.h" -#include +#include #include #include #include #ifdef HAVE_QGPGME #include #include #include #include #endif KeyDownloader::KeyDownloader(QObject *parent) : QObject(parent) { } bool KeyDownloader::isValid() const { #ifdef HAVE_QGPGME return true; #else //HAVE_QGPGME return false; #endif //HAVE_QGPGME } void KeyDownloader::downloadKey(QString fingerprint, Signature *sig) { downloadKey(fingerprint, sig, false); } void KeyDownloader::downloadKey(QString fingerprint, Signature *sig, bool mirrorFailed) { if (fingerprint.isEmpty() || (!sig && !mirrorFailed)) { return; } if (!fingerprint.startsWith(QLatin1String("0x"))) { fingerprint = "0x" + fingerprint; } if (m_downloading.contains(fingerprint) && !mirrorFailed) { if (!m_downloading.contains(fingerprint, sig)) { m_downloading.insert(fingerprint, sig); } } else { const QStringList servers = Settings::signatureKeyServers(); if (!servers.count()) { KMessageBox::error(nullptr, i18n("No server for downloading keys is specified in settings. Downloading aborted."), i18n("No key server")); return; } QString mirror; if (mirrorFailed) { const QStringList failedMirrors = m_triedMirrors.values(fingerprint); for (int i = 0; i < servers.count(); ++i) { if (!m_triedMirrors.contains(fingerprint, servers.at(i))) { mirror = servers.at(i); break; } } } else { mirror = servers.first(); } if (mirror.isEmpty()) { KMessageBox::error(0, i18n("No useful key server found, key not downloaded. Add more servers to the settings or restart KGet and retry downloading."), i18n("No key server")); return; } m_triedMirrors.insert(fingerprint, mirror); if (!mirrorFailed) { m_downloading.insert(fingerprint, sig); } QUrl url; url.setPath(mirror + "pks/lookup"); url.setQuery("op=get&options=mr&search=" + fingerprint); url.setPort(11371); qCDebug(KGET_DEBUG) << "Dowloading:" << url; KIO::StoredTransferJob *job = KIO::storedGet(url, KIO::Reload, KIO::HideProgressInfo); m_jobs[job] = fingerprint; connect(job, SIGNAL(finished(KJob*)), this, SLOT(slotDownloaded(KJob*))); } } void KeyDownloader::slotDownloaded(KJob *job) { #ifdef HAVE_QGPGME if (!m_jobs.contains(job)) { return; } const QString fingerprint = m_jobs[job]; KIO::StoredTransferJob *transferJob = static_cast(job); if (transferJob->isErrorPage()) { qCDebug(KGET_DEBUG) << "Mirror did not work, try another one."; downloadKey(fingerprint, 0, true); return; } QByteArray data = transferJob->data(); if (data.isEmpty()) { qCDebug(KGET_DEBUG) << "Downloaded data is empty."; downloadKey(fingerprint, 0, true); return; } const int indexStart = data.indexOf("
");
     const int indexEnd = data.indexOf("
", indexStart); if ((indexStart == -1) || (indexEnd == -1)) { qCDebug(KGET_DEBUG) << "Could not find a key."; downloadKey(fingerprint, 0, true); return; } data = data.mid(indexStart + 6, indexEnd - indexStart - 6); GpgME::initializeLibrary(); GpgME::Error err = GpgME::checkEngine(GpgME::OpenPGP); if (err) { qCDebug(KGET_DEBUG) << "Problem checking the engine."; return; } QScopedPointer context(GpgME::Context::createForProtocol(GpgME::OpenPGP)); if (!context.data()) { qCDebug(KGET_DEBUG) << "Could not create context."; return; } QGpgME::QByteArrayDataProvider keyBA(data); GpgME::Data key(&keyBA); GpgME::ImportResult importResult = context->importKeys(key); err = importResult.error(); if (err) { qCDebug(KGET_DEBUG) << "Error while importing key.";; return; } qCDebug(KGET_DEBUG) << "Key downloaded, notifying requesters."; QList sigs = m_downloading.values(fingerprint); foreach (Signature *sig, sigs) { sig->d->signatureDownloaded(); } m_downloading.remove(fingerprint); #else //HAVE_QGPGME Q_UNUSED(job) qCWarning(KGET_DEBUG) << "No QGPGME support."; #endif //HAVE_QGPGME } diff --git a/core/kget.cpp b/core/kget.cpp index 1c992940..a4a7a932 100644 --- a/core/kget.cpp +++ b/core/kget.cpp @@ -1,1599 +1,1600 @@ /* 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 +#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::fromLocalFile(destDir); QString directory = targetUrl.adjusted(QUrl::RemoveFilename).path(); 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).path(); } while (!isValidDestDirectory(destDir)); } else { destUrl = QUrl::fromLocalFile(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::fromLocalFile(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(); QUrl src = transfer->source(); QString dest = transfer->dest().toLocalFile(); 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 = QStandardPaths::writableLocation(QStandardPaths::DataLocation); // make sure that the DataLocation directory exists (earlier this used to be handled by KStandardDirs) if (!QFileInfo::exists(filename)) { QDir().mkpath(filename); } filename += QStringLiteral("/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 = QStandardPaths::writableLocation(QStandardPaths::DataLocation); // make sure that the DataLocation directory exists (earlier this used to be handled by KStandardDirs) if (!QFileInfo::exists(filename)) { QDir().mkpath(filename); } filename += QStringLiteral("/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()) { qDebug() << 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() { qDebug(); 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 = QFileDialog::getExistingDirectory(nullptr, i18nc("@title:window", "Choose Directory"), generalDestDir(), QFileDialog::ShowDirsOnly | QFileDialog::DontResolveSymlinks); 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 = QFileDialog::getSaveFileUrl(m_mainWindow, i18nc("@title:window", "Save As"), startLocation, QString()); if (!destUrl.isEmpty()) { Settings::setLastDirectory(destUrl.adjusted(QUrl::RemoveFilename).path()); } 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) { qDebug() << "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]; qDebug() << "Resolve error for" << handler->source().toString() << "with id" << handler->error().id; handler->resolveError(handler->error().id); m_notifications.remove(notification); } } void GenericObserver::slotNotificationClosed() { qDebug() << "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; } } qDebug() << "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(QStringLiteral("org.freedesktop.PowerManagement"), QStringLiteral("/org/freedesktop/PowerManagement"), QStringLiteral("org.freedesktop.PowerManagement"), QStringLiteral("Hibernate")); QDBusConnection::sessionBus().asyncCall(call); break; } case KGet::Suspend: { QDBusMessage call; call = QDBusMessage::createMethodCall(QStringLiteral("org.freedesktop.PowerManagement"), QStringLiteral("/org/freedesktop/PowerManagement"), QStringLiteral("org.freedesktop.PowerManagement"), QStringLiteral("Suspend")); QDBusConnection::sessionBus().asyncCall(call); break; } #endif default: break; } } void GenericObserver::slotAbortAfterFinishAction() { qCDebug(KGET_DEBUG); m_finishAction->stop(); } diff --git a/core/kget.h b/core/kget.h index 0bc6c522..bd36a5dc 100644 --- a/core/kget.h +++ b/core/kget.h @@ -1,503 +1,501 @@ /* This file is part of the KDE project Copyright (C) 2005 Dario Massarin Copyright (C) 2009 Lukas Appelhans Copyright (C) 2009 Matthias Fuchs Based on: kmainwidget.{h,cpp} Copyright (C) 2002 by Patrick Charbonnier that was based On Caitoo v.0.7.3 (c) 1998 - 2000, Matej Koss 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. */ #ifndef KGET_H #define KGET_H -#include -#include -#include +#include +#include #include -#include -#include +#include #include #include #include #include "kuiserverjobs.h" #include "scheduler.h" #include "kget_export.h" #include "transfer.h" #include "transfergrouphandler.h" class QDomElement; class TransferDataSource; class TransferGroup; class TransferHandler; class TransferFactory; class TransferTreeModel; class TransferTreeSelectionModel; class KGetPlugin; class MainWindow; class NewTransferDialog; class TransferGroupScheduler; class TransferHistoryStore; /** * 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?). **/ class KGET_EXPORT KGet { friend class NewTransferDialog; friend class NewTransferDialogHandler; friend class GenericObserver; friend class TransferTreeModel; friend class UrlChecker; public: enum AfterFinishAction { Quit = 0, Shutdown = 1, Hibernate = 2, Suspend = 3 }; enum DeleteMode { AutoDelete, DeleteFiles }; ~KGet(); static KGet* self( MainWindow * mainWindow = nullptr ); /** * Adds a new group to the KGet. * * @param groupName The name of the new group * * @returns true if the group has been successully added, otherwise * it returns false, probably because a group with that named * already exists */ static bool addGroup(const QString& groupName); /** * Removes a group from the KGet. * * @param group The name of the group to be deleted * @param askUser Whether to ask user about the deletion */ static void delGroup(TransferGroupHandler * group, bool askUser = true); /** * Removes specific groups from the KGet. * * @param groups The names of the groups to be deleted. * @param askUser Whether to ask user about the deletion */ static void delGroups(QList groups, bool askUser = true); /** * Changes the name of the group * * @param oldName the name of the group to be changed * @param newName the new name of the group */ static void renameGroup(const QString& oldName, const QString& newName); /** * @returns the name of the available transfers groups */ static QStringList transferGroupNames(); /** * Adds a new transfer to the KGet * * @param srcUrl The url to be downloaded * @param destDir The destination directory. If empty we show a dialog * where the user can choose it. * @param suggestedFileName a suggestion of a simple filename to be saved in destDir * @param groupName The name of the group the new transfer will belong to * @param start Specifies if the newly added transfers should be started. * If the group queue is already in a running state, this flag does nothing */ static TransferHandler * addTransfer(QUrl srcUrl, QString destDir = QString(), QString suggestedFileName = QString(), QString groupName = QString(), bool start = false); /** * Adds new transfers to the KGet, it is assumed that this takes place because of loading * that results in less checks for loaction etc. * * @param elements The dom elements of the transfers to add * @param groupName The name of the group the new transfer will belong to */ static QList addTransfers(const QList &elements, const QString &groupName = QString()); /** * Adds new transfers to the KGet * * @param srcUrls The urls to be downloaded * @param destDir The destination directory. If empty we show a dialog * where the user can choose it. * @param groupName The name of the group the new transfer will belong to * @param start Specifies if the newly added transfers should be started. * If the group queue is already in a running state, this flag does nothing */ static const QList addTransfer(QList srcUrls, QString destDir = QString(), QString groupName = QString(), bool start=false); /** * Removes a transfer from the KGet * * @param transfer The transfer to be removed * @param mode The deletion mode */ static bool delTransfer(TransferHandler * transfer, DeleteMode mode = AutoDelete); /** * Removes multiple transfers from the KGet * * @param transfers The transfers to be removed * @param mode The deletion mode */ static bool delTransfers(const QList &transfers, DeleteMode mode = AutoDelete); /** * Moves a transfer to a new group * * @param transfer The transfer to be moved * @param groupName The name of the new transfer's group */ static void moveTransfer(TransferHandler * transfer, const QString& groupName); /** * Redownload a transfer * @param transfer the transfer to redownload */ static void redownloadTransfer(TransferHandler * transfer); /** * @returns the list of selected transfers */ static QList selectedTransfers(); /** * @returns the list of the finished transfers */ static QList finishedTransfers(); /** * @returns the list of selected groups */ static QList selectedTransferGroups(); /** * @returns a pointer to the TransferTreeModel object */ static TransferTreeModel * model(); /** * @returns a pointer to the QItemSelectionModel object */ static TransferTreeSelectionModel * selectionModel(); /** * Imports the transfers and groups included in the provided xml file * * @param filename the file name to */ static void load( QString filename=QString() ); /** * Exports all the transfers and groups to the given file * * @param filename the file name * @param plain should list be in plain mode or kget mode */ static void save( QString filename=QString(), bool plain=false ); /** * @returns a list of all transferfactories */ static QList factories(); /** * @returns a list of pluginInfos associated with all transferFactories */ static KPluginInfo::List pluginInfos(); /** * @returns The factory of a given transfer * * @param transfer the transfer about which we want to have the factory */ static TransferFactory * factory(TransferHandler * transfer); /** * @return a pointer to the KActionCollection objects */ static KActionCollection * actionCollection(); /** * if running == true starts the scheduler * if running == false stops the scheduler */ static void setSchedulerRunning(bool running=true); /** * Returns true if the scheduler has running jobs. */ static bool schedulerRunning(); /** * true suspends the scheduler, any events that would result in a reschedule are ignored * false wakes up the scheduler, events result in reschedule again * NOTE this is a HACK for cases where the scheduler is the bottleneck, e.g. when stopping * a lot of running transfers, or starting a lot transfers */ static void setSuspendScheduler(bool isSuspended); /** * Gets all transfers */ static QList allTransfers(); /** * Gets all transfer-groups */ static QList allTransferGroups(); /** * Get the transfer with the given url * @param src the url */ static TransferHandler * findTransfer(const QUrl &src); /** * Get the group with the given name * @param name the name */ static TransferGroupHandler * findGroup(const QString &name); /** * Run this function for enabling the systemTray * (will be automatically done, if there is download running) */ static void checkSystemTray(); /** * This will be called when the settings have been changed */ static void settingsChanged(); /** * @return a list of the groups assigned to the filename of a transfer */ static QList groupsFromExceptions(const QUrl &filename); /** * Returns @c true if sourceUrl matches any of the patterns */ static bool matchesExceptions(const QUrl &sourceUrl, const QStringList &patterns); /** * Scans for all the available plugins and creates the proper * transfer DataSource object for transfers Containers * * @param src Source Url * @param type the type of the DataSource that should be created e.g. \ * this is only needed when creating a "special" TransferDataSource like the search for Urls * you can set additional information and the TransferDataSource will use it if it can * @param parent the parent QObject */ static TransferDataSource * createTransferDataSource(const QUrl &src, const QDomElement &type = QDomElement(), QObject *parent = nullptr); /** * Sets the global download limit * @param limit the new global download limit */ static void setGlobalDownloadLimit(int limit); /** * Sets the global upload limit * @param limit the new global upload limit */ static void setGlobalUploadLimit(int limit); /** * Recalculates the global speedlimits */ static void calculateGlobalSpeedLimits(); /** * Recalculates the global download-limit */ static void calculateGlobalDownloadLimit(); /** * Recalculates the global upload-limit */ static void calculateGlobalUploadLimit(); /** * Shows a knotification * @param parent QWidget parent of the notification * @param eventType Notification type * @param text Description of the information showed by the notification * @param icon Pixmap showed in the notification, by default 'dialog-error' * @param title Notification window title * @param flags Notification flags */ static KNotification *showNotification(QWidget *parent, const QString &eventType, const QString &text, const QString &icon = QString("dialog-error"), const QString &title = i18n("KGet"), const KNotification::NotificationFlags &flags = KNotification::CloseOnTimeout); static void loadPlugins(); /** * Returns a download directory * @param preferXDGDownloadDir if true the XDG_DOWNLOAD_DIR will be taken if it is not empty * @note depending if the directories exist it will return them in the following order: * (preferXDGDownloadDirectory >) lastDirectory > XDG_DOWNLOAD_DIR */ static QString generalDestDir(bool preferXDGDownloadDir = false); private: KGet(); class TransferData; /** * Scans for all the available plugins and creates the proper * transfer object for the given src url * * @param src the source url * @param dest the destination url * @param groupName the group name * @param start Specifies if the newly added transfers should be started. */ static TransferHandler * createTransfer(const QUrl &src, const QUrl &dest, const QString& groupName = QString(), bool start = false, const QDomElement * e = nullptr); /** * Creates multiple transfers with transferData */ static QList createTransfers(const QList &transferData); static QUrl urlInputDialog(); static QString destDirInputDialog(); static QUrl destFileInputDialog(QString destDir = QString(), const QString& suggestedFileName = QString()); static bool isValidSource(const QUrl &source); static bool isValidDestDirectory(const QString& destDir); static QUrl getValidDestUrl(const QUrl& destDir, const QUrl &srcUrl); //Plugin-related functions static KGetPlugin* loadPlugin(const KPluginMetaData& md); /** * Stops all downloads if there is no connection and also displays * a message. * If there is a connection, then the downloads will be started again */ static void setHasNetworkConnection(bool hasConnection); /** * Deletes the given file, if possible. * * @param url The file to delete * * @return true if the file was successully deleted: if the given url * is a directory or if it is not local it returns false and shows a * warning message. */ static bool safeDeleteFile( const QUrl& url ); //Interview models static TransferTreeModel * m_transferTreeModel; static TransferTreeSelectionModel * m_selectionModel; //Lists of available plugins static KPluginInfo::List m_pluginInfoList; static QList m_transferFactories; //pointer to the Main window static MainWindow * m_mainWindow; //Scheduler object static TransferGroupScheduler * m_scheduler; //pointer to the kget uiserver jobs manager static KUiServerJobs *m_jobManager; //pointer to the used TransferHistoryStore static TransferHistoryStore *m_store; static bool m_hasConnection; }; class KGET_EXPORT KGet::TransferData { public: TransferData(const QUrl &src, const QUrl &dest, const QString &groupName = QString(), bool start = false, const QDomElement *e = nullptr); QUrl src; QUrl dest; QString groupName; bool start; const QDomElement *e; }; class GenericObserver : public QObject { Q_OBJECT public: GenericObserver(QObject *parent = nullptr); virtual ~GenericObserver (); public slots: void groupAddedEvent(TransferGroupHandler *handler); void groupRemovedEvent(TransferGroupHandler *handler); void transfersAddedEvent(const QList &handlers); void transfersRemovedEvent(const QList &handlers); void transfersChangedEvent(QMap transfers); void groupsChangedEvent(QMap groups); void transferMovedEvent(TransferHandler *, TransferGroupHandler *); private slots: void slotSave(); void slotAfterFinishAction(); void slotAbortAfterFinishAction(); void slotResolveTransferError(); void slotNotificationClosed(); void slotNetworkStatusChanged(bool online); private: bool allTransfersFinished(); void requestSave(); private: QTimer *m_save; QTimer *m_finishAction; QHash m_notifications; QNetworkConfigurationManager m_networkConfig; }; #endif diff --git a/core/kgetglobaljob.cpp b/core/kgetglobaljob.cpp index 3f780b97..08acf355 100644 --- a/core/kgetglobaljob.cpp +++ b/core/kgetglobaljob.cpp @@ -1,72 +1,72 @@ /* This file is part of the KDE project Copyright (C) 2007 by Javier Goday Copyright (C) 2009 by Dario Massarin 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 "kgetglobaljob.h" #include "transferhandler.h" #include "kget.h" #include "kget_debug.h" -#include +#include #include #include KGetGlobalJob::KGetGlobalJob(QObject *parent) : KJob(parent) { setCapabilities(Killable); } KGetGlobalJob::~KGetGlobalJob() { } void KGetGlobalJob::update() { int runningTransfers = 0; qulonglong processedAmount = 0; qulonglong totalAmount = 0; unsigned long speed = 0; unsigned long percent = 0; foreach(TransferHandler * transfer, KGet::allTransfers()) { if(transfer->status() == Job::Running) { runningTransfers++; processedAmount += transfer->downloadedSize(); speed += transfer->downloadSpeed(); totalAmount += transfer->totalSize(); } } // qCDebug(KGET_DEBUG) << totalAmount; if (totalAmount > 0) percent = 100 * processedAmount / totalAmount; else percent = 0; emit description(this, "KGet global information", qMakePair(QString("source"), i18np("KGet is downloading %1 file", "KGet is downloading %1 files", runningTransfers))); emitSpeed(speed); setTotalAmount(KJob::Bytes, totalAmount); setProcessedAmount(KJob::Bytes, processedAmount); setPercent(percent); } bool KGetGlobalJob::doKill() { qCDebug(KGET_DEBUG) << "Kill of global job called:" << this; emit requestStop(this, nullptr); return KJob::doKill(); } diff --git a/core/kgetglobaljob.h b/core/kgetglobaljob.h index ed1204e5..7e8d65df 100644 --- a/core/kgetglobaljob.h +++ b/core/kgetglobaljob.h @@ -1,43 +1,43 @@ /* This file is part of the KDE project Copyright (C) 2007 by Javier Goday Copyright (C) 2009 by Dario Massarin 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. */ #ifndef KGETGLOBALJOB_H #define KGETGLOBALJOB_H -#include +#include class TransferHandler; class KGetGlobalJob : public KJob { Q_OBJECT public: KGetGlobalJob(QObject *parent = nullptr); ~KGetGlobalJob(); void update(); void start() override {}; signals: /** * Emitted when doKill is called, e.g. when the gui is closed. * Not handling this signal might lead to a crash if something tries to * access the then non-existing gui. * @param job is this * @param handler is always nullptr suggesting that all TransferHandlers should be stopped */ void requestStop(KJob *job, TransferHandler *handler); protected: virtual bool doKill() override; }; #endif diff --git a/core/kgetkjobadapter.cpp b/core/kgetkjobadapter.cpp index 381a0376..558f979a 100644 --- a/core/kgetkjobadapter.cpp +++ b/core/kgetkjobadapter.cpp @@ -1,78 +1,78 @@ /* This file is part of the KDE project Copyright (C) 2008 Javier Goday Idea by Copyright (C) 2008 Lukas Appelhans 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 "kgetkjobadapter.h" #include "kget_debug.h" -#include +#include #include KGetKJobAdapter::KGetKJobAdapter(QObject *parent, TransferHandler *transfer) : KJob(parent), m_transferHandler(transfer) { setCapabilities(Killable | Suspendable); } KGetKJobAdapter::~KGetKJobAdapter() { } qulonglong KGetKJobAdapter::processedAmount(Unit unit) const { Q_UNUSED(unit) return m_transferHandler->downloadedSize(); } qulonglong KGetKJobAdapter::totalAmount(Unit unit) const { Q_UNUSED(unit) return m_transferHandler->totalSize(); } unsigned long KGetKJobAdapter::percent() const { return m_transferHandler->percent(); } void KGetKJobAdapter::slotUpdateDescription() { emit description(this, i18n("KGet Transfer"), qMakePair(QString("source"), m_transferHandler->source().toString()), qMakePair(QString("destination"), m_transferHandler->dest().toString())); emitSpeed(m_transferHandler->downloadSpeed()); setProcessedAmount(KJob::Bytes, processedAmount(KJob::Bytes)); setTotalAmount(KJob::Bytes, totalAmount(KJob::Bytes)); setPercent(percent()); } bool KGetKJobAdapter::doKill() { qCDebug(KGET_DEBUG) << "Kill of job adapter called:" << this << m_transferHandler->dest(); emit requestStop(this, m_transferHandler); return KJob::doKill(); } bool KGetKJobAdapter::doSuspend() { if (m_transferHandler->capabilities() & Transfer::Cap_Resuming) { emit requestSuspend(this, m_transferHandler); return true; } return false; } bool KGetKJobAdapter::doResume() { emit requestResume(this, m_transferHandler); return true; } diff --git a/core/kgetkjobadapter.h b/core/kgetkjobadapter.h index 717a868c..ac878f79 100644 --- a/core/kgetkjobadapter.h +++ b/core/kgetkjobadapter.h @@ -1,57 +1,57 @@ /* This file is part of the KDE project Copyright (C) 2008 Javier Goday Idea by Copyright (C) 2008 Lukas Appelhans Copyright (C) 2010 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. */ #ifndef KGETKJOBADAPTER_H #define KGETKJOBADAPTER_H #include "transferhandler.h" -#include +#include /** * Allows kget to register all transfers in kuiserver as kjobs */ class KGetKJobAdapter : public KJob { Q_OBJECT public: KGetKJobAdapter(QObject *parent, TransferHandler *transfer); ~KGetKJobAdapter(); void start() override {}; qulonglong processedAmount(Unit unit) const; qulonglong totalAmount(Unit unit) const; unsigned long percent() const; public slots: void slotUpdateDescription(); signals: /** * Emitted when doKill is called, e.g. when the gui is closed. * Not handling this signal might lead to a crash if something tries to * access the then non-existing gui. */ void requestStop(KJob *job, TransferHandler *handler); void requestSuspend(KJob *job, TransferHandler *handler); void requestResume(KJob *job, TransferHandler *handler); protected: virtual bool doKill() override; virtual bool doSuspend() override; virtual bool doResume() override; private: TransferHandler *m_transferHandler; }; #endif diff --git a/core/kuiserverjobs.cpp b/core/kuiserverjobs.cpp index faf579b4..d61f4321 100644 --- a/core/kuiserverjobs.cpp +++ b/core/kuiserverjobs.cpp @@ -1,226 +1,226 @@ /* This file is part of the KDE project Copyright (C) 2007 by Javier Goday Copyright (C) 2009 by Dario Massarin Copyright (C) 2010 by 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 "kuiserverjobs.h" #include "kgetglobaljob.h" #include "kgetkjobadapter.h" #include "transferhandler.h" #include "settings.h" #include "kget.h" #include "kget_debug.h" -#include -#include +#include +#include KUiServerJobs::KUiServerJobs(QObject *parent) : QObject(parent), m_globalJob(nullptr) { } KUiServerJobs::~KUiServerJobs() { while(m_registeredJobs.size()) { unregisterJob(m_registeredJobs.begin().value(), m_registeredJobs.begin().key()); } delete m_globalJob; } void KUiServerJobs::settingsChanged() { QList transfers = KGet::allTransfers(); foreach(TransferHandler * transfer, transfers) { if(shouldBeShown(transfer)) registerJob(transfer->kJobAdapter(), transfer); else unregisterJob(transfer->kJobAdapter(), transfer); } // GlobalJob is associated to a virtual transfer pointer of value == nullptr if(shouldBeShown(nullptr)) registerJob(globalJob(), nullptr); else unregisterJob(globalJob(), nullptr); } void KUiServerJobs::slotTransfersAdded(QList transfers) { qCDebug(KGET_DEBUG); foreach (TransferHandler *transfer, transfers) { if(shouldBeShown(transfer)) registerJob(transfer->kJobAdapter(), transfer); if(shouldBeShown(nullptr)) { globalJob()->update(); registerJob(globalJob(), nullptr); } else unregisterJob(globalJob(), nullptr); } } void KUiServerJobs::slotTransfersAboutToBeRemoved(const QList &transfers) { qCDebug(KGET_DEBUG); m_invalidTransfers << transfers; foreach (TransferHandler *transfer, transfers) { unregisterJob(transfer->kJobAdapter(), transfer); if (shouldBeShown(nullptr)) { globalJob()->update(); registerJob(globalJob(), nullptr); } else { unregisterJob(globalJob(), nullptr); } } } void KUiServerJobs::slotTransfersChanged(QMap transfers) { qCDebug(KGET_DEBUG); if(!Settings::enableKUIServerIntegration()) return; QMapIterator i(transfers); while (i.hasNext()) { i.next(); // if(!m_invalidTransfers.contains(i.key())) { TransferHandler * transfer = i.key(); if (shouldBeShown(transfer)) { registerJob(transfer->kJobAdapter(), transfer); } else { unregisterJob(transfer->kJobAdapter(), transfer); } } } if(shouldBeShown(nullptr)) { globalJob()->update(); registerJob(globalJob(), nullptr); } else unregisterJob(globalJob(), nullptr); } void KUiServerJobs::registerJob(KGetKJobAdapter *job, TransferHandler *transfer) { if (m_registeredJobs.contains(transfer) || !job) { return; } connect(job, SIGNAL(requestStop(KJob*,TransferHandler*)), this, SLOT(slotRequestStop(KJob*,TransferHandler*))); connect(job, SIGNAL(requestSuspend(KJob*,TransferHandler*)), this, SLOT(slotRequestSuspend(KJob*,TransferHandler*))); connect(job, SIGNAL(requestResume(KJob*,TransferHandler*)), this, SLOT(slotRequestResume(KJob*,TransferHandler*))); KJob *j = job; registerJob(j, transfer); } void KUiServerJobs::registerJob(KJob * job, TransferHandler * transfer) { if(m_registeredJobs.contains(transfer) || !job) return; KIO::getJobTracker()->registerJob(job); m_registeredJobs[transfer] = job; } bool KUiServerJobs::unregisterJob(KJob * job, TransferHandler * transfer) { if (!m_registeredJobs.contains(transfer) || !job) return false; //Transfer should only be suspended, thus still show the job tracker if (m_suspendRequested.contains(transfer)) { m_suspendRequested.removeAll(transfer); return false; } //unregister the job if it was a single adaptor if (job != m_globalJob) { disconnect(job); } KIO::getJobTracker()->unregisterJob(m_registeredJobs[transfer]); m_registeredJobs.remove(transfer); return true; } void KUiServerJobs::slotRequestStop(KJob *job, TransferHandler *transfer) { if (unregisterJob(job, transfer)) { if (transfer) { transfer->stop(); } else { foreach (TransferHandler *t, KGet::allTransfers()) { t->stop(); } } } } bool KUiServerJobs::shouldBeShown(TransferHandler * transfer) { if(!Settings::enableKUIServerIntegration()) return false; if(Settings::exportGlobalJob() && (transfer == nullptr) && existRunningTransfers()) return true; if(!Settings::exportGlobalJob() && (transfer) && (transfer->status() == Job::Running)) return true; return false; } bool KUiServerJobs::existRunningTransfers() { foreach(TransferHandler * transfer, KGet::allTransfers()) { //if added to m_invalidTransfers it means that the job is about to be removed if ((transfer->status() == Job::Running) && !m_invalidTransfers.contains(transfer)) { return true; } } return false; } KGetGlobalJob * KUiServerJobs::globalJob() { if (!m_globalJob) { m_globalJob = new KGetGlobalJob(); connect(m_globalJob, SIGNAL(requestStop(KJob*,TransferHandler*)), this, SLOT(slotRequestStop(KJob*,TransferHandler*))); } return m_globalJob; } void KUiServerJobs::slotRequestSuspend(KJob *job, TransferHandler *transfer) { Q_UNUSED(job) if (transfer) { m_suspendRequested << transfer; transfer->stop(); } } void KUiServerJobs::slotRequestResume(KJob *job, TransferHandler *transfer) { Q_UNUSED(job) if (transfer) { transfer->start(); } } diff --git a/core/linkimporter.cpp b/core/linkimporter.cpp index e439e2f3..961b144c 100644 --- a/core/linkimporter.cpp +++ b/core/linkimporter.cpp @@ -1,141 +1,141 @@ /* This file is part of the KDE project Copyright (C) 2008 Javier Goday First Url regular expression taken from urlview tool by Michael Elkins . Regular expression improved by FiNex. Improvements to regular expression and slotReadFile by Frantisek Ziacik 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 "linkimporter.h" +#include #include #include #include #include #include #include -#include #include -#include +#include //static QString REGULAR_EXPRESSION = "(((https?|ftp|gopher)://|(mailto|file|news):)[^’ <>\"]+|(www|web|w3).[-a-z0-9.]+)[^’ .,;<>\":]"; // static QString REGULAR_EXPRESSION = "((http|https|ftp|ftps)+([\\:\\w\\d:#@%/;$()~_?\\+-=\\\\.&])*)"; static QString REGULAR_EXPRESSION = "(\\w+[:]//)?(((([\\w-]+[.]){1,}(ac|ad|ae|af|ag|ai|al|am|an|ao|aq|ar|as|at|au|aw|az|ba|bb|bd|be|bf|bg|bh|bi|bj|bm|bn|bo|br|bs|bt|bv|bw|by|bz|ca|cc|cd|cf|cg|ch|ci|ck|cl|cm|cn|co|com|cr|cs|cu|cv|cx|cy|cz|de|dj|dk|dm|do|dz|ec|edu|ee|eg|eh|er|es|et|eu|fi|fj|fk|fm|fo|fr|ga|gd|ge|gf|gg|gh|gi|gl|gm|gn|gov|gp|gq|gr|gs|gt|gu|gw|gy|hk|hm|hn|hr|ht|hu|id|ie|il|im|in|int|io|iq|ir|is|it|je|jm|jo|jp|ke|kg|kh|ki|km|kn|kp|kr|kw|ky|kz|la|lb|lc|li|lk|lr|ls|lt|lu|lv|ly|ma|mc|md|mg|mh|mil|mk|ml|mm|mn|mo|mp|mq|mr|ms|mt|mu|mv|mw|mx|my|mz|na|nc|ne|net|nf|ng|ni|nl|no|np|nr|nt|nu|nz|om|org|pa|pe|pf|pg|ph|pk|pl|pm|pn|pr|ps|pt|pw|py|qa|re|ro|ru|rw|sa|sb|sc|sd|se|sg|sh|si|sj|sk|sl|sm|sn|so|sr|sv|st|sy|sz|tc|td|tf|tg|th|tj|tk|tm|tn|to|tp|tr|tt|tv|tw|tz|ua|ug|uk|um|us|uy|uz|va|vc|ve|vg|vi|vn|vu|wf|ws|ye|yt|yu|za|zm|zw|aero|biz|coop|info|museum|name|pro|travel))|([0-9]+[.][0-9]+[.][0-9]+[.][0-9]+)))([:][0-9]*)?([?/][\\w~#\\-;%?@&=/.+]*)?(?!\\w)"; LinkImporter::LinkImporter(const QUrl &url, QObject *parent) : QThread(parent), m_url(url), m_transfers(), m_tempFile() { } LinkImporter::LinkImporter(QObject *parent) : QThread(parent), m_url(), m_transfers(), m_tempFile() { } LinkImporter::~LinkImporter() { } void LinkImporter::checkClipboard(const QString &clipboardContent) { QRegExp rx(REGULAR_EXPRESSION); int regexPos = 0; while ((regexPos = rx.indexIn(clipboardContent, regexPos)) > -1) { QString link = rx.capturedTexts()[0]; addTransfer(link); regexPos += rx.matchedLength(); } } void LinkImporter::run() { if(!m_url.isLocalFile() && !m_tempFile.isEmpty()) { slotReadFile(QUrl(m_tempFile)); } else { slotReadFile(m_url); } quit(); } void LinkImporter::copyRemoteFile() { m_tempFile = QString("%1/%2.tmp").arg(QDir::tempPath()).arg("importer_aux"); QUrl aux(m_tempFile); KIO::CopyJob *job = KIO::copy(m_url, aux, KIO::HideProgressInfo); if(!job->exec()) { emit error(ki18n("Error trying to get %1").subs(m_url.url())); } } void LinkImporter::slotReadFile(const QUrl &url) { QRegExp rx(REGULAR_EXPRESSION); QFile file(url.toLocalFile()); if (!file.open(QIODevice::ReadOnly | QIODevice::Text)) return; QTextStream in(&file); quint64 size = file.size(); quint64 position = 0; while (!in.atEnd()) { QString line = in.readLine(); int regexPos = 0; quint64 lastPosition = position; while ((regexPos = rx.indexIn(line, regexPos)) > -1) { QString link = rx.capturedTexts()[0]; addTransfer(link); regexPos += rx.matchedLength(); position = lastPosition + regexPos; emit progress(position * 100 / size); } position += line.size(); emit progress(position * 100 / size); } if(!m_url.isLocalFile()) { file.remove(); } } void LinkImporter::addTransfer(QString &link) { QUrl auxUrl; if (link.contains("://")) { auxUrl = QUrl(link); } else { auxUrl = QUrl(QString("http://") + link); } if(!link.isEmpty() && auxUrl.isValid() && m_transfers.indexOf(link) < 0 && !auxUrl.scheme().isEmpty() && !auxUrl.host().isEmpty()) { m_transfers << link; } } diff --git a/core/plugin/transferfactory.cpp b/core/plugin/transferfactory.cpp index 385e6239..8a8d9834 100644 --- a/core/plugin/transferfactory.cpp +++ b/core/plugin/transferfactory.cpp @@ -1,86 +1,86 @@ /* This file is part of the KDE project Copyright (C) 2004 Dario Massarin Copyright (C) 2009 Lukas Appelhans 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 "transferfactory.h" #include "kget.h" -#include +#include #include TransferFactory::TransferFactory(QObject *parent, const QVariantList &args) : KGetPlugin(parent, args) { } Transfer * TransferFactory::createTransfer(const QUrl &srcUrl, const QUrl &destUrl, TransferGroup * parent, Scheduler * scheduler, const QDomElement * n) { Q_UNUSED(srcUrl) Q_UNUSED(destUrl) Q_UNUSED(parent) Q_UNUSED(scheduler) Q_UNUSED(n) return 0; } TransferHandler * TransferFactory::createTransferHandler(Transfer * transfer, Scheduler * scheduler) { return new TransferHandler(transfer, scheduler); } QWidget * TransferFactory::createDetailsWidget(TransferHandler * transfer) { Q_UNUSED(transfer) return 0; } QDialog * TransferFactory::createNewTransferDialog(const QUrl &srcUrl, const QString &suggestedFileName, TransferGroupHandler * defaultGroup) { Q_UNUSED(srcUrl) Q_UNUSED(suggestedFileName) Q_UNUSED(defaultGroup) return 0; } const QList TransferFactory::actions(TransferHandler *handler) { Q_UNUSED(handler) return QList(); } TransferDataSource * TransferFactory::createTransferDataSource(const QUrl &srcUrl, const QDomElement &type, QObject *parent) { Q_UNUSED(srcUrl) Q_UNUSED(type) Q_UNUSED(parent) return 0; } bool TransferFactory::isSupported(const QUrl &url) const { Q_UNUSED(url) return false; } QStringList TransferFactory::addsProtocols() const { return QStringList(); } QString TransferFactory::displayName() const { return "Undefined"; } diff --git a/core/scheduler.cpp b/core/scheduler.cpp index 9f803aa4..9bff3142 100644 --- a/core/scheduler.cpp +++ b/core/scheduler.cpp @@ -1,441 +1,441 @@ /* This file is part of the KDE project Copyright (C) 2004 Dario Massarin Copyright (C) 2010 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/scheduler.h" #include "core/transferhandler.h" #include "settings.h" #include #include #include "kget_debug.h" -#include +#include Scheduler::Scheduler(QObject * parent) : QObject(parent), m_failureCheckTimer(0), m_stallTime(5), m_stallTimeout(Settings::reconnectDelay()), m_abortTimeout(Settings::reconnectDelay()), m_isSuspended(false), m_hasConnection(true) { } Scheduler::~Scheduler() { } void Scheduler::setIsSuspended(bool isSuspended) { const bool changed = (isSuspended != m_isSuspended); m_isSuspended = isSuspended; //update all the queues if (changed && shouldUpdate()) { updateAllQueues(); } } void Scheduler::setHasNetworkConnection(bool hasConnection) { const bool changed = (hasConnection != m_hasConnection); m_hasConnection = hasConnection; if (changed) { if (hasConnection) { if (!m_failureCheckTimer) { m_failureCheckTimer = startTimer(1000); } updateAllQueues(); } else { if (m_failureCheckTimer) { killTimer(m_failureCheckTimer); m_failureCheckTimer = 0; } foreach (JobQueue *queue, m_queues) { std::for_each(queue->begin(), queue->end(), boost::bind(&Job::stop, _1)); } } } } void Scheduler::addQueue(JobQueue * queue) { if(!m_queues.contains(queue)) m_queues.append(queue); } void Scheduler::delQueue(JobQueue * queue) { m_queues.removeAll(queue); } struct IsRunningJob { bool operator()(Job *job) const {return (job->status() == Job::Running);} }; bool Scheduler::hasRunningJobs() const { foreach (JobQueue *queue, m_queues) { if (std::find_if(queue->begin(), queue->end(), IsRunningJob()) != queue->end()) { return true; } } return false; } int Scheduler::countRunningJobs() const { int count = 0; foreach(JobQueue * queue, m_queues) { count += std::count_if(queue->begin(), queue->end(), IsRunningJob()); } return count; } void Scheduler::settingsChanged() { m_stallTimeout = Settings::reconnectDelay(); m_abortTimeout = Settings::reconnectDelay(); updateAllQueues(); } void Scheduler::jobQueueChangedEvent(JobQueue * queue, JobQueue::Status status) { if( status == JobQueue::Stopped ) { JobQueue::iterator it = queue->begin(); JobQueue::iterator itEnd = queue->end(); for ( ; it!=itEnd ; ++it) { if ((*it)->status() != Job::Stopped) (*it)->stop(); } } else updateQueue(queue); } void Scheduler::jobQueueMovedJobEvent(JobQueue * queue, Job * job) { Q_UNUSED(job) updateQueue(queue); } void Scheduler::jobQueueAddedJobEvent(JobQueue * queue, Job * job) { Q_UNUSED(job) updateQueue(queue); } void Scheduler::jobQueueAddedJobsEvent(JobQueue *queue, const QList jobs) { Q_UNUSED(jobs) updateQueue(queue); } void Scheduler::jobQueueRemovedJobEvent(JobQueue * queue, Job * job) { Q_UNUSED(job) updateQueue(queue); } void Scheduler::jobQueueRemovedJobsEvent(JobQueue *queue, const QList jobs) { Q_UNUSED(jobs) updateQueue(queue); } void Scheduler::jobChangedEvent(Job * job, Job::Status status) { qCDebug(KGET_DEBUG) << "Scheduler::jobChangedEvent (job=" << job << " status=" << status << ")"; if (!m_failureCheckTimer) m_failureCheckTimer = startTimer(1000); if (status != Job::Running) updateQueue( job->jobQueue() ); } void Scheduler::jobChangedEvent(Job * job, Job::Policy policy) { Q_UNUSED(policy) updateQueue( job->jobQueue() ); } void Scheduler::jobChangedEvent(Job * job, JobFailure failure) { switch(failure.status) { case None: qCDebug(KGET_DEBUG) << "job = " << job << " failure (#" << failure.count << ") = None "; break; case AboutToStall: qCDebug(KGET_DEBUG) << "job = " << job << " failure (#" << failure.count << ") = AboutToStall "; break; case Stall: qCDebug(KGET_DEBUG) << "job = " << job << " failure (#" << failure.count << ") = Stall "; break; case StallTimeout: qCDebug(KGET_DEBUG) << "job = " << job << " failure (#" << failure.count << ") = StallTimeout "; break; case Abort: qCDebug(KGET_DEBUG) << "job = " << job << " failure (#" << failure.count << ") = Abort "; break; case AbortTimeout: qCDebug(KGET_DEBUG) << "job = " << job << " failure (#" << failure.count << ") = AbortTimeout "; break; case Error: qCDebug(KGET_DEBUG) << "job = " << job << " failure (#" << failure.count << ") = Error "; break; } if (failure.status == Error) { static_cast(job)->handler()->stop(); } else if (//If this happens the job just gets stopped // Second condition: if count > reconnectRetries and Timeout happened trigger a stop/start BUT only if // 10 timeouts have happened (9 of them without taking any action). This means every 10*Settings::reconnectDelay() (ex. 15s -> 150s) (failure.count > Settings::reconnectRetries() && (failure.status == StallTimeout || failure.status == AbortTimeout) && !((failure.count - Settings::reconnectRetries()) % 10)) ) { //FIXME reenable once a connection limit per mirror is in place BUG:262098 //static_cast(job)->handler()->stop();// This will trigger the changedEvent which will trigger an updateQueue call job->stop();//FIXME remove once a connection limit per mirror is in place } else if (failure.count <= Settings::reconnectRetries() && (failure.status == StallTimeout || failure.status == AbortTimeout)){ // First condition: if count <= reconnectRetries and Timeout happened trigger a stop/start job->stop();//stops the job, it will be later restarted by updateQueue } else updateQueue( job->jobQueue() ); } void Scheduler::start() { std::for_each(m_queues.begin(), m_queues.end(), boost::bind(&JobQueue::setStatus, _1, JobQueue::Running)); } void Scheduler::stop() { std::for_each(m_queues.begin(), m_queues.end(), boost::bind(&JobQueue::setStatus, _1, JobQueue::Stopped)); } void Scheduler::updateQueue( JobQueue * queue ) { static bool updatingQueue = false; if (!shouldUpdate() || updatingQueue) return; updatingQueue = true; int runningJobs = 0; //Jobs that are running (and not in the stallTimeout) int waitingJobs = 0; //Jobs that we leave running but are in stallTimeout. We wait for them to start downloading, while we start other ones /** * Implemented behaviour * * The scheduler allows a maximum number of runningJobs equal to the queue->maxSimultaneousJobs() setting. * If that number is not reached because of stallTimeout transfers, the scheduler allows that: * (runningJobs + waitingJobs) < 2 * queue->maxSimultaneousJobs() * Examples (with maxSimultaneousJobs = 2): * These are if the running jobs come first in the queue * 1) 2 runningJobs - 0 waitingJobs * 2) 1 runningJobs - up to 3 waitingJobs * 3) 0 runningJobs - up to 4 waitingJobs * These are if the waiting jobs come first in the queue * 1) 1 waitingJobs - 2 runningJobs * 2) 2 waitingJobs - 2 runningJobs * 3) 3 waitingJobs - 1 runningJobs * 4) 4 waitingJobs - 0 runningJobs **/ JobQueue::iterator it = queue->begin(); JobQueue::iterator itEnd = queue->end(); for( int job=0 ; it!=itEnd ; ++it, ++job) { //qCDebug(KGET_DEBUG) << "MaxSimJobs " << queue->maxSimultaneousJobs(); qCDebug(KGET_DEBUG) << "Scheduler: Evaluating job " << job; JobFailure failure = m_failedJobs.value(*it); if( runningJobs < queue->maxSimultaneousJobs() && ((runningJobs + waitingJobs) < 2 * queue->maxSimultaneousJobs()) ) { if( (*it)->status() == Job::Running || (*it)->status() == Job::FinishedKeepAlive ) { if( !shouldBeRunning(*it) ) { qCDebug(KGET_DEBUG) << "Scheduler: stopping job"; (*it)->stop(); } else if(failure.status == None || failure.status == AboutToStall) runningJobs++; else waitingJobs++; } else // != Job::Running { if( shouldBeRunning(*it) ) { qCDebug(KGET_DEBUG) << "Scheduler: starting job"; (*it)->start(); if((failure.status == None || failure.status == AboutToStall) && (*it)->status() != Job::FinishedKeepAlive) runningJobs++; else waitingJobs++; } } } else { //Stop all the other running downloads qCDebug(KGET_DEBUG) << "Scheduler: stopping job over maxSimJobs limit"; (*it)->stop(); } } updatingQueue = false; } void Scheduler::updateAllQueues() { foreach (JobQueue *queue, m_queues) { updateQueue(queue); } } bool Scheduler::shouldBeRunning( Job * job ) { Job::Policy policy = job->policy(); Job::Status status = job->status(); if( job->jobQueue()->status() == JobQueue::Stopped ) { return ( (policy == Job::Start) && ((status != Job::Finished) && (status != Job::Aborted || job->error().type == Job::AutomaticRetry))); } else //JobQueue::Running { return ( (policy != Job::Stop) && ((status != Job::Finished) && (status != Job::Aborted || job->error().type == Job::AutomaticRetry))); } } void Scheduler::timerEvent( QTimerEvent * event ) { Q_UNUSED(event) // qCDebug(KGET_DEBUG); if (!shouldUpdate()) { return; } foreach(JobQueue * queue, m_queues) { JobQueue::iterator it = queue->begin(); JobQueue::iterator itEnd = queue->end(); for( int job=0 ; it!=itEnd ; ++it, ++job) { JobFailure failure = m_failedJobs[*it]; JobFailure prevFailure = failure; if((*it)->isStalled()) // Stall status initialization { if(failure.status!=AboutToStall && failure.status!=Stall && failure.status!=StallTimeout) { failure.status = AboutToStall; failure.time = 0; failure.count = 0; } else { failure.time++; if(failure.time >= m_stallTime + m_stallTimeout) { failure.status = StallTimeout; failure.count++; } else if(failure.time >= m_stallTime) failure.status = Stall; else failure.status = AboutToStall; if(failure.status == StallTimeout) failure.time = m_stallTime; } } else if((*it)->status() == Job::Aborted) // Abort status initialization { if ((*it)->error().type != Job::AutomaticRetry) { failure.status = Error; } else { if(failure.status!=Abort) { failure.status = Abort; failure.time = 0; failure.count = 0; } else { failure.time++; failure.count++; if(failure.time >= m_abortTimeout) { failure.status = AbortTimeout; failure.count++; } if(failure.status == AbortTimeout) failure.time = 0; } } } else if ((*it)->isWorking()) { failure = JobFailure(); } if(failure.isValid()) // A failure has been detected m_failedJobs[*it] = failure; else // No failure detected, remove it m_failedJobs.remove(*it); // if(failure.isValid() || prevFailure.isValid()) // qCDebug(KGET_DEBUG) << "failure = " << failure.status << " T=" << failure.time << " prevFailure = " << prevFailure.status; if(failure.status != prevFailure.status) jobChangedEvent(*it, failure); // Notify the scheduler } } } diff --git a/core/signature.cpp b/core/signature.cpp index 5794b003..e40cb227 100644 --- a/core/signature.cpp +++ b/core/signature.cpp @@ -1,304 +1,305 @@ /************************************************************************** * Copyright (C) 2009-2011 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. * * * * 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 "signature_p.h" #include "keydownloader.h" #include "settings.h" #include "kget_debug.h" -#include +#include #include #include #include -#include +#include + #ifdef HAVE_QGPGME #include #include #include #include #endif #ifdef HAVE_QGPGME -K_GLOBAL_STATIC(KeyDownloader, signatureDownloader) +Q_GLOBAL_STATIC(KeyDownloader, signatureDownloader) #endif //HAVE_QGPGME SignaturePrivate::SignaturePrivate(Signature *signature) : q(signature), type(Signature::NoType), status(Signature::NoResult), verifyTried(false), sigSummary(0), error(0) { } SignaturePrivate::~SignaturePrivate() { } void SignaturePrivate::signatureDownloaded() { if (verifyTried) { qCDebug(KGET_DEBUG) << "Rerun verification."; q->verify(); } } #ifdef HAVE_QGPGME GpgME::VerificationResult SignaturePrivate::verify(const QUrl &dest, const QByteArray &sig) { GpgME::VerificationResult result; if (!QFile::exists(dest.toDisplayString(QUrl::PreferLocalFile)) || sig.isEmpty()) { return result; } GpgME::initializeLibrary(); GpgME::Error error = GpgME::checkEngine(GpgME::OpenPGP); if (error) { qCDebug(KGET_DEBUG) << "OpenPGP not supported!"; return result; } QScopedPointer context(GpgME::Context::createForProtocol(GpgME::OpenPGP)); if (!context.data()) { qCDebug(KGET_DEBUG) << "Could not create context."; return result; } std::shared_ptr qFile(new QFile(dest.toDisplayString(QUrl::PreferLocalFile))); qFile->open(QIODevice::ReadOnly); QGpgME::QIODeviceDataProvider *file = new QGpgME::QIODeviceDataProvider(qFile); GpgME::Data dFile(file); QGpgME::QByteArrayDataProvider signatureBA(sig); GpgME::Data signature(&signatureBA); return context->verifyDetachedSignature(signature, dFile); } #endif //HAVE_QGPGME Signature::Signature(const QUrl &dest, QObject *object) : QObject(object), d(new SignaturePrivate(this)) { d->dest = dest; #ifdef HAVE_QGPGME qRegisterMetaType("GpgME::VerificationResult"); connect(&d->thread, SIGNAL(verified(GpgME::VerificationResult)), this, SLOT(slotVerified(GpgME::VerificationResult))); #endif //HAVE_QGPGME } Signature::~Signature() { delete d; } QUrl Signature::destination() const { return d->dest; } void Signature::setDestination(const QUrl &destination) { d->dest = destination; } Signature::VerificationStatus Signature::status() const { return d->status; } #ifdef HAVE_QGPGME GpgME::VerificationResult Signature::verificationResult() { return d->verificationResult; } #endif //HAVE_QGPGME QByteArray Signature::signature() { return d->signature; } void Signature::setAsciiDetatchedSignature(const QString &signature) { setSignature(signature.toAscii(), AsciiDetached); } void Signature::setSignature(const QByteArray &signature, SignatureType type) { if ((signature == d->signature) && (type == d->type)) { return; } d->type = type; d->signature = signature; d->fingerprint.clear(); d->error = 0; d->sigSummary = 0; d->status = Signature::NoResult; #ifdef HAVE_QGPGME d->verificationResult = GpgME::VerificationResult(); #endif //HAVE_QGPGME emit verified(d->status);//FIXME } Signature::SignatureType Signature::type() const { return d->type; } QString Signature::fingerprint() { return d->fingerprint; } void Signature::downloadKey(QString fingerprint) // krazy:exclude=passbyvalue { #ifdef HAVE_QGPGME qCDebug(KGET_DEBUG) << "Downloading key:" << fingerprint; signatureDownloader->downloadKey(fingerprint, this); #else Q_UNUSED(fingerprint) #endif //HAVE_QGPGME } bool Signature::isVerifyable() { #ifdef HAVE_QGPGME return QFile::exists(d->dest.toDisplayString(QUrl::PreferLocalFile)) && !d->signature.isEmpty(); #else return false; #endif //HAVE_QGPGME } void Signature::verify() { #ifdef HAVE_QGPGME d->thread.verify(d->dest, d->signature); #endif //HAVE_QGPGME } #ifdef HAVE_QGPGME void Signature::slotVerified(const GpgME::VerificationResult &result) { d->verificationResult = result; d->status = Signature::NotWorked; if (!d->verificationResult.numSignatures()) { qCDebug(KGET_DEBUG) << "No signatures\n"; emit verified(d->status); return; } GpgME::Signature signature = d->verificationResult.signature(0); d->sigSummary = signature.summary(); d->error = signature.status().code(); d->fingerprint = signature.fingerprint(); qCDebug(KGET_DEBUG) << "Fingerprint:" << d->fingerprint; qCDebug(KGET_DEBUG) << "Signature summary:" << d->sigSummary; qCDebug(KGET_DEBUG) << "Error code:" << d->error; if (d->sigSummary & GpgME::Signature::KeyMissing) { qCDebug(KGET_DEBUG) << "Public key missing."; if (Settings::signatureAutomaticDownloading() || (KMessageBox::warningYesNoCancel(nullptr, i18n("The key to verify the signature is missing, do you want to download it?")) == KMessageBox::Yes)) { d->verifyTried = true; downloadKey(d->fingerprint); emit verified(d->status); return; } } if (!signature.status()) { if (d->sigSummary & GpgME::Signature::Valid) { d->status = Signature::Verified; } else if ((d->sigSummary & GpgME::Signature::Green) || (d->sigSummary == 0)) { d->status = Signature::VerifiedInformation; } } else if (signature.status()) { if ((d->sigSummary & GpgME::Signature::KeyExpired) || (d->sigSummary & GpgME::Signature::KeyRevoked)) { d->status = Signature::VerifiedWarning; } if (d->sigSummary & GpgME::Signature::Red) {//TODO handle more cases! d->status = Signature::NotVerified; //TODO handle that dialog better in 4.5 KMessageBox::error(nullptr, i18n("The signature could not be verified for %1. See transfer settings for more information.", d->dest.fileName()), i18n("Signature not verified")); } } emit verified(d->status); } #endif //HAVE_QGPGME void Signature::save(const QDomElement &element) { QDomElement e = element; QDomElement verification = e.ownerDocument().createElement("signature"); verification.setAttribute("status", d->status); verification.setAttribute("sigStatus", d->sigSummary); verification.setAttribute("error", d->error); verification.setAttribute("fingerprint", d->fingerprint); verification.setAttribute("type", d->type); QDomText value; switch (d->type) { case NoType: case AsciiDetached: value = e.ownerDocument().createTextNode(d->signature); break; case BinaryDetached: value = e.ownerDocument().createTextNode(d->signature.toBase64()); break; } verification.appendChild(value); e.appendChild(verification); } void Signature::load(const QDomElement &e) { QDomElement verification = e.firstChildElement("signature"); d->status = static_cast(verification.attribute("status").toInt()); d->sigSummary = verification.attribute("sigStatus").toInt(); d->error = verification.attribute("error").toInt(); d->fingerprint = verification.attribute("fingerprint"); d->type = static_cast(verification.attribute("type").toInt()); switch (d->type) { case NoType: case AsciiDetached: d->signature = verification.text().toAscii(); break; case BinaryDetached: d->signature = QByteArray::fromBase64(verification.text().toAscii()); } } diff --git a/core/signaturethread.cpp b/core/signaturethread.cpp index 5079576a..b41d871e 100644 --- a/core/signaturethread.cpp +++ b/core/signaturethread.cpp @@ -1,79 +1,79 @@ /************************************************************************** * Copyright (C) 2009-2011 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. * * * * 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 "signaturethread.h" #include "signature_p.h" #include "kget_debug.h" -#include +#include SignatureThread::SignatureThread(QObject *parent) : QThread(parent), m_abort(false) { } SignatureThread::~SignatureThread() { m_mutex.lock(); m_abort = true; m_mutex.unlock(); wait(); } bool SignatureThread::isValid() const { #ifdef HAVE_QGPGME return true; #else //HAVE_QGPGME return false; #endif //HAVE_QGPGME } void SignatureThread::verify(const QUrl &dest, const QByteArray &sig) { QMutexLocker locker(&m_mutex); m_dest.append(dest); m_sig.append(sig); if (!isRunning()) { start(); } } void SignatureThread::run() { #ifdef HAVE_QGPGME while (!m_abort && m_dest.count()) { m_mutex.lock(); const QUrl dest = m_dest.takeFirst(); const QByteArray sig = m_sig.takeFirst(); m_mutex.unlock(); GpgME::VerificationResult result = SignaturePrivate::verify(dest, sig); if (!m_abort) { emit verified(result); } } #else //HAVE_QGPGME qCWarning(KGET_DEBUG) << "No QGPGME support."; #endif //HAVE_QGPGME } diff --git a/core/transfer.cpp b/core/transfer.cpp index 528e9249..7fe440a2 100644 --- a/core/transfer.cpp +++ b/core/transfer.cpp @@ -1,333 +1,333 @@ /* This file is part of the KDE project Copyright (C) 2004 Dario Massarin Copyright (C) 2008 - 2011 Lukas Appelhans 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/transfer.h" #include "settings.h" #include "core/transferhandler.h" #include "core/plugin/transferfactory.h" #include "core/scheduler.h" -#include +#include #include #include #include struct StatusStrings { const char * context; const char * name; }; const StatusStrings STATUSTEXTS[] = { {"", I18N_NOOP("Downloading....")}, {I18N_NOOP2_NOSTRIP("transfer state: delayed", "Delayed")}, {I18N_NOOP2_NOSTRIP("transfer state: stopped", "Stopped")}, {I18N_NOOP2_NOSTRIP("transfer state: aborted", "Aborted")}, {I18N_NOOP2_NOSTRIP("transfer state: finished", "Finished")}, {"", ""},//TODO: Add FinishedKeepAlive status {I18N_NOOP2_NOSTRIP("changing the destination of the file", "Changing destination")} }; const QStringList STATUSICONS = QStringList() << "media-playback-start" << "view-history" << "process-stop" << "dialog-error" << "dialog-ok" << "media-playback-start" << "media-playback-pause"; Transfer::Transfer(TransferGroup * parent, TransferFactory * factory, Scheduler * scheduler, const QUrl & source, const QUrl & dest, const QDomElement * e) : Job(scheduler, parent), m_source(source), m_dest(dest), m_totalSize(0), m_downloadedSize(0), m_uploadedSize(0), m_percent(0), m_downloadSpeed(0), m_uploadSpeed(0), m_uploadLimit(0), m_downloadLimit(0), m_isSelected(false), m_capabilities(0), m_visibleUploadLimit(0), m_visibleDownloadLimit(0), m_ratio(0), m_handler(nullptr), m_factory(factory) { Q_UNUSED(e) } Transfer::~Transfer() { } void Transfer::setCapabilities(Capabilities capabilities) { if (m_capabilities != capabilities) { m_capabilities = capabilities; emit capabilitiesChanged(); } } void Transfer::create() { init(); } void Transfer::destroy(DeleteOptions options) { deinit(options); } void Transfer::init()//TODO think about e, maybe not have it at all in the constructor? { } bool Transfer::setDirectory(const QUrl& newDirectory) { Q_UNUSED(newDirectory) //the standard implemention always returns false return false; } int Transfer::elapsedTime() const { if (status() == Job::Running) return m_runningTime.elapsed() / 1000; return m_runningSeconds; } int Transfer::averageDownloadSpeed() const { const int runningSeconds = elapsedTime(); if (runningSeconds) { return m_totalSize / runningSeconds; } return 0; } QHash > Transfer::availableMirrors(const QUrl &file) const { Q_UNUSED(file) QHash > available; available[m_source] = QPair(true, 1); return available; } void Transfer::setUploadLimit(int ulLimit, SpeedLimit limit) { if (limit == Transfer::VisibleSpeedLimit) { m_visibleUploadLimit = ulLimit; if (ulLimit < m_uploadLimit || m_uploadLimit == 0) m_uploadLimit = ulLimit; } else { m_uploadLimit = ulLimit; } setSpeedLimits(m_uploadLimit, m_downloadLimit); } void Transfer::setDownloadLimit(int dlLimit, SpeedLimit limit) { if (limit == Transfer::VisibleSpeedLimit) { m_visibleDownloadLimit = dlLimit; if (dlLimit < m_downloadLimit || m_downloadLimit == 0) m_downloadLimit = dlLimit; } else { m_downloadLimit = dlLimit; } setSpeedLimits(m_uploadLimit, m_downloadLimit); } int Transfer::uploadLimit(SpeedLimit limit) const { if (limit == Transfer::VisibleSpeedLimit) return m_visibleUploadLimit; return m_uploadLimit; } int Transfer::downloadLimit(SpeedLimit limit) const { if (limit == Transfer::VisibleSpeedLimit) return m_visibleDownloadLimit; return m_downloadLimit; } void Transfer::setMaximumShareRatio(double ratio) { m_ratio = ratio; checkShareRatio(); } void Transfer::checkShareRatio() { if (m_downloadedSize == 0 || m_ratio == 0) return; if (m_uploadedSize / m_downloadedSize >= m_ratio) setDownloadLimit(1, Transfer::InvisibleSpeedLimit);//If we set it to 0 we would have no limit xD else setDownloadLimit(0, Transfer::InvisibleSpeedLimit); } void Transfer::setLog(const QString& message, Transfer::LogLevel level) { QString msg("" + QTime::currentTime().toString() + " : "); if (level == Log_Error) { msg += "" + message + ""; } if (level == Log_Warning) { msg += "" + message + ""; } else { msg += message; } m_log << msg; } TransferHandler * Transfer::handler() { if(!m_handler) m_handler = m_factory->createTransferHandler(this, scheduler()); return m_handler; } TransferTreeModel * Transfer::model() { return group()->model(); } void Transfer::save(const QDomElement &element) { QDomElement e = element; e.setAttribute("Source", m_source.url()); e.setAttribute("Dest", m_dest.url()); e.setAttribute("TotalSize", m_totalSize); e.setAttribute("DownloadedSize", m_downloadedSize); e.setAttribute("UploadedSize", m_uploadedSize); e.setAttribute("DownloadLimit", m_visibleDownloadLimit); e.setAttribute("UploadLimit", m_visibleUploadLimit); e.setAttribute("ElapsedTime", status() == Job::Running ? m_runningTime.elapsed() / 1000 : m_runningSeconds); e.setAttribute("Policy", policy() == Job::Start ? "Start" : (policy() == Job::Stop ? "Stop" : "None")); } void Transfer::load(const QDomElement *element) { if (!element) { setStatus(status(), i18nc("transfer state: stopped", "Stopped"), SmallIcon("process-stop")); setStartStatus(status()); return; } const QDomElement e = *element; m_source = QUrl(e.attribute("Source")); m_dest = QUrl(e.attribute("Dest")); m_totalSize = e.attribute("TotalSize").toULongLong(); m_downloadedSize = e.attribute("DownloadedSize").toULongLong(); m_uploadedSize = e.attribute("UploadedSize").toULongLong(); m_percent = (m_totalSize ? ((100.0 * m_downloadedSize) / m_totalSize) : 0); if ((m_totalSize == m_downloadedSize) && (m_totalSize != 0)) { setStartStatus(Job::Finished); setStatus(startStatus()); } else { setStatus(status(), i18nc("transfer state: stopped", "Stopped"), SmallIcon("process-stop")); setStartStatus(status()); } setUploadLimit(e.attribute("UploadLimit").toInt(), Transfer::VisibleSpeedLimit); setDownloadLimit(e.attribute("DownloadLimit").toInt(), Transfer::VisibleSpeedLimit); m_runningSeconds = e.attribute("ElapsedTime").toInt(); if (Settings::startupAction() == 1) { setPolicy(Job::Start); } else if (Settings::startupAction() == 2) { setPolicy(Job::Stop); } else { if (e.attribute("Policy") == "Start") setPolicy(Job::Start); else if (e.attribute("Policy") == "Stop") setPolicy(Job::Stop); else setPolicy(Job::None); } } void Transfer::setStatus(Job::Status jobStatus, const QString &text, const QPixmap &pix) { const bool statusChanged = (status() != jobStatus); QString statusText = text; if (statusText.isEmpty()) { statusText = i18nc(STATUSTEXTS[jobStatus].context, STATUSTEXTS[jobStatus].name); } //always prefer pix, if it is set if (!pix.isNull()) { m_statusPixmap = pix; } else if (statusChanged || m_statusPixmap.isNull()) { m_statusPixmap = SmallIcon(STATUSICONS[jobStatus]); } m_statusText = statusText; if (jobStatus == Job::Running && status() != Job::Running) { m_runningTime.restart(); m_runningTime.addSecs(m_runningSeconds); } if (jobStatus != Job::Running && status() == Job::Running) m_runningSeconds = m_runningTime.elapsed() / 1000; /** * It's important to call job::setStatus AFTER having changed the * icon or the text or whatever. * This because this function also notifies about this change * the scheduler which could also decide to change it another time * as well. For example if a job status is set to Aborted, the scheduler * could mark it to Delayed. This could trigger another icon or text * change which would be the right one since the status of the Job * has changed. If we set the icon or text after calling setStatus(), * we can overwrite the last icon or text change. */ Job::setStatus(jobStatus); } void Transfer::setTransferChange(ChangesFlags change, bool postEvent) { if (change & Tc_DownloadedSize || change & Tc_Status) { change = change | Tc_RemainingTime; } handler()->setTransferChange(change, postEvent); } QString Transfer::statusText(Job::Status status) { return i18nc(STATUSTEXTS[status].context, STATUSTEXTS[status].name); } QPixmap Transfer::statusPixmap(Job::Status status) { return SmallIcon(STATUSICONS[status]); } diff --git a/core/transfer.h b/core/transfer.h index 2443d064..df8fa428 100644 --- a/core/transfer.h +++ b/core/transfer.h @@ -1,384 +1,384 @@ /* This file is part of the KDE project Copyright (C) 2004 Dario Massarin Copyright (C) 2008 - 2011 Lukas Appelhans 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. */ #ifndef TRANSFER_H #define TRANSFER_H #include "job.h" #include "kget_export.h" #include #include #include -#include +#include class QDomElement; class Signature; class TransferHandler; class TransferFactory; class TransferGroup; class Scheduler; class TransferTreeModel; class NepomukHandler; class FileModel; class Verifier; class KGET_EXPORT Transfer : public Job { Q_OBJECT friend class TransferHandler; friend class TransferTreeModel; public: /** * Here we define the flags that should be shared by all the transfers. * A transfer should also be able to define additional flags, in the future. */ enum TransferChange { Tc_None = 0x00000000, // These flags respect the Model columns order NOTE: The model only checks the last 8 bits, so all values which need to be updated by the model should look like: 0x000000xx Tc_Source = 0x00000001, Tc_FileName = 0x00000002, Tc_Status = 0x00000004, Tc_TotalSize = 0x00000008, Tc_Percent = 0x00000010, Tc_DownloadSpeed = 0x00000020, Tc_RemainingTime = 0x00000040, // Misc Tc_UploadSpeed = 0x00000100, Tc_UploadLimit = 0x00000200, Tc_DownloadLimit = 0x00000400, Tc_CanResume = 0x00000800, Tc_DownloadedSize = 0x00001000, Tc_UploadedSize = 0x00002000, Tc_Log = 0x00004000, Tc_Group = 0x00008000, Tc_Selection = 0x00010000 }; enum Capability { Cap_SpeedLimit = 0x00000001, Cap_MultipleMirrors = 0x00000002, Cap_Resuming = 0x00000004, Cap_Renaming = 0x00000008, Cap_Moving = 0x00000010, Cap_FindFilesize = 0x00000020 }; Q_DECLARE_FLAGS(Capabilities, Capability) enum LogLevel { Log_Info, Log_Warning, Log_Error }; enum SpeedLimit { VisibleSpeedLimit = 0x01, InvisibleSpeedLimit = 0x02 }; enum DeleteOption { DeleteTemporaryFiles = 0x00000001, DeleteFiles = 0x00000002 }; Q_DECLARE_FLAGS(DeleteOptions, DeleteOption) typedef int ChangesFlags; Transfer(TransferGroup * parent, TransferFactory * factory, Scheduler * scheduler, const QUrl & src, const QUrl & dest, const QDomElement * e = nullptr); virtual ~Transfer(); /** * Returns the capabilities this Transfer supports */ Capabilities capabilities() const {return m_capabilities;} /** * This functions gets called whenever a Transfer gets created. As opposed * to init(), this isn't a virtual function and is not meant to be used in * transfer plugins */ void create(); /** * This functions gets called whenever a Transfer is going to be deleted. As opposed * to deinit(), this isn't a virtual function and is not meant to be used in * transfer plugins */ void destroy(DeleteOptions options); /** * This function is called after the creation of a Transfer * In transfer plugins you can put here whatever needs to be initialized * @note this function creates a NepomukHandler */ virtual void init(); /** * This function is called before the deletion of a Transfer * In transfer plugins you can put here whatever needs to be deinitialized */ virtual void deinit(DeleteOptions options) {Q_UNUSED(options);} /** * Tries to repair file * @param file the file of a download that should be repaired, * if not defined all files of a download are going to be repaird * @return true if a repair started, false if it was not nescessary */ virtual bool repair(const QUrl &file = QUrl()) {Q_UNUSED(file) return false;} const QUrl & source() const {return m_source;} const QUrl & dest() const {return m_dest;} /** * @returns all files of this transfer */ virtual QList files() const {return QList() << m_dest;} /** * @returns the directory the Transfer will be stored to */ virtual QUrl directory() const {return KIO::upUrl(m_dest);} /** * Move the download to the new destination * @param newDirectory is a directory where the download should be stored * @returns true if newDestination can be used */ virtual bool setDirectory(const QUrl &newDirectory); //Transfer status KIO::filesize_t totalSize() const {return m_totalSize;} KIO::filesize_t downloadedSize() const {return m_downloadedSize;} KIO::filesize_t uploadedSize() const {return m_uploadedSize;} QString statusText() const {return m_statusText;} QPixmap statusPixmap() const {return (error().pixmap.isNull() ? m_statusPixmap : error().pixmap);} static QString statusText(Job::Status status); static QPixmap statusPixmap(Job::Status status); int percent() const {return m_percent;} int downloadSpeed() const {return m_downloadSpeed;} int averageDownloadSpeed() const; int uploadSpeed() const {return m_uploadSpeed;} virtual int remainingTime() const override {return KIO::calculateRemainingSeconds(totalSize(), downloadedSize(), downloadSpeed());} virtual int elapsedTime() const override; virtual bool isStalled() const override {return (status() == Job::Running && downloadSpeed() == 0);} virtual bool isWorking() const override {return downloadSpeed() > 0;} /** * The mirrors that are available * bool if it is used, int how many paralell connections are allowed * to the mirror * @param file the file for which the availableMirrors should be get */ virtual QHash > availableMirrors(const QUrl &file) const; /** * Set the mirrors, int the number of parallel connections to the mirror * bool if the mirror should be used * @param file the file for which the availableMirrors should be set * @param mirrors the mirrors */ virtual void setAvailableMirrors(const QUrl &file, const QHash > &mirrors) {Q_UNUSED(file) Q_UNUSED(mirrors)} /** * Set the Transfer's UploadLimit * @note this is not displayed in any GUI, use setVisibleUploadLimit(int) instead * @param ulLimit upload Limit * @param limit speed limit */ void setUploadLimit(int ulLimit, SpeedLimit limit); /** * Set the Transfer's UploadLimit, which are displayed in the GUI * @note this is not displayed in any GUI, use setVisibleDownloadLimit(int) instead * @param dlLimit upload Limit * @param limit speed limit */ void setDownloadLimit(int dlLimit, SpeedLimit limit); /** * @return the UploadLimit, which is invisible in the GUI */ int uploadLimit(SpeedLimit limit) const; /** * @return the DownloadLimit, which is invisible in the GUI */ int downloadLimit(SpeedLimit limit) const; /** * Set the maximum share-ratio * @param ratio the new maximum share-ratio */ void setMaximumShareRatio(double ratio); /** * @return the maximum share-ratio */ double maximumShareRatio() {return m_ratio;} /** * Recalculate the share ratio */ void checkShareRatio(); bool isSelected() const {return m_isSelected;} /** * Transfer history */ const QStringList log() const; /** * Defines the order between transfers */ bool operator<(const Transfer& t2) const; /** * The owner group */ TransferGroup * group() const {return (TransferGroup *) m_jobQueue;} /** * @return the associated TransferHandler */ TransferHandler * handler(); /** * @returns the TransferTreeModel that owns this group */ TransferTreeModel * model(); /** * @returns a pointer to the TransferFactory object */ TransferFactory * factory() const {return m_factory;} /** * @returns a pointer to the FileModel containing all files of this download */ virtual FileModel * fileModel() {return nullptr;} /** * @param file for which to get the verifier * @return Verifier that allows you to add checksums manually verify a file etc. */ virtual Verifier * verifier(const QUrl &file) {Q_UNUSED(file) return nullptr;} /** * @param file for which to get the signature * @return Signature that allows you to add signatures and verify them */ virtual Signature * signature(const QUrl &file) {Q_UNUSED(file) return nullptr;} /** * Saves this transfer to the given QDomNode * * @param element The pointer to the QDomNode where the transfer will be saved */ virtual void save(const QDomElement &element); /** * Loads the transfer's info from the QDomElement * * @param element The pointer to the QDomNode where info will be loaded from */ virtual void load(const QDomElement *element); signals: /** * Emitted when the capabilities of the Transfer change */ void capabilitiesChanged(); public slots: /** * Set Transfer history */ void setLog(const QString& message, Transfer::LogLevel level = Log_Info); protected: /** * Sets the Job status to jobStatus, the status text to text and * the status pixmap to pix. */ void setStatus(Job::Status jobStatus, const QString &text = QString(), const QPixmap &pix = QPixmap()); /** * Sets the capabilities and automatically emits capabilitiesChanged */ void setCapabilities(Capabilities capabilities); /** * Makes the TransferHandler associated with this transfer know that * a change in this transfer has occurred. * * @param change the TransferChange flags to be set * @param postEvent whether the post event is taken into account */ virtual void setTransferChange(ChangesFlags change, bool postEvent=false); /** * Function used to set the SpeedLimits to the transfer */ virtual void setSpeedLimits(int uploadLimit, int downloadLimit) {Q_UNUSED(uploadLimit) Q_UNUSED(downloadLimit) } // --- Transfer information --- QUrl m_source; QUrl m_dest; QStringList m_log; KIO::filesize_t m_totalSize; KIO::filesize_t m_downloadedSize; KIO::filesize_t m_uploadedSize; int m_percent; int m_downloadSpeed; int m_uploadSpeed; int m_uploadLimit; int m_downloadLimit; bool m_isSelected; private: Capabilities m_capabilities; int m_visibleUploadLimit; int m_visibleDownloadLimit; int m_runningSeconds; double m_ratio; QString m_statusText; QPixmap m_statusPixmap; QTime m_runningTime; TransferHandler * m_handler; TransferFactory * m_factory; #ifdef HAVE_NEPOMUK NepomukHandler * m_nepomukHandler; #endif }; Q_DECLARE_OPERATORS_FOR_FLAGS(Transfer::Capabilities) Q_DECLARE_OPERATORS_FOR_FLAGS(Transfer::DeleteOptions) #endif diff --git a/core/transferdatasource.cpp b/core/transferdatasource.cpp index f745df01..bc4e69a1 100644 --- a/core/transferdatasource.cpp +++ b/core/transferdatasource.cpp @@ -1,90 +1,90 @@ /* This file is part of the KDE project Copyright (C) 2008 Manolo Valdes 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 "transferdatasource.h" #include "kget_debug.h" -#include +#include TransferDataSource::TransferDataSource(const QUrl &srcUrl, QObject *parent) : QObject(parent), m_sourceUrl(srcUrl), m_speed(0), m_supposedSize(0), m_paralellSegments(1), m_currentSegments(0), m_capabilities(0) { qCDebug(KGET_DEBUG) ; } TransferDataSource::~TransferDataSource() { qCDebug(KGET_DEBUG) ; } Transfer::Capabilities TransferDataSource::capabilities() const { return m_capabilities; } void TransferDataSource::setCapabilities(Transfer::Capabilities capabilities) { m_capabilities = capabilities; emit capabilitiesChanged(); } void TransferDataSource::findFileSize(KIO::fileoffset_t segmentSize) { Q_UNUSED(segmentSize); } QPair TransferDataSource::removeConnection() { return QPair(-1, -1); } QList > TransferDataSource::assignedSegments() const { return QList >(); } int TransferDataSource::countUnfinishedSegments() const { return 0; } QPair TransferDataSource::split() { return QPair(-1, -1); } int TransferDataSource::paralellSegments() const { return m_paralellSegments; } void TransferDataSource::setParalellSegments(int paralellSegments) { m_paralellSegments = paralellSegments; } int TransferDataSource::currentSegments() const { return m_currentSegments; } int TransferDataSource::changeNeeded() const { return paralellSegments() - currentSegments(); } diff --git a/core/transferdatasource.h b/core/transferdatasource.h index 0f5fc780..f1122c81 100644 --- a/core/transferdatasource.h +++ b/core/transferdatasource.h @@ -1,255 +1,255 @@ /* This file is part of the KDE project Copyright (C) 2008 Manolo Valdes 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. */ #ifndef TRANSFERDATASOURCE_H #define TRANSFERDATASOURCE_H #include "kget_export.h" #include "transfer.h" #include -#include +#include /** * This Class is an interface for inter-plugins data change. * allowing to use already implemented features from others plugins */ class KGET_EXPORT TransferDataSource : public QObject { Q_OBJECT public: TransferDataSource(const QUrl &srcUrl, QObject *parent); virtual ~TransferDataSource(); /** * @enum Error * @brief Error type enum */ enum Error { Unknown, WrongDownloadSize, NotResumeable }; /** * Returns the capabilities this TransferDataSource supports */ Transfer::Capabilities capabilities() const; virtual void start() = 0; virtual void stop() = 0; /** * Tries to find the filesize if this capability is supported, * if successful it emits foundFileSize(TransferDataSource*,KIO::filesize_t,QPair) * and assigns all segments to itself * if not successful it will try to download the file nevertheless * @note if stop is called and no size is found yet then this is aborted, i.e. needs to be * called again if start is later called * @param segmentSize the segments should have */ virtual void findFileSize(KIO::fileoffset_t segmentSize); /** * Adds multiple continuous segments that should be downloaded by this TransferDataSource * @param segmentSize first is always the general segmentSize, second the segmentSize * of the last segment in the range. If just one (the last) segment was assigned, then * first would not equal second, this is to ensure that first can be used to calculate the offset * TransferDataSources have to handle all that internally. * @param segmentRange first the beginning, second the end */ virtual void addSegments(const QPair &segmentSize, const QPair &segmentRange) = 0; /** * Removes one connection, useful when setMaximumParalellDownloads was called with a lower number * @return the segments that are removed (unassigned) now * */ virtual QPair removeConnection(); QUrl sourceUrl() const {return m_sourceUrl;}//TODO /** * returns the current speed of this data source * @return the speed */ ulong currentSpeed() const {return m_speed;} /** * Set the size the server used for downloading should report * @param supposedSize the size the file should have */ virtual void setSupposedSize(KIO::filesize_t supposedSize) {m_supposedSize = supposedSize;} /** * Returns the assignedSegments to this TransferDataSource * Each connection is represented by a QPair, where the first int is the beginning * segment and the last the ending segment * @note an empty list is returned by default, the elements can also be (-1, -1) */ virtual QList > assignedSegments() const; /** * Returns the number of unfinished Segments of the connection with the most * unfinished segments * Each TransferDataSource can have multiple connections and each connection * can have multiple segments assigned * @note default implementation returns 0 */ virtual int countUnfinishedSegments() const; /** * If a connection of this TransferDataSource is assigned multiple (continuous) segments, then * this method will split them (the unfinished ones) in half, it returns the beginning * and the end of the now unassigned segments; (-1, -1) if there are none * @note if only one segment is assigned to a connection split will also return (-1, -1) */ virtual QPair split();//TODO should split also take the current running segment into account? //the following methods are used for managing the number of parallel connections //subclasses have to keep track of the currentSegments /** * @return the number of parallel segments this DataSource is allowed to use, * default is 1 */ virtual int paralellSegments() const; /** * Sets the number of parallel segments this DataSource is allowed to use */ virtual void setParalellSegments(int paralellSegments); /** * @return the number of parallel segments this DataSources currently uses */ virtual int currentSegments() const; /** * Returns the mismatch of paralellSegments() and currentSegments() * @return the number of segments to add/remove e.g. -1 means one segment to remove */ virtual int changeNeeded() const; signals: /** * Emitted after findFileSize is called successfully * @param source that found the filesize * @param fileSize that was found * @param segmentRange that was calculated based on the segmentSize and that was assigned to * source automatically */ void foundFileSize(TransferDataSource *source, KIO::filesize_t fileSize, const QPair &segmentRange); /** * Emitted when the capabilities of the TransferDataSource change */ void capabilitiesChanged(); /** * Emitted when the TransferDataSource finished the download on its own, e.g. when findFileSize * is being called but no fileSize is found and instead the download finishes * @param source the source that emitted this signal * @param fileSize the fileSize of the finished file (calculated by the downloaded bytes) */ void finishedDownload(TransferDataSource *source, KIO::filesize_t fileSize); /** * Returns data in the forms of chucks * @note if the receiver set worked to wrong the TransferDataSource should cache the data * @param offset the offset in the file * @param data the downloaded data * @param worked if the receiver could handle the data, if not, the sender should cache the data */ void data(KIO::fileoffset_t offset, const QByteArray &data, bool &worked); /** * Returns data in the forms of URL List * @param data in form of QUrl list */ void data(const QList &data); /** * Returns found checksums with their type * @param type the type of the checksum * @param checksum the checksum */ void data(const QString type, const QString checksum); /** * emitted when there is no more data */ void finished(); /** * emitted when an assigned segment finishes * @param source the source that emitted this signal * @param segmentNumber the number of the segment, to identify it * @param connectionFinished true if all segments of this connection have been finished, * if one segment (instead of a group of segments) has been assigned this is always true */ void finishedSegment(TransferDataSource *source, int segmentNumber, bool connectionFinished = true); /** * Alert that datasource is no able to send any data * @param source the datasource, sending the signal * @param error the error type */ void broken(TransferDataSource *source, TransferDataSource::Error error); /** * emitted when an assigned segment is broken * @param source the source that emitted this signal * @param segmentRange the range of the segments e.g. (1,1,) or (0, 10) */ void brokenSegments(TransferDataSource *source, QPair segmentRange); /** * The speed of the download * @param speed speed of the download */ void speed(ulong speed); /** * Emitted when a Datasource itself decides to not download a specific segmentRange, * e.g. when there are too many connections for this TransferDataSource */ void freeSegments(TransferDataSource *source, QPair segmentRange); void log(const QString &message, Transfer::LogLevel logLevel); /** * Emitted when the filename of a url changes, e.g. when a link redirects */ void urlChanged(const QUrl &old, const QUrl &newUrl); protected: /** * Sets the capabilities and automatically emits capabilitiesChanged */ void setCapabilities(Transfer::Capabilities capabilities); private Q_SLOTS: virtual void slotSpeed(ulong speed) {Q_UNUSED(speed)} protected: QUrl m_sourceUrl; ulong m_speed; KIO::filesize_t m_supposedSize; int m_paralellSegments; int m_currentSegments; private: Transfer::Capabilities m_capabilities; }; #endif diff --git a/core/transfergroup.cpp b/core/transfergroup.cpp index c849f93a..26a4020f 100644 --- a/core/transfergroup.cpp +++ b/core/transfergroup.cpp @@ -1,360 +1,360 @@ /* This file is part of the KDE project Copyright (C) 2005 Dario Massarin Copyright (C) 2010 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/transfergroup.h" #include "core/transfergrouphandler.h" #include "core/kget.h" #include "kget_debug.h" -#include -#include +#include +#include #include TransferGroup::TransferGroup(TransferTreeModel * model, Scheduler * parent, const QString & name) : JobQueue(parent), m_model(model), m_name(name), m_totalSize(0), m_downloadedSize(0), m_uploadedSize(0), m_percent(0), m_downloadSpeed(0), m_uploadSpeed(0), m_downloadLimit(0), m_uploadLimit(0), m_visibleDownloadLimit(0), m_visibleUploadLimit(0), m_iconName("bookmark-new-list"), m_defaultFolder() { m_handler = new TransferGroupHandler(parent, this); } TransferGroup::~TransferGroup() { } int TransferGroup::downloadSpeed() { m_downloadSpeed = 0; foreach(Job *job, runningJobs()) { Transfer *transfer = static_cast(job); if (transfer) m_downloadSpeed += transfer->downloadSpeed(); } return m_downloadSpeed; } int TransferGroup::uploadSpeed() { m_uploadSpeed = 0; foreach(Job *job, runningJobs()) { Transfer *transfer = static_cast(job); if (transfer) m_uploadSpeed += transfer->uploadSpeed(); } return m_uploadSpeed; } bool TransferGroup::supportsSpeedLimits() { QList jobs = runningJobs(); foreach (Job *job, jobs) { Transfer * transfer = static_cast(job); if (!(transfer->capabilities() & Transfer::Cap_SpeedLimit)) { return false; } } //empty jobs can't support a speed limit return !jobs.isEmpty(); } void TransferGroup::setStatus(Status queueStatus) { JobQueue::setStatus(queueStatus); m_handler->setGroupChange(Gc_Status, true); } void TransferGroup::append(Transfer * transfer) { JobQueue::append(transfer); calculateSpeedLimits(); } void TransferGroup::append(const QList &transfers) { QList jobs; foreach (Transfer *transfer, transfers) { jobs << transfer; } JobQueue::append(jobs); calculateSpeedLimits(); } void TransferGroup::prepend(Transfer * transfer) { JobQueue::prepend(transfer); calculateSpeedLimits(); } void TransferGroup::insert(Transfer * transfer, Transfer * after) { JobQueue::insert(transfer, after); calculateSpeedLimits(); } void TransferGroup::remove(Transfer * transfer) { JobQueue::remove(transfer); calculateSpeedLimits(); } void TransferGroup::remove(const QList &transfers) { QList jobs; foreach (Transfer *transfer, transfers) { jobs << transfer; } JobQueue::remove(jobs); calculateSpeedLimits(); } void TransferGroup::move(Transfer * transfer, Transfer * after) { if(transfer == after) return; JobQueue::move(transfer, after); } Transfer * TransferGroup::findTransfer(const QUrl &src) { iterator it = begin(); iterator itEnd = end(); for(; it!=itEnd ; ++it) { Transfer * t = (Transfer *) *it; if( t->source().url() == src.url() ) return t; } return nullptr; } Transfer *TransferGroup::findTransferByDestination(const QUrl &dest) { iterator it = begin(); iterator itEnd = end(); for(; it!=itEnd ; ++it) { Transfer *t = (Transfer *) *it; if(t->dest().url() == dest.url()) { return t; } } return nullptr; } void TransferGroup::setUploadLimit(int ulLimit, Transfer::SpeedLimit limit) { if (limit == Transfer::VisibleSpeedLimit) { m_visibleUploadLimit = ulLimit; if (ulLimit < m_uploadLimit || m_uploadLimit == 0) m_uploadLimit = ulLimit; } else { m_uploadLimit = ulLimit; } calculateUploadLimit(); } void TransferGroup::setDownloadLimit(int dlLimit, Transfer::SpeedLimit limit) { if (limit == Transfer::VisibleSpeedLimit) { m_visibleDownloadLimit = dlLimit; if (dlLimit < m_downloadLimit || m_downloadLimit == 0) m_downloadLimit = dlLimit; } else { m_downloadLimit = dlLimit; } calculateDownloadLimit(); } int TransferGroup::uploadLimit(Transfer::SpeedLimit limit) const { if (limit == Transfer::VisibleSpeedLimit) return m_visibleUploadLimit; return m_uploadLimit; } int TransferGroup::downloadLimit(Transfer::SpeedLimit limit) const { if (limit == Transfer::VisibleSpeedLimit) return m_visibleDownloadLimit; return m_downloadLimit; } void TransferGroup::calculateSpeedLimits() { qCDebug(KGET_DEBUG) << "We will calculate the new SpeedLimits now"; calculateDownloadLimit(); calculateUploadLimit(); } void TransferGroup::calculateDownloadLimit() { qCDebug(KGET_DEBUG) << "Calculate new DownloadLimit of " + QString::number(m_downloadLimit); if (supportsSpeedLimits()) { const QList running = runningJobs(); int n = running.count(); int pool = 0;//We create a pool where we have some KiB/s to go to other transfer's... QList transfersNeedSpeed; foreach (Job *job, running) { Transfer * transfer = static_cast(job); if (transfer) { if (m_downloadLimit == 0 && transfer->downloadLimit(Transfer::VisibleSpeedLimit) != 0) continue; else if (m_downloadLimit == 0 && transfer->downloadLimit(Transfer::VisibleSpeedLimit) == 0) transfer->setDownloadLimit(0, Transfer::InvisibleSpeedLimit); else if (transfer->downloadLimit(Transfer::VisibleSpeedLimit) < m_downloadLimit / n && transfer->downloadLimit(Transfer::VisibleSpeedLimit) != 0) /*If the transfer's visible download limit is under the new one, we move the KiB/s which are different to the pool*/ pool = pool + (m_downloadLimit / n - transfer->downloadLimit(Transfer::VisibleSpeedLimit)); else if (transfer->downloadSpeed() + 10 < m_downloadLimit / n) { /*When the downloadSpeed of the transfer is under the new downloadLimit + 10 then we set the downloadLimit to the downloadSpeed + 10*/ pool = pool + m_downloadLimit / n - transfer->downloadSpeed() + 10; transfer->setDownloadLimit(transfer->downloadSpeed() + 10, Transfer::InvisibleSpeedLimit); } else { transfer->setDownloadLimit(m_downloadLimit / n, Transfer::InvisibleSpeedLimit); transfersNeedSpeed.append(transfer); } } } foreach (Transfer *transfer, transfersNeedSpeed) { transfer->setDownloadLimit(m_downloadLimit / n + pool / transfersNeedSpeed.count(), Transfer::InvisibleSpeedLimit); } } } void TransferGroup::calculateUploadLimit() { qCDebug(KGET_DEBUG) << "Calculate new Upload Limit of " + QString::number(m_uploadLimit); if (supportsSpeedLimits()) { const QList running = runningJobs(); int n = running.count(); int pool = 0;//We create a pool where we have some KiB/s to go to other transfer's... QList transfersNeedSpeed; foreach (Job *job, running) { Transfer * transfer = static_cast(job); if (transfer) { if (m_uploadLimit == 0 && transfer->uploadLimit(Transfer::VisibleSpeedLimit) != 0) continue; else if (m_uploadLimit == 0 && transfer->uploadLimit(Transfer::VisibleSpeedLimit) == 0) transfer->setUploadLimit(0, Transfer::InvisibleSpeedLimit); else if (transfer->uploadLimit(Transfer::VisibleSpeedLimit) < m_uploadLimit / n && transfer->uploadLimit(Transfer::VisibleSpeedLimit) != 0) /*If the transfer's visible upload limit is under the new one, we move the KiB/s which are different to the pool*/ pool = pool + (m_uploadLimit / n - transfer->uploadLimit(Transfer::VisibleSpeedLimit)); else if (transfer->uploadSpeed() + 10 < m_uploadLimit / n) { /*When the uploadSpeed of the transfer is under the new uploadLimit + 10 then we set the uploadLimit to the uploadSpeed + 10*/ pool = pool + m_uploadLimit / n - transfer->uploadSpeed() + 10; transfer->setUploadLimit(transfer->uploadSpeed() + 10, Transfer::InvisibleSpeedLimit); } else { transfer->setUploadLimit(m_uploadLimit / n, Transfer::InvisibleSpeedLimit); transfersNeedSpeed.append(transfer); } } } foreach (Transfer *transfer, transfersNeedSpeed) { transfer->setUploadLimit(m_uploadLimit / n + pool / transfersNeedSpeed.count(), Transfer::InvisibleSpeedLimit); } } } void TransferGroup::save(QDomElement e) // krazy:exclude=passbyvalue { //qCDebug(KGET_DEBUG) << " --> " << name(); e.setAttribute("Name", m_name); e.setAttribute("DefaultFolder", m_defaultFolder); e.setAttribute("DownloadLimit", m_visibleDownloadLimit); e.setAttribute("UploadLimit", m_visibleUploadLimit); e.setAttribute("Icon", m_iconName); e.setAttribute("Status", status() == JobQueue::Running ? "Running" : "Stopped"); e.setAttribute("RegExpPattern", m_regExp.pattern()); iterator it = begin(); iterator itEnd = end(); for( ; it!=itEnd; ++it ) { Transfer* transfer = static_cast(*it); qCDebug(KGET_DEBUG) << " --> " << name() << " transfer: " << transfer->source(); QDomElement t = e.ownerDocument().createElement("Transfer"); e.appendChild(t); transfer->save(t); } } void TransferGroup::load(const QDomElement & e) { qCDebug(KGET_DEBUG) << "TransferGroup::load"; m_name = e.attribute("Name"); m_defaultFolder = e.attribute("DefaultFolder"); m_visibleDownloadLimit = e.attribute("DownloadLimit").toInt(); m_visibleUploadLimit = e.attribute("UploadLimit").toInt(); if (!e.attribute("Icon").isEmpty()) m_iconName = e.attribute("Icon"); if (e.attribute("Status") == "Running") setStatus(JobQueue::Running); else setStatus(JobQueue::Stopped); m_regExp.setPattern(e.attribute("RegExpPattern")); QDomNodeList nodeList = e.elementsByTagName("Transfer"); int nItems = nodeList.length(); QList elements; for (int i = 0; i < nItems; ++i) { elements << nodeList.item(i).toElement(); } qCDebug(KGET_DEBUG) << "TransferGroup::load ->" << "add" << nItems << "transfers"; KGet::addTransfers(elements, name()); } diff --git a/core/transfergrouphandler.cpp b/core/transfergrouphandler.cpp index 5a4c4f32..975ba063 100644 --- a/core/transfergrouphandler.cpp +++ b/core/transfergrouphandler.cpp @@ -1,182 +1,181 @@ /* This file is part of the KDE project Copyright (C) 2005 Dario Massarin Copyright (C) 2008 Lukas Appelhans 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/transfergrouphandler.h" #include "core/kgetkjobadapter.h" #include "core/transferhandler.h" #include "core/transfertreemodel.h" #include "core/transfer.h" #include "core/kget.h" #include "kget_debug.h" -#include +#include #include -#include #include #include TransferGroupHandler::TransferGroupHandler(Scheduler * scheduler, TransferGroup * parent) : Handler(scheduler, parent), m_group(parent), m_changesFlags(Transfer::Tc_None) { } TransferGroupHandler::~TransferGroupHandler() { } void TransferGroupHandler::start() { qCDebug(KGET_DEBUG) << "TransferGroupHandler::start()"; m_group->setStatus( JobQueue::Running ); } void TransferGroupHandler::stop() { qCDebug(KGET_DEBUG) << "TransferGroupHandler::stop()"; m_group->setStatus( JobQueue::Stopped ); } void TransferGroupHandler::move(QList transfers, TransferHandler * after) { //Check that the given transfer (after) belongs to this group if( after && (after->group() != this) ) return; QList::iterator it = transfers.begin(); QList::iterator itEnd = transfers.end(); for( ; it!=itEnd ; ++it ) { //Move the transfers in the JobQueue if(after) m_group->move( (*it)->m_transfer, after->m_transfer ); else m_group->move( (*it)->m_transfer, nullptr ); after = *it; } } TransferHandler * TransferGroupHandler::operator[] (int i) { // qCDebug(KGET_DEBUG) << "TransferGroupHandler::operator[" << i << "]"; return (*m_group)[i]->handler(); } void TransferGroupHandler::setName(const QString &name) { m_group->setName(name); } QVariant TransferGroupHandler::data(int column) { // qCDebug(KGET_DEBUG) << "TransferGroupHandler::data(" << column << ")"; switch(column) { case 0: /*if (!m_group->supportsSpeedLimits() && (m_group->downloadLimit(Transfer::VisibleSpeedLimit) != 0 || m_group->uploadLimit(Transfer::VisibleSpeedLimit) != 0)) return name() + " - Does not supports SpeedLimits";//FIXME: Do a better text here else*/ return name(); case 2: if(m_group->size()) return i18np("1 Item", "%1 Items", m_group->size()); else return QString(); /* if (totalSize() != 0) return KIO::convertSize(totalSize()); else return i18nc("not available", "n/a");*/ case 3: // return QString::number(percent())+'%'; // display progressbar instead return QVariant(); case 4: if (downloadSpeed() == 0) { return QString(); } else return i18n("%1/s", KIO::convertSize(downloadSpeed())); default: return QVariant(); } } TransferGroup::ChangesFlags TransferGroupHandler::changesFlags() { return m_changesFlags; } void TransferGroupHandler::resetChangesFlags() { m_changesFlags = 0; } int TransferGroupHandler::indexOf(TransferHandler * transfer) { return m_group->indexOf(transfer->m_transfer); } const QList TransferGroupHandler::transfers() { QList transfers; TransferGroup::iterator it = m_group->begin(); TransferGroup::iterator itEnd = m_group->end(); for( ; it!=itEnd ; ++it ) { transfers.append((static_cast(*it))->handler()); } return transfers; } const QList & TransferGroupHandler::actions() { createActions(); return m_actions; } void TransferGroupHandler::setGroupChange(ChangesFlags change, bool notifyModel) { m_changesFlags |= change; if (notifyModel) m_group->model()->postDataChangedEvent(this); } void TransferGroupHandler::createActions() { if( !m_actions.empty() ) return; QAction *startAction = KGet::actionCollection()->addAction("transfer_group_start"); startAction->setText(i18nc("start transfergroup downloads", "Start")); startAction->setIcon(QIcon::fromTheme("media-playback-start")); QObject::connect(startAction, SIGNAL(triggered()), SLOT(start())); m_actions.append(startAction); QAction *stopAction = KGet::actionCollection()->addAction("transfer_group_stop"); stopAction->setText(i18nc("stop transfergroup downloads", "Stop")); stopAction->setIcon(QIcon::fromTheme("media-playback-pause")); QObject::connect(stopAction, SIGNAL(triggered()), SLOT(stop())); m_actions.append(stopAction); } diff --git a/core/transfergroupscheduler.cpp b/core/transfergroupscheduler.cpp index f89381d2..80a0276e 100644 --- a/core/transfergroupscheduler.cpp +++ b/core/transfergroupscheduler.cpp @@ -1,128 +1,128 @@ /* This file is part of the KDE project Copyright (C) 2008 Lukas Appelhans 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 "transfergroupscheduler.h" #include "kget.h" #include "transfergrouphandler.h" #include "settings.h" #include "kget_debug.h" -#include +#include TransferGroupScheduler::TransferGroupScheduler(QObject *parent) : Scheduler(parent), m_downloadLimit(0), m_uploadLimit(0) { } TransferGroupScheduler::~TransferGroupScheduler() { } void TransferGroupScheduler::calculateSpeedLimits() { calculateDownloadLimit(); calculateUploadLimit(); } void TransferGroupScheduler::calculateDownloadLimit() { int n = KGet::allTransferGroups().count(); int pool = 0;//We create a pool where we have some KiB/s to go to other groups... QList transfergroupsNeedSpeed; foreach (TransferGroupHandler *handler, KGet::allTransferGroups()) { if (!Settings::speedLimit()) handler->setDownloadLimit(handler->downloadLimit(Transfer::VisibleSpeedLimit), Transfer::InvisibleSpeedLimit); else if (handler->transfers().count() < 1) { pool = pool + downloadLimit() / n; } else if (downloadLimit() == 0 && handler->downloadLimit(Transfer::VisibleSpeedLimit) != 0) continue; else if (downloadLimit() == 0 && handler->downloadLimit(Transfer::VisibleSpeedLimit) == 0) handler->setDownloadLimit(0, Transfer::InvisibleSpeedLimit); else if (handler->downloadLimit(Transfer::VisibleSpeedLimit) < downloadLimit() / n && handler->downloadLimit(Transfer::VisibleSpeedLimit) != 0) /*If the handler's visible download limit is under the new one, we move the KiB/s which are different to the pool*/ pool = pool + (downloadLimit() / n - handler->downloadLimit(Transfer::VisibleSpeedLimit)); else if (handler->downloadSpeed() + 10 < downloadLimit() / n) { /*When the downloadSpeed of the handler is under the new downloadLimit + 10 then we set the downloadLimit to the downloadSpeed + 10*/ pool = pool + downloadLimit() / n - handler->downloadSpeed() + 10; handler->setDownloadLimit(handler->downloadSpeed() + 10, Transfer::InvisibleSpeedLimit); } else { handler->setDownloadLimit(downloadLimit() / n, Transfer::InvisibleSpeedLimit); transfergroupsNeedSpeed.append(handler); } } foreach (TransferGroupHandler *handler, transfergroupsNeedSpeed) { handler->setDownloadLimit(downloadLimit() / n + pool / transfergroupsNeedSpeed.count(), Transfer::InvisibleSpeedLimit); } } void TransferGroupScheduler::calculateUploadLimit() { int n = KGet::allTransferGroups().count(); qCDebug(KGET_DEBUG) << n; int pool = 0;//We create a pool where we have some KiB/s to go to other groups... QList transfergroupsNeedSpeed; foreach (TransferGroupHandler *handler, KGet::allTransferGroups()) { if (!Settings::speedLimit()) handler->setUploadLimit(handler->uploadLimit(Transfer::VisibleSpeedLimit), Transfer::InvisibleSpeedLimit); else if (handler->transfers().count() < 1) pool = pool + uploadLimit() / n; else if (uploadLimit() == 0 && handler->uploadLimit(Transfer::VisibleSpeedLimit) != 0) continue; else if (uploadLimit() == 0 && handler->uploadLimit(Transfer::VisibleSpeedLimit) == 0) handler->setUploadLimit(0, Transfer::InvisibleSpeedLimit); else if (handler->uploadLimit(Transfer::VisibleSpeedLimit) < uploadLimit() / n && handler->uploadLimit(Transfer::VisibleSpeedLimit) != 0) /*If the handler's visible download limit is under the new one, we move the KiB/s which are different to the pool*/ pool = pool + (uploadLimit() / n - handler->uploadLimit(Transfer::VisibleSpeedLimit)); else if (handler->uploadSpeed() + 10 < uploadLimit() / n) { /*When the downloadSpeed of the handler is under the new downloadLimit + 10 then we set the downloadLimit to the downloadSpeed + 10*/ pool = pool + uploadLimit() / n - handler->uploadSpeed() + 10; handler->setUploadLimit(handler->uploadSpeed() + 10, Transfer::InvisibleSpeedLimit); } else { handler->setUploadLimit(uploadLimit() / n, Transfer::InvisibleSpeedLimit); transfergroupsNeedSpeed.append(handler); } } foreach (TransferGroupHandler *handler, transfergroupsNeedSpeed) { handler->setUploadLimit(uploadLimit() / n + pool / transfergroupsNeedSpeed.count(), Transfer::InvisibleSpeedLimit); } } void TransferGroupScheduler::setDownloadLimit(int limit) { m_downloadLimit = limit; calculateDownloadLimit(); } void TransferGroupScheduler::setUploadLimit(int limit) { m_uploadLimit = limit; calculateUploadLimit(); } diff --git a/core/transferhandler.cpp b/core/transferhandler.cpp index 0cdd4c07..e99bfa90 100644 --- a/core/transferhandler.cpp +++ b/core/transferhandler.cpp @@ -1,226 +1,226 @@ /* This file is part of the KDE project Copyright (C) 2005 Dario Massarin 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/transferhandler.h" #include "core/job.h" #include "core/jobqueue.h" #include "core/transfergroup.h" #include "core/transfergrouphandler.h" #include "core/transfertreemodel.h" #include "core/plugin/transferfactory.h" #include "settings.h" #include "mainwindow.h" #include "kgetkjobadapter.h" #include "kget_debug.h" -#include +#include #include #include #include TransferHandler::TransferHandler(Transfer * parent, Scheduler * scheduler) : Handler(scheduler, parent), m_transfer(parent), m_changesFlags(Transfer::Tc_None) { static int dBusObjIdx = 0; m_dBusObjectPath = "/KGet/Transfers/" + QString::number(dBusObjIdx++); m_kjobAdapter = new KGetKJobAdapter(this, this); connect(m_transfer, SIGNAL(capabilitiesChanged()), this, SIGNAL(capabilitiesChanged())); } TransferHandler::~TransferHandler() { } Transfer::Capabilities TransferHandler::capabilities() const { return m_transfer->capabilities(); } void TransferHandler::start() { if(m_transfer->group()->status() == JobQueue::Running) { m_transfer->setPolicy(Job::None); KGet::model()->moveTransfer(m_transfer, m_transfer->group()); } else { m_transfer->setPolicy(Job::Start); } } void TransferHandler::stop() { if (m_transfer->group()->status() == JobQueue::Stopped) { m_transfer->setPolicy(Job::None); } else { m_transfer->setPolicy(Job::Stop); } } int TransferHandler::elapsedTime() const { return m_transfer->elapsedTime(); } int TransferHandler::remainingTime() const { return m_transfer->remainingTime(); } KIO::filesize_t TransferHandler::totalSize() const { return m_transfer->totalSize(); } KIO::filesize_t TransferHandler::downloadedSize() const { return m_transfer->downloadedSize(); } KIO::filesize_t TransferHandler::uploadedSize() const { return m_transfer->uploadedSize(); } int TransferHandler::percent() const { return m_transfer->percent(); } int TransferHandler::downloadSpeed() const { return m_transfer->downloadSpeed(); } int TransferHandler::averageDownloadSped() const { return m_transfer->averageDownloadSpeed(); } int TransferHandler::uploadSpeed() const { return m_transfer->uploadSpeed(); } QVariant TransferHandler::data(int column) { // qCDebug(KGET_DEBUG) << "TransferHandler::data(" << column << ")"; switch(column) { case 0: return dest().fileName(); case 1: if (status() == Job::Aborted && !error().text.isEmpty()) return error().text; return statusText(); case 2: if (totalSize() != 0) return KIO::convertSize(totalSize()); else return i18nc("not available", "n/a"); case 3: // return QString::number(percent())+'%'; // display progressbar instead return QVariant(); case 4: if (downloadSpeed() == 0) { if (m_transfer->isStalled()) return i18n("Stalled"); else return QString(); } else return i18n("%1/s", KIO::convertSize(downloadSpeed())); case 5: if (status() == Job::Running && downloadSpeed() != 0) return KIO::convertSeconds(remainingTime()); else return QString(); default: return QVariant(); } } void TransferHandler::setSelected( bool select ) { if( (select && !isSelected()) || (!select && isSelected()) ) { m_transfer->m_isSelected = select; setTransferChange( Transfer::Tc_Selection, true ); } } bool TransferHandler::isSelected() const { return m_transfer->m_isSelected; } Transfer::ChangesFlags TransferHandler::changesFlags() const { return m_changesFlags; } void TransferHandler::resetChangesFlags() { m_changesFlags = 0; } void TransferHandler::destroy() { qCDebug(KGET_DEBUG) << "TransferHandler::destroy() ENTERING"; qCDebug(KGET_DEBUG) << "TransferHandler::destroy() LEAVING"; } void TransferHandler::setTransferChange(ChangesFlags change, bool notifyModel) { m_changesFlags |= change; if (notifyModel) { // Notify the TransferTreeModel m_transfer->model()->postDataChangedEvent(this); m_kjobAdapter->slotUpdateDescription(); } } QList TransferHandler::contextActions() { QList actions; if (status() != Job::Finished) { actions << KGet::actionCollection()->action("start_selected_download") << KGet::actionCollection()->action("stop_selected_download"); } actions << KGet::actionCollection()->action("delete_selected_download") << KGet::actionCollection()->action("redownload_selected_download") << KGet::actionCollection()->action("select_all"); return actions; } QList TransferHandler::factoryActions() { QList actions; foreach(QAction *action, m_transfer->factory()->actions(this)) actions.append(action); return actions; } diff --git a/core/transferhistorystore.cpp b/core/transferhistorystore.cpp index a7d4943b..6c5dcdaf 100644 --- a/core/transferhistorystore.cpp +++ b/core/transferhistorystore.cpp @@ -1,149 +1,149 @@ /* This file is part of the KDE project Copyright (C) 2007 by Lukas Appelhans Copyright (C) 2008 by Javier Goday 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/transferhistorystore.h" #include "core/transferhistorystore_xml_p.h" #ifdef HAVE_SQLITE #include "core/transferhistorystore_sqlite_p.h" #endif #include "core/transfer.h" #include "settings.h" #include #include #include #include -#include +#include #include TransferHistoryItem::TransferHistoryItem() : QObject() {} TransferHistoryItem::TransferHistoryItem(const Transfer &transfer) : QObject() { setDest(transfer.dest().toLocalFile()); setSource(transfer.source().url()); setSize(transfer.totalSize()); setDateTime(QDateTime::currentDateTime()); setState(transfer.status()); } TransferHistoryItem::TransferHistoryItem(const TransferHistoryItem &item) : QObject() { setDest(item.dest()); setSource(item.source()); setState(item.state()); setSize(item.size()); setDateTime(item.dateTime()); } void TransferHistoryItem::setDest(const QString &dest) { m_dest = dest; } void TransferHistoryItem::setSource(const QString &source) { m_source = source; } void TransferHistoryItem::setState(int state) { m_state = state; } void TransferHistoryItem::setSize(int size) { m_size = size; } void TransferHistoryItem::setDateTime(const QDateTime &dateTime) { m_dateTime = dateTime; } QString TransferHistoryItem::dest() const { return m_dest; } QString TransferHistoryItem::source() const { return m_source; } int TransferHistoryItem::state() const { return m_state; } int TransferHistoryItem::size() const { return m_size; } QDateTime TransferHistoryItem::dateTime() const { return m_dateTime; } TransferHistoryItem& TransferHistoryItem::operator=(const TransferHistoryItem &item) { setDest(item.dest()); setSource(item.source()); setState(item.state()); setSize(item.size()); setDateTime(item.dateTime()); return *this; } bool TransferHistoryItem::operator==(const TransferHistoryItem& item) const { return dest() == item.dest() && source() == item.source(); } TransferHistoryStore::TransferHistoryStore() : QObject(), m_items() { } TransferHistoryStore::~TransferHistoryStore() { } QList TransferHistoryStore::items() const { return m_items; } TransferHistoryStore *TransferHistoryStore::getStore() { // make sure that the DataLocation directory exists (earlier this used to be handled by KStandardDirs) if (!QFileInfo::exists(QStandardPaths::writableLocation(QStandardPaths::DataLocation))) { QDir().mkpath(QStandardPaths::writableLocation(QStandardPaths::DataLocation)); } switch(Settings::historyBackend()) { case TransferHistoryStore::SQLite: #ifdef HAVE_SQLITE return new SQLiteStore(QStandardPaths::writableLocation(QStandardPaths::DataLocation) + QStringLiteral("/transferhistory.db")); break; #endif case TransferHistoryStore::Xml: default: return new XmlStore(QStandardPaths::writableLocation(QStandardPaths::DataLocation) + QStringLiteral("/transferhistory.kgt")); } } diff --git a/core/transferhistorystore_sqlite.cpp b/core/transferhistorystore_sqlite.cpp index 73d9cdc1..a9f8384f 100644 --- a/core/transferhistorystore_sqlite.cpp +++ b/core/transferhistorystore_sqlite.cpp @@ -1,159 +1,159 @@ /* This file is part of the KDE project Copyright (C) 2007 by Lukas Appelhans Copyright (C) 2008 by Javier Goday 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. */ #ifdef HAVE_SQLITE #include "core/transferhistorystore_sqlite_p.h" #include #include #include #include #include #include "kget_debug.h" -#include +#include SQLiteStore::SQLiteStore(const QString &database) : TransferHistoryStore(), m_dbName(database), m_sql() { } SQLiteStore::~SQLiteStore() { if (m_sql.isOpen()) { m_sql.close(); } } void SQLiteStore::load() { m_items.clear(); if (sql().open()) { if (!sql().tables().contains("transfer_history_item")) { createTables(); } QSqlQuery query = sql().exec("SELECT * FROM transfer_history_item"); if (query.lastError().isValid()) { qCDebug(KGET_DEBUG) << query.lastError().text(); } else { QSqlRecord rec = query.record(); while (query.next()) { TransferHistoryItem item; item.setDest(query.value(rec.indexOf("dest")).toString()); item.setSource(query.value(rec.indexOf("source")).toString()); item.setState(query.value(rec.indexOf("state")).toInt()); item.setDateTime(QDateTime::fromTime_t(query.value(rec.indexOf("time")).toUInt())); item.setSize(query.value(rec.indexOf("size")).toInt()); m_items << item; emit elementLoaded(query.at(), query.size(), item); } } } sql().close(); emit loadFinished(); } void SQLiteStore::clear() { QFile::remove(m_dbName); } void SQLiteStore::saveItem(const TransferHistoryItem &item) { saveItems(QList() << item); } void SQLiteStore::saveItems(const QList &items) { if (sql().open()) { if (!sql().tables().contains("transfer_history_item")) { createTables(); } if (!sql().transaction()) { qCWarning(KGET_DEBUG) << "Could not establish a transaction, might be slow."; } foreach (const TransferHistoryItem &item, items) { QSqlQuery query = sql().exec("insert into transfer_history_item(source, dest, size, time, state)" "values ('"+item.source()+"', '"+item.dest()+"', " + QString::number(item.size()) + ", " + QString::number(item.dateTime().toTime_t()) + ", '" + QString::number(item.state())+"')"); if (query.lastError().isValid()) { qCDebug(KGET_DEBUG) << query.lastError().text(); } m_items << item; } if (!sql().commit()) { qCWarning(KGET_DEBUG) << "Could not commit changes."; } } sql().close(); emit saveFinished(); } void SQLiteStore::deleteItem(const TransferHistoryItem &item) { if (sql().open()) { if (!sql().tables().contains("transfer_history_item")) { createTables(); } QSqlQuery query = sql().exec("delete from transfer_history_item where " " source = '" + item.source() + "';"); if (query.lastError().isValid()) { qCDebug(KGET_DEBUG) << query.lastError().text(); } sql().commit(); m_items.removeAll(item); } sql().close(); emit deleteFinished(); } QSqlDatabase SQLiteStore::sql() { if (!m_sql.isValid()) { m_sql = QSqlDatabase::addDatabase("QSQLITE"); m_sql.setDatabaseName(m_dbName); } return m_sql; } void SQLiteStore::createTables() { QSqlQuery query = sql().exec("CREATE TABLE transfer_history_item(dest VARCHAR NOT NULL, " "source VARCHAR NOT NULL, size int NOT NULL, time int not null, " "state int, PRIMARY KEY(dest, source));"); if (query.lastError().isValid()) { qCDebug(KGET_DEBUG) << query.lastError().text(); } } #endif diff --git a/core/transferhistorystore_xml.cpp b/core/transferhistorystore_xml.cpp index 38ade8f5..73f27be3 100644 --- a/core/transferhistorystore_xml.cpp +++ b/core/transferhistorystore_xml.cpp @@ -1,255 +1,255 @@ /* This file is part of the KDE project Copyright (C) 2007 - 2014 by Lukas Appelhans Copyright (C) 2008 by Javier Goday 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/transferhistorystore_xml_p.h" #include #include #include #include "kget_debug.h" -#include +#include XmlStore::SaveThread::SaveThread(QObject *parent, const QString &url, const QList &list) : QThread(parent), m_url(url), m_items(list), m_item() { } XmlStore::SaveThread::SaveThread(QObject *parent, const QString &url, const TransferHistoryItem &item) : QThread(parent), m_url(url), m_items(), m_item(item) { } void XmlStore::SaveThread::run() { QFile file(m_url); QDomDocument *doc; QDomElement root; if (!file.exists()) { doc = new QDomDocument("Transfers"); root = doc->createElement("Transfers"); doc->appendChild(root); } else { doc = new QDomDocument(); doc->setContent(&file); file.close(); root = doc->documentElement(); doc->appendChild(root); } QDomElement e = doc->createElement("Transfer"); root.appendChild(e); e.setAttribute("Source", m_item.source()); e.setAttribute("Dest", m_item.dest()); e.setAttribute("Time", QDateTime::currentDateTime().toTime_t()); e.setAttribute("Size", QString::number(m_item.size())); e.setAttribute("State", QString::number(m_item.state())); if (file.open(QFile::WriteOnly | QFile::Truncate)) { QTextStream stream( &file ); doc->save( stream, 0 ); file.close(); } delete doc; } XmlStore::DeleteThread::DeleteThread(QObject *parent, const QString &url, const TransferHistoryItem &item) : QThread(parent), m_url(url), m_item(item), m_items() { } void XmlStore::DeleteThread::run() { QDomDocument doc("tempHistory"); QFile file(m_url); QString error; int line; int column; if (!doc.setContent(&file, &error, &line, &column)) { qCDebug(KGET_DEBUG) << "Error1" << error << line << column; return; } file.close(); QDomElement root = doc.documentElement(); QDomNodeList list = root.elementsByTagName("Transfer"); int nItems = list.length(); for (int i = 0 ; i < nItems ; i++) { QDomElement element = list.item(i).toElement(); if(QString::compare(element.attribute("Source"), m_item.source()) == 0) { root.removeChild(element); } else { TransferHistoryItem item; item.setDest(element.attribute("Dest")); item.setSource(element.attribute("Source")); item.setSize(element.attribute("Size").toInt()); item.setDateTime(QDateTime::fromTime_t(element.attribute("Time").toUInt())); item.setState(element.attribute("State").toInt()); m_items << item; } } if (file.open(QFile::WriteOnly | QFile::Truncate)) { QTextStream stream( &file ); doc.save(stream, 0); file.close(); doc.clear(); } } XmlStore::LoadThread::LoadThread(QObject *parent, const QString &url) : QThread(parent), m_url(url) { } void XmlStore::LoadThread::run() { qRegisterMetaType("TransferHistoryItem"); QDomDocument doc("tempHistory"); QFile file(m_url); QString error; int line; int column; int total; if (!doc.setContent(&file, &error, &line, &column)) { qCDebug(KGET_DEBUG) << "Error1" << error << line << column; file.close(); return; } QDomElement root = doc.documentElement(); total = root.childNodes().size(); QDomNodeList list = root.elementsByTagName("Transfer"); int nItems = list.length(); for (int i = 0 ; i < nItems ; i++) { QDomElement dom = list.item(i).toElement(); TransferHistoryItem item; item.setDest(dom.attribute("Dest")); item.setSource(dom.attribute("Source")); item.setSize(dom.attribute("Size").toInt()); item.setDateTime(QDateTime::fromTime_t(dom.attribute("Time").toUInt())); item.setState(dom.attribute("State").toInt()); emit elementLoaded(i, total, item); } doc.clear(); file.close(); } XmlStore::XmlStore(const QString &url) : TransferHistoryStore(), m_storeUrl(url), m_loadThread(nullptr), m_saveThread(nullptr), m_deleteThread(nullptr) { } XmlStore::~XmlStore() { if(m_loadThread && m_loadThread->isRunning()) { m_loadThread->terminate(); } if(m_saveThread && m_saveThread->isRunning()) { m_saveThread->terminate(); } if(m_deleteThread && m_deleteThread->isRunning()) { m_deleteThread->terminate(); } delete m_loadThread; delete m_saveThread; delete m_deleteThread; } void XmlStore::load() { m_items.clear(); // TODO: only load if necessary m_loadThread = new XmlStore::LoadThread(this, m_storeUrl); connect(m_loadThread, SIGNAL(finished()), SIGNAL(loadFinished())); connect(m_loadThread, SIGNAL(elementLoaded(int,int,TransferHistoryItem)), SIGNAL(elementLoaded(int,int,TransferHistoryItem))); connect(m_loadThread, SIGNAL(elementLoaded(int,int,TransferHistoryItem)), SLOT(slotLoadElement(int,int,TransferHistoryItem))); m_loadThread->start(); } void XmlStore::clear() { QFile::remove(m_storeUrl); } void XmlStore::saveItem(const TransferHistoryItem &item) { m_saveThread = new XmlStore::SaveThread(this, m_storeUrl, item); connect(m_saveThread, SIGNAL(finished()), SIGNAL(saveFinished())); connect(m_saveThread, SIGNAL(elementLoaded(int,int,TransferHistoryItem)), SIGNAL(elementLoaded(int,int,TransferHistoryItem))); m_saveThread->start(); } void XmlStore::deleteItem(const TransferHistoryItem &item) { Q_UNUSED(item) m_deleteThread = new XmlStore::DeleteThread(this, m_storeUrl, item); connect(m_deleteThread, SIGNAL(finished()), SLOT(slotDeleteElement())); m_deleteThread->start(); } void XmlStore::slotLoadElement(int number, int total, const TransferHistoryItem &item) { Q_UNUSED(number) Q_UNUSED(total) m_items.append(item); } void XmlStore::slotDeleteElement() { m_items.clear(); m_items << m_deleteThread->items(); emit loadFinished(); } diff --git a/core/transfertreemodel.cpp b/core/transfertreemodel.cpp index 77014683..e3a7110b 100644 --- a/core/transfertreemodel.cpp +++ b/core/transfertreemodel.cpp @@ -1,793 +1,794 @@ /* This file is part of the KDE project Copyright (C) 2006 Dario Massarin Copyright (C) 2009 Lukas Appelhans Copyright (C) 2010 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/transfertreemodel.h" #include "core/kget.h" #include "core/transfertreeselectionmodel.h" #include "core/transfergrouphandler.h" #include "core/transfergroup.h" #include "core/transferhandler.h" #include "core/transfer.h" #include "transferadaptor.h" #include "dbus/dbustransferwrapper.h" #include "settings.h" #include "transfergroupscheduler.h" #include "kget_debug.h" -#include -#include +#include + +#include #include ItemMimeData::ItemMimeData() : QMimeData() { } ItemMimeData::~ItemMimeData() { } void ItemMimeData::appendTransfer(const QWeakPointer &transfer) { m_transfers.append(transfer); } QList > ItemMimeData::transfers() const { return m_transfers; } ModelItem::ModelItem(Handler * handler) : QStandardItem(), m_handler(handler) { } ModelItem::~ModelItem() { } void ModelItem::emitDataChanged() { QStandardItem::emitDataChanged(); } Handler * ModelItem::handler() { return m_handler; } bool ModelItem::isGroup() { return false; } GroupModelItem * ModelItem::asGroup() { return dynamic_cast(this); } TransferModelItem * ModelItem::asTransfer() { return dynamic_cast(this); } TransferModelItem::TransferModelItem(TransferHandler *handler) : ModelItem(handler), m_transferHandler(handler) { } TransferModelItem::~TransferModelItem() { } QVariant TransferModelItem::data(int role) const { if (role == Qt::DisplayRole) return m_transferHandler->data(column()); else if (role == Qt::DecorationRole) { switch (column()) { case 0: { //store the icon for speed improvements, KIconLoader should make sure, that //the icon data gets shared if (m_mimeType.isNull()) { m_mimeType = QIcon::fromTheme(KIO::iconNameForUrl(m_transferHandler->dest().url())); } return m_mimeType; } case 1: return m_transferHandler->statusPixmap(); } } if (role == Qt::TextAlignmentRole) { switch (column()) { case 0: // name return QVariant(Qt::AlignLeft | Qt::AlignVCenter); default: return Qt::AlignCenter; } } // KextendableItemDelegate::ShowExtensionIndicatorRole // tells the KExtendableItemDelegate which column contains the extender icon if (role == Qt::UserRole + 200) { if (column() == 0) return true; else return false; } return QVariant(); } TransferHandler * TransferModelItem::transferHandler() { return m_transferHandler; } GroupModelItem::GroupModelItem(TransferGroupHandler *handler) : ModelItem(handler), m_groupHandler(handler) { } GroupModelItem::~GroupModelItem() { } QVariant GroupModelItem::data(int role) const { if (role == Qt::DisplayRole) { return m_groupHandler->data(column()); } if (role == Qt::TextAlignmentRole) { switch (column()) { case 0: // name return Qt::AlignVCenter; case 2: // size case 3: // speed case 4: //progress return Qt::AlignCenter; default: return QVariant(Qt::AlignLeft | Qt::AlignBottom); } } if (role == Qt::DecorationRole && column() == 0) return m_groupHandler->pixmap(); return QVariant(); } TransferGroupHandler * GroupModelItem::groupHandler() { //qDebug() << m_groupHandler->name(); return m_groupHandler; } bool GroupModelItem::isGroup() { return true; } TransferTreeModel::TransferTreeModel(Scheduler * scheduler) : QStandardItemModel(), m_scheduler(scheduler), m_timerId(-1) { m_transferGroups.clear(); m_transfers.clear(); } TransferTreeModel::~TransferTreeModel() { } void TransferTreeModel::addGroup(TransferGroup * group) { QList items; for (int i = 0; i != group->handler()->columnCount(); i++) items << new GroupModelItem(group->handler()); appendRow(items); m_transferGroups.append(static_cast(items.first())); emit groupAddedEvent(group->handler()); KGet::m_scheduler->addQueue(group); } void TransferTreeModel::delGroup(TransferGroup * group) { if (m_transferGroups.count() <= 1) //If there is only one group left, we should not remove it return; GroupModelItem *item = itemFromTransferGroupHandler(group->handler()); if (!item) { return; } QList transfers; JobQueue::iterator it; JobQueue::iterator itEnd = group->end(); for (it = group->begin(); it != itEnd; ++it) { transfers << static_cast(*it); } delTransfers(transfers); m_transferGroups.removeAll(item); removeRow(item->row()); m_changedGroups.removeAll(group->handler()); emit groupRemovedEvent(group->handler()); KGet::m_scheduler->delQueue(group); } void TransferTreeModel::addTransfers(const QList &transfers, TransferGroup *group) { ModelItem *parentItem = itemFromTransferGroupHandler(group->handler()); beginInsertRows(parentItem->index(), parentItem->rowCount(), parentItem->rowCount() + transfers.count() - 1); //HACK blocks all signals from the model when adding multiple items, //that way rowsInserted gets only emitted once, and not constantly when doing appendRow //change this once there is a better way to append many transfers at once blockSignals(true); //now create and add the new items QList handlers; group->append(transfers); foreach (Transfer *transfer, transfers) { TransferHandler *handler = transfer->handler(); handlers << handler; QList items; for (int i = 0; i != handler->columnCount(); ++i) { items << new TransferModelItem(handler); } parentItem->appendRow(items); m_transfers.append(static_cast(items.first())); DBusTransferWrapper * wrapper = new DBusTransferWrapper(handler); new TransferAdaptor(wrapper); QDBusConnection::sessionBus().registerObject(handler->dBusObjectPath(), wrapper); } //notify the rest of the changes blockSignals(false); endInsertRows(); emit transfersAddedEvent(handlers); } void TransferTreeModel::delTransfers(const QList &t) { QList transfers = t; QList handlers; //find all valid items and sort them according to their groups QHash > groups; QHash > groupsTransfer; { QList::iterator it; QList::iterator itEnd = transfers.end(); for (it = transfers.begin(); it != itEnd; ) { TransferModelItem *item = itemFromTransferHandler((*it)->handler()); if (item) { handlers << (*it)->handler(); groups[(*it)->group()] << item; groupsTransfer[(*it)->group()] << *it; ++it; } else { it = transfers.erase(it); } } } emit transfersAboutToBeRemovedEvent(handlers); //remove the items from the model { QHash >::iterator it; QHash >::iterator itEnd = groups.end(); for (it = groups.begin(); it != itEnd; ++it) { const int numItems = (*it).count(); QStandardItem *parentItem = (*it).first()->parent(); QModelIndex parentIndex = parentItem->index(); if (numItems == parentItem->rowCount()) { for (int i = 0; i < numItems; ++i) { m_transfers.removeAll((*it)[i]); } removeRows(0, numItems, parentIndex); continue; } int rowStart = (*it).first()->row(); int numRows = 1; m_transfers.removeAll((*it).first()); for (int i = 1; i < numItems; ++i) { //previous item is neighbour if (rowStart + numRows == (*it)[i]->row()) { ++numRows; //no neighbour, so start again } else { removeRows(rowStart, numRows, parentIndex); rowStart = (*it)[i]->row(); numRows = 1; } m_transfers.removeAll((*it)[i]); } //remove last items removeRows(rowStart, numRows, parentIndex); } } foreach(Transfer *transfer, transfers) { QDBusConnection::sessionBus().unregisterObject(transfer->handler()->dBusObjectPath()); m_changedTransfers.removeAll(transfer->handler()); } { QHash >::iterator it; QHash >::iterator itEnd = groupsTransfer.end(); for (it = groupsTransfer.begin(); it != itEnd; ++it) { it.key()->remove(it.value()); } } emit transfersRemovedEvent(handlers); } TransferModelItem * TransferTreeModel::itemFromTransferHandler(TransferHandler * handler) { foreach (TransferModelItem * item, m_transfers) { if (handler == item->transferHandler()) return item; } return nullptr; } GroupModelItem * TransferTreeModel::itemFromTransferGroupHandler(TransferGroupHandler * handler) { foreach (GroupModelItem * item, m_transferGroups) { if (handler == item->groupHandler()) return item; } return nullptr; } ModelItem * TransferTreeModel::itemFromHandler(Handler * handler) { TransferHandler *transfer = qobject_cast(handler); if (transfer) { return itemFromTransferHandler(transfer); } return itemFromTransferGroupHandler(qobject_cast(handler)); } ModelItem * TransferTreeModel::itemFromIndex(const QModelIndex &index) const { QStandardItem *item = QStandardItemModel::itemFromIndex(index); if (item) return dynamic_cast(item); return nullptr; } void TransferTreeModel::moveTransfer(Transfer * transfer, TransferGroup * destGroup, Transfer * after) { if( (after) && (destGroup != after->group()) ) return; int position = transfer->group()->indexOf(transfer); TransferGroup * oldGroup = transfer->group(); bool sameGroup = false; if (destGroup == transfer->group()) { sameGroup = true; if (after) destGroup->move(transfer, after); else destGroup->move(transfer, nullptr); } else { transfer->group()->remove(transfer); if (after) destGroup->insert(transfer, after); else destGroup->prepend(transfer); transfer->m_jobQueue = destGroup; } QList items = itemFromHandler(oldGroup->handler())->takeRow(position); itemFromHandler(destGroup->handler())->insertRow(destGroup->indexOf(transfer), items); if (!sameGroup) emit transferMovedEvent(transfer->handler(), destGroup->handler()); KGet::selectionModel()->clearSelection(); } void TransferTreeModel::moveTransfer(TransferHandler *transfer, TransferGroupHandler *destGroup, TransferHandler *after) { Transfer *afterTransfer = nullptr; if (after) { afterTransfer = after->m_transfer; } moveTransfer(transfer->m_transfer, destGroup->m_group, afterTransfer); } QList TransferTreeModel::transferGroups() { QList transferGroups; foreach (GroupModelItem * item, m_transferGroups) { transferGroups << item->groupHandler()->m_group; } return transferGroups; } TransferGroup * TransferTreeModel::findGroup(const QString & groupName) { foreach (GroupModelItem * group, m_transferGroups) { if (group->groupHandler()->name() == groupName) return group->groupHandler()->m_group; } return nullptr; } Transfer * TransferTreeModel::findTransfer(const QUrl &src) { /*foreach (TransferGroup * group, transferGroups()) { Transfer * t = group->findTransfer(src); if (t) return t; }*/ foreach (TransferModelItem * transfer, m_transfers) { if (transfer->transferHandler()->source() == src) return transfer->transferHandler()->m_transfer; } return nullptr; } Transfer *TransferTreeModel::findTransferByDestination(const QUrl &dest) { /*foreach (TransferGroup * group, transferGroups()) { Transfer * t = group->findTransferByDestination(dest); if (t) return t; }*/ foreach (TransferModelItem * transfer, m_transfers) { if (transfer->transferHandler()->dest() == dest) return transfer->transferHandler()->m_transfer; } return nullptr; } Transfer * TransferTreeModel::findTransferByDBusObjectPath(const QString & dbusObjectPath) { foreach (TransferModelItem * transfer, m_transfers) { if (transfer->transferHandler()->dBusObjectPath() == dbusObjectPath) return transfer->transferHandler()->m_transfer; } return nullptr; } void TransferTreeModel::postDataChangedEvent(TransferHandler * transfer) { if(m_timerId == -1) m_timerId = startTimer(500); m_changedTransfers.append(transfer); } void TransferTreeModel::postDataChangedEvent(TransferGroupHandler * group) { if(m_timerId == -1) m_timerId = startTimer(500); m_changedGroups.append(group); } Qt::ItemFlags TransferTreeModel::flags (const QModelIndex & index) const { // qCDebug(KGET_DEBUG) << "TransferTreeModel::flags()"; if (!index.isValid()) return Qt::ItemIsEnabled; Qt::ItemFlags flags = Qt::ItemIsEnabled | Qt::ItemIsSelectable; if (!index.parent().isValid()) { if(index.column() == 0) flags |= Qt::ItemIsDropEnabled; } else flags |= Qt::ItemIsDragEnabled; //flags |= Qt::ItemIsDropEnabled; // We can edit all the groups but the default one if(index.row() > 0) { flags |= Qt::ItemIsEditable; } return flags; } QVariant TransferTreeModel::headerData(int section, Qt::Orientation orientation, int role) const { if (orientation == Qt::Horizontal && role == Qt::DisplayRole) { return columnName(section); } return QVariant(); } Qt::DropActions TransferTreeModel::supportedDropActions() const { return Qt::CopyAction | Qt::MoveAction; } QStringList TransferTreeModel::mimeTypes() const { QStringList types; types << "kget/transfer_pointer"; return types; } QMimeData * TransferTreeModel::mimeData(const QModelIndexList &indexes) const { ItemMimeData *mimeData = new ItemMimeData(); QModelIndexList sortedIndexes = indexes; qSort(sortedIndexes.begin(), sortedIndexes.end(), qGreater()); foreach (const QModelIndex &index, sortedIndexes) { if (index.isValid() && index.column() == 0 && index.parent().isValid()) { ModelItem *item = itemFromIndex(index); if (!item->isGroup()) { mimeData->appendTransfer(QWeakPointer(item->asTransfer()->transferHandler())); } } } mimeData->setData("kget/transfer_pointer", QByteArray()); return mimeData; } bool TransferTreeModel::dropMimeData(const QMimeData * mdata, Qt::DropAction action, int row, int column, const QModelIndex &parent) { if (action == Qt::IgnoreAction) return true; const ItemMimeData *itemData = qobject_cast(mdata); if (!itemData) { qCWarning(KGET_DEBUG) << "Unsuported mime data dropped."; return false; } TransferGroup *destGroup = findGroup(data(parent, Qt::DisplayRole).toString()); if (!destGroup) { qCWarning(KGET_DEBUG) << "No group could be found where the transfers should be inserted to."; return false; } if (parent.isValid()) qCDebug(KGET_DEBUG) << "TransferTreeModel::dropMimeData" << " " << row << " " << column << endl; QList > transfers = itemData->transfers(); qCDebug(KGET_DEBUG) << "TransferTreeModel::dropMimeData:" << transfers.count() << "transfers."; const bool droppedInsideGroup = parent.isValid(); Transfer * after = nullptr; for (int i = 0; i < transfers.count(); ++i) { bool b = destGroup->size() > row && row - 1 >= 0; if (b) qCDebug(KGET_DEBUG) << "TRANSFER AFTER:" << destGroup->operator[](row - 1)->source(); else qCDebug(KGET_DEBUG) << "TRANSFER AFTER NOT EXISTING"; if (!after) { bool rowValid = (row - 1 >= 0) && (destGroup->size() >= row); if (droppedInsideGroup && rowValid) { after = destGroup->operator[](row - 1);//insert at the correct position } } if (transfers[i].isNull()) { qWarning() << "The moved transfer has been deleted inbetween."; } else { moveTransfer(transfers[i].data()->m_transfer, destGroup, after); } } return true; } QString TransferTreeModel::columnName(int column) { switch(column) { case 0: return i18nc("name of download", "Name"); case 1: return i18nc("status of download", "Status"); case 2: return i18nc("size of download", "Size"); case 3: return i18nc("progress of download", "Progress"); case 4: return i18nc("speed of download", "Speed"); case 5: return i18nc("remaining time of download", "Remaining Time"); } return QString(); } int TransferTreeModel::column(Transfer::TransferChange flag) { switch(flag) { case Transfer::Tc_FileName: return 0; case Transfer::Tc_Status: return 1; case Transfer::Tc_TotalSize: return 2; case Transfer::Tc_Percent: return 3; case Transfer::Tc_DownloadSpeed: return 4; case Transfer::Tc_RemainingTime: return 5; default: return -1; } return -1; } int TransferTreeModel::column(TransferGroup::GroupChange flag) { switch(flag) { case TransferGroup::Gc_GroupName: return 0; case TransferGroup::Gc_Status: return 1; case TransferGroup::Gc_TotalSize: return 2; case TransferGroup::Gc_Percent: return 3; case TransferGroup::Gc_DownloadSpeed: return 4; default: return -1; } return -1; } void TransferTreeModel::timerEvent(QTimerEvent *event) { Q_UNUSED(event) // qCDebug(KGET_DEBUG) << "TransferTreeModel::timerEvent"; QMap updatedTransfers; QMap updatedGroups; foreach (TransferHandler * transfer, m_changedTransfers) { if (!updatedTransfers.contains(transfer)) { TransferGroupHandler * group = transfer->group(); ModelItem * item = itemFromHandler(group); Transfer::ChangesFlags changesFlags = transfer->changesFlags(); emit transfer->transferChangedEvent(transfer, changesFlags); int row = group->indexOf(transfer); // qCDebug(KGET_DEBUG) << "CHILD = " << item->child(row, column(Transfer::Tc_FileName)); // Now, check that model child items already exist (there are some cases when the transfer // can notify for changes before the gui has been correctly initialized) if(item->child(row, 0)) { if (changesFlags & Transfer::Tc_FileName) static_cast(item->child(row, column(Transfer::Tc_FileName)))->emitDataChanged(); if (changesFlags & Transfer::Tc_Status) static_cast(item->child(row, column(Transfer::Tc_Status)))->emitDataChanged(); if (changesFlags & Transfer::Tc_TotalSize) static_cast(item->child(row, column(Transfer::Tc_TotalSize)))->emitDataChanged(); if (changesFlags & Transfer::Tc_Percent) static_cast(item->child(row, column(Transfer::Tc_Percent)))->emitDataChanged(); if (changesFlags & Transfer::Tc_DownloadSpeed) static_cast(item->child(row, column(Transfer::Tc_DownloadSpeed)))->emitDataChanged(); if (changesFlags & Transfer::Tc_RemainingTime) static_cast(item->child(row, column(Transfer::Tc_RemainingTime)))->emitDataChanged(); transfer->resetChangesFlags(); updatedTransfers.insert(transfer,changesFlags); } } } if(!updatedTransfers.isEmpty()) emit transfersChangedEvent(updatedTransfers); foreach(TransferGroupHandler * group, m_changedGroups) { if(!updatedGroups.contains(group)) { TransferGroup::ChangesFlags changesFlags = group->changesFlags(); emit group->groupChangedEvent(group, changesFlags); int row = itemFromHandler(group)->row(); if (changesFlags & TransferGroup::Gc_GroupName) static_cast(item(row, column(TransferGroup::Gc_GroupName)))->emitDataChanged(); if (changesFlags & TransferGroup::Gc_Status) static_cast(item(row, column(TransferGroup::Gc_Status)))->emitDataChanged(); if (changesFlags & TransferGroup::Gc_TotalSize) static_cast(item(row, column(TransferGroup::Gc_TotalSize)))->emitDataChanged(); if (changesFlags & TransferGroup::Gc_Percent) static_cast(item(row, column(TransferGroup::Gc_Percent)))->emitDataChanged(); if (changesFlags & TransferGroup::Gc_DownloadSpeed) static_cast(item(row, column(TransferGroup::Gc_DownloadSpeed)))->emitDataChanged(); /*for(int i=0; i<8; i++) { if(((changesFlags >> i) & 0x00000001)) { QStandardItem *groupItem = itemFromHandler(group); dynamic_cast(invisibleRootItem()->child(groupItem->row(), i))->emitDataChanged(); //QModelIndex index = createIndex(m_transferGroups.indexOf(group->m_group), i, group); //emit dataChanged(index,index); } }*/ group->resetChangesFlags(); updatedGroups.insert(group, changesFlags); } } if(!updatedGroups.isEmpty()) emit groupsChangedEvent(updatedGroups); m_changedTransfers.clear(); m_changedGroups.clear(); killTimer(m_timerId); m_timerId = -1; } diff --git a/core/urlchecker.cpp b/core/urlchecker.cpp index 2cbb51d9..c34319de 100644 --- a/core/urlchecker.cpp +++ b/core/urlchecker.cpp @@ -1,827 +1,827 @@ /*************************************************************************** * Copyright (C) 2007-2014 Lukas Appelhans * * Copyright (C) 2008 Dario Freddi * * Copyright (C) 2010 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. * * * * 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 "urlchecker.h" #include "urlchecker_p.h" #include "mainwindow.h" #include "core/filedeleter.h" #include "core/kget.h" #include "core/transferhandler.h" #include "core/transfertreemodel.h" #include "settings.h" #include #include #include "kget_debug.h" -#include #include +#include +#include #include #include #include -#include #include #include #include #include ExistingTransferDialog::ExistingTransferDialog(const QString &text, const QString &caption, QWidget *parent) : QDialog(parent) { setWindowTitle(caption.isEmpty() ? i18n("Question") : caption); setModal(true); QVBoxLayout *layout = new QVBoxLayout; QHBoxLayout *bottomLayout = new QHBoxLayout; QLabel *label = new QLabel(text, this); layout->addWidget(label); layout->addWidget(new KSeparator(Qt::Horizontal, this)); m_applyAll = new QCheckBox(i18n("Appl&y to all"), this); bottomLayout->addStretch(1); bottomLayout->addWidget(m_applyAll); QDialogButtonBox *buttonBox = new QDialogButtonBox(this); buttonBox->setStandardButtons(QDialogButtonBox::Yes | QDialogButtonBox::No | QDialogButtonBox::Cancel); connect(buttonBox->button(QDialogButtonBox::Yes), &QPushButton::clicked, this, &ExistingTransferDialog::slotYesClicked); connect(buttonBox->button(QDialogButtonBox::No), &QPushButton::clicked, this, &ExistingTransferDialog::slotNoClicked); connect(buttonBox->button(QDialogButtonBox::Cancel), &QPushButton::clicked, this, &ExistingTransferDialog::slotCancelClicked); bottomLayout->addWidget(buttonBox); layout->addLayout(bottomLayout, 0); setLayout(layout); } void ExistingTransferDialog::slotYesClicked() { done(m_applyAll->isChecked() ? YesAll : Yes); } void ExistingTransferDialog::slotNoClicked() { done(m_applyAll->isChecked() ? NoAll : No); } void ExistingTransferDialog::slotCancelClicked() { done(Cancel); } UrlChecker::UrlChecker(UrlType type) : m_type(type), m_cancle(false), m_overwriteAll(false), m_autoRenameAll(false), m_skipAll(false) { } UrlChecker::~UrlChecker() { } ///Static methods following struct lessThan { bool operator()(const QUrl &lhs, const QUrl &rhs) const { return lhs.url() < rhs.url(); } }; void UrlChecker::removeDuplicates(QList &urls) { std::sort(urls.begin(), urls.end(), lessThan());//sort the urls, to find duplicates fast urls.erase(std::unique(urls.begin(), urls.end(), boost::bind(&QUrl::matches, _1, _2, QUrl::StripTrailingSlash | QUrl::NormalizePathSegments)), urls.end()); } UrlChecker::UrlError UrlChecker::checkUrl(const QUrl &url, const UrlChecker::UrlType type, bool showNotification) { switch (type) { case Source: return checkSource(url, showNotification); case Destination: return checkDestination(url, showNotification); case Folder: return checkFolder(url, showNotification); } return NoError; } bool UrlChecker::wouldOverwrite(const QUrl &source, const QUrl &dest) { return (dest.isLocalFile() && QFile::exists(dest.toLocalFile()) && source != dest && !FileDeleter::isFileBeingDeleted(dest)); } UrlChecker::UrlError UrlChecker::checkSource(const QUrl &src, bool showNotification) { //NOTE hasPath is not used, as this would dissallow adresses like http://www.kde.org/ as there is no path UrlError error = NoError; if (src.isEmpty()) { return Empty; } if ((error == NoError) && !src.isValid()) { error = Invalid; } if ((error == NoError) && src.scheme().isEmpty()){ error = NoProtocol; } /*if ((error == NoError) && !src.hasHost()) {//FIXME deactivated to allow file://....metalink etc error = NoHost; }*/ if (showNotification && (error != NoError)) { qCDebug(KGET_DEBUG) << "Source:" << src << "has error:" << error; KGet::showNotification(KGet::m_mainWindow, "error", message(src, Source, error)); } //TODO also check sourceUrl.url() != QUrl(sourceUrl.url()).fileName() as in NewTransferDialog::setSource? return error; } UrlChecker::UrlError UrlChecker::checkDestination(const QUrl &destination, bool showNotification) { UrlError error = NoError; if (destination.isEmpty()) { error = Empty; } if (error == NoError) { //not supposed to be a folder QFileInfo fileInfo(destination.toLocalFile()); if (!destination.isValid() || fileInfo.isDir()) { error = Invalid; } qDebug() << "Adjusted destination:" << destination.adjusted(QUrl::RemoveFilename).path(); if ((error == NoError) && !QFileInfo(destination.adjusted(QUrl::RemoveFilename).path()).isWritable()) { error = NotWriteable; } } qCDebug(KGET_DEBUG) << "Destination:" << destination << "has error:" << error; if (showNotification && (error != NoError)) { KGet::showNotification(KGet::m_mainWindow, "error", message(destination, Destination, error)); } return error; } UrlChecker::UrlError UrlChecker::checkFolder(const QUrl &folder, bool showNotification) { UrlError error = NoError; const QString destDir = folder.toLocalFile(); if (folder.isEmpty() || destDir.isEmpty()) { error = Empty; } if (error == NoError) { //has to be a folder QFileInfo fileInfo(destDir); if (!folder.isValid() || !fileInfo.isDir()) { error = Invalid; } //has to be writeable if ((error == NoError) && !fileInfo.isWritable()) { error = NotWriteable; } } if (showNotification && (error != NoError)) { qCDebug(KGET_DEBUG) << "Folder:" << folder << "has error:" << error; KGet::showNotification(KGet::m_mainWindow, "error", message(folder, Folder, error)); } return error; } QUrl UrlChecker::destUrl(const QUrl &destOrFolder, const QUrl &source, const QString &fileName) { QUrl dest = destOrFolder; if (QFileInfo(dest.toLocalFile()).isDir()) { QString usedFileName = (fileName.isEmpty() ? source.fileName() : fileName); if (usedFileName.isEmpty()) { usedFileName = QUrl::toPercentEncoding(source.toString(), "/"); } if (!dest.path().endsWith('/')) dest.setPath(dest.path() + '/'); dest.setPath(dest.adjusted(QUrl::RemoveFilename).path() + usedFileName); } else if (!fileName.isEmpty()) { dest.setPath(dest.adjusted(QUrl::RemoveFilename).path() + fileName); } return dest; } TransferHandler *UrlChecker::existingTransfer(const QUrl &url, const UrlChecker::UrlType type, UrlChecker::UrlWarning *warning) { UrlWarning temp; UrlWarning &warn = (warning ? (*warning) : temp); warn = NoWarning; switch (type) { case Source: return existingSource(url, warn); case Destination: return existingDestination(url, warn); default: return nullptr; } } TransferHandler *UrlChecker::existingSource(const QUrl &source, UrlChecker::UrlWarning &warning) { // Check if a transfer with the same url already exists Transfer *transfer = KGet::m_transferTreeModel->findTransfer(source); if (transfer) { if (transfer->status() == Job::Finished) { warning = ExistingFinishedTransfer; } else { warning = ExistingTransfer; } } return (transfer ? transfer->handler() : nullptr); } TransferHandler *UrlChecker::existingDestination(const QUrl &url, UrlChecker::UrlWarning &warning) { Transfer *transfer = KGet::m_transferTreeModel->findTransferByDestination(url); if (transfer) { if (transfer->status() == Job::Finished) { warning = ExistingFinishedTransfer; } else { warning = ExistingTransfer; } } else if (QFile::exists(url.toString())) { warning = ExistingFile; } return (transfer ? transfer->handler() : nullptr); } QString UrlChecker::message(const QUrl &url, const UrlChecker::UrlType type, const UrlChecker::UrlError error) { if (url.isEmpty()) { if (type == Folder) { switch (error) { case Empty: return i18n("No download directory specified."); case Invalid: return i18n("Invalid download directory specified."); case NotWriteable: return i18n("Download directory is not writeable."); default: return QString(); } } if (type == Destination) { switch (error) { case Empty: return i18n("No download destination specified."); case Invalid: return i18n("Invalid download destination specified."); case NotWriteable: return i18n("Download destination is not writeable."); default: return QString(); } } if (type == Source) { switch (error) { case Empty: return i18n("No URL specified."); case Invalid: return i18n("Malformed URL."); case NoProtocol: return i18n("Malformed URL, protocol missing."); case NoHost: return i18n("Malformed URL, host missing."); default: return QString(); } } } else { const QString urlString = url.toString(); if (type == Folder) { switch (error) { case Empty: return i18n("No download directory specified."); case Invalid: return i18n("Invalid download directory specified:\n%1", urlString); case NotWriteable: return i18n("Download directory is not writeable:\n%1", urlString); default: return QString(); } } if (type == Destination) { switch (error) { case Empty: return i18n("No download destination specified."); case Invalid: return i18n("Invalid download destination specified:\n%1", urlString); case NotWriteable: return i18n("Download destination is not writeable:\n%1", urlString); default: return QString(); } } if (type == Source) { switch (error) { case Empty: return i18n("No URL specified."); case Invalid: return i18n("Malformed URL:\n%1", urlString); case NoProtocol: return i18n("Malformed URL, protocol missing:\n%1", urlString); case NoHost: return i18n("Malformed URL, host missing:\n%1", urlString); default: return QString(); } } } return QString(); } QString UrlChecker::message(const QUrl &url, const UrlChecker::UrlType type, const UrlChecker::UrlWarning warning) { if (url.isEmpty()) { if (type == Destination) { switch (warning) { case ExistingFile: return i18n("File already exists. Overwrite it?"); case ExistingFinishedTransfer: return i18n("You have already downloaded that file from another location.\nDownload and delete the previous one?"); case ExistingTransfer: return i18n("You are already downloading that file from another location.\nDownload and delete the previous one?"); default: return QString(); } } if (type == Source) { switch (warning) { case ExistingFile: return i18n("File already exists. Overwrite it?"); case ExistingFinishedTransfer: return i18n("You have already completed a download from that location. Download it again?"); case ExistingTransfer: return i18n("You have a download in progress from that location.\nDelete it and download again?"); default: return QString(); } } } else { const QString urlString = url.toString(); if (type == Destination) { switch (warning) { case ExistingFile: return i18n("File already exists:\n%1\nOverwrite it?", urlString); case ExistingFinishedTransfer: return i18n("You have already downloaded that file from another location.\nDownload and delete the previous one?"); case ExistingTransfer: return i18n("You are already downloading that file from another location.\nDownload and delete the previous one?"); default: return QString(); } } if (type == Source) { switch (warning) { case ExistingFinishedTransfer: return i18n("You have already completed a download from the location: \n\n%1\n\nDownload it again?", urlString); case ExistingTransfer: return i18n("You have a download in progress from the location: \n\n%1\n\nDelete it and download again?", urlString); default: return QString(); } } } return QString(); } QString UrlChecker::message(const QList &urls, const UrlChecker::UrlType type, const UrlChecker::UrlError error) { QString urlsString; if (!urls.isEmpty()) { urlsString = urls.first().toString(); for (int i = 1; i < urls.count(); ++i) { urlsString += '\n' + urls[i].toString(); } urlsString = QString("

\%1

").arg(urlsString); } if (urls.isEmpty()) { if ((type == Folder) || (type == Destination)) { return message(QUrl(), type, error); } if (type == Source) { switch (error) { case Empty: return i18n("No URL specified."); case Invalid: return i18n("Malformed URLs."); case NoProtocol: return i18n("Malformed URLs, protocol missing."); case NoHost: return i18n("Malformed URLs, host missing."); default: return QString(); } } } else { switch (error) { case Empty: return i18n("No URL specified."); case Invalid: return i18n("Malformed URLs:\n%1", urlsString); case NoProtocol: return i18n("Malformed URLs, protocol missing:\n%1", urlsString); case NoHost: return i18n("Malformed URLs, host missing:\n%1", urlsString); case NotWriteable: return i18n("Destinations are not writable:\n%1", urlsString); default: return QString(); } } return QString(); } QString UrlChecker::message(const QList &urls, const UrlChecker::UrlType type, const UrlChecker::UrlWarning warning) { QString urlsString; if (!urls.isEmpty()) { urlsString = urls.first().toString(); for (int i = 1; i < urls.count(); ++i) { urlsString += '\n' + urls[i].toString(); } urlsString = QString("

\%1

").arg(urlsString); } if (urls.isEmpty()) { if (type == Destination) { switch (warning) { case ExistingFile: return i18n("Files exist already. Overwrite them?"); case ExistingFinishedTransfer: return i18n("You have already completed downloads at those destinations. Download them again?"); case ExistingTransfer: return i18n("You have downloads in progress to these destinations.\nDelete them and download again?"); default: return QString(); } } if (type == Source) { switch (warning) { case ExistingFinishedTransfer: return i18n("You have already completed downloads from these locations. Download them again?"); case ExistingTransfer: return i18n("You have downloads in progress from these locations.\nDelete them and download again?"); default: return QString(); } } } else { if (type == Destination) { switch (warning) { case ExistingFile: return i18n("Files exist already:\n%1\nOverwrite them?", urlsString); case ExistingFinishedTransfer: return i18n("You have already completed downloads at those destinations: \n\n%1\n\n Download them again?", urlsString); case ExistingTransfer: return i18n("You have downloads in progress to these destinations: \n\n%1\n\nDelete them and download again?", urlsString); default: return QString(); } } if (type == Source) { switch (warning) { case ExistingFinishedTransfer: return i18n("You have already completed downloads from these locations: \n\n%1\n\nDownload them again?", urlsString); case ExistingTransfer: return i18n("You have downloads in progress from these locations: \n\n%1\n\nDelete them and download again?", urlsString); default: QString(); } } } return QString(); } QList UrlChecker::hasExistingTransferMessages(const QList &urls, const UrlChecker::UrlType type) { UrlWarning warning; QHash > > splitWarnings; QList urlsToDownload; //collect all errors foreach(const QUrl &url, urls) { TransferHandler *transfer = existingTransfer(url, type, &warning); if (transfer) { splitWarnings[warning] << qMakePair(url, transfer); } else { urlsToDownload << url; } } //First ask about unfinished existing transfers QList >::const_iterator it; QList >::const_iterator itEnd; QList orderOfExecution; QList toDelete; orderOfExecution << ExistingTransfer << ExistingFinishedTransfer; for (int i = 0; i < orderOfExecution.count(); ++i) { warning = orderOfExecution[i]; if (splitWarnings.contains(warning)) { QList > existing = splitWarnings[warning]; itEnd = existing.constEnd(); bool isYesAll = false; bool isNoAll = false; for (it = existing.constBegin(); it != itEnd; ++it) { if (isYesAll) { urlsToDownload << it->first; toDelete << it->second; continue; } if (isNoAll) { break; } int result; if (Settings::filesOverwrite() || (Settings::filesAutomaticRename() && (warning != ExistingTransfer))) { result = ExistingTransferDialog::ExistingDialogReturn::YesAll; } else { result = hasExistingDialog(it->first, type, warning); } switch (result) { case ExistingTransferDialog::ExistingDialogReturn::YesAll: isYesAll = true; // fallthrough case ExistingTransferDialog::ExistingDialogReturn::Yes: urlsToDownload << it->first; toDelete << it->second; break; case ExistingTransferDialog::ExistingDialogReturn::NoAll: isNoAll = true; case ExistingTransferDialog::ExistingDialogReturn::No: break; case ExistingTransferDialog::ExistingDialogReturn::Cancel: default: removeTransfers(toDelete); return urlsToDownload; } } } } removeTransfers(toDelete); return urlsToDownload; } void UrlChecker::removeTransfers(const QList &toRemove) { QList transfers = toRemove; transfers.removeAll(nullptr); if (!transfers.isEmpty()) { KGet::delTransfers(transfers); } } int UrlChecker::hasExistingDialog(const QUrl &url, const UrlChecker::UrlType type, const UrlChecker::UrlWarning warning) { QWidget *parent = KGet::m_mainWindow; //getting the caption QString caption; if (type == Source) { switch (warning) { case ExistingFinishedTransfer: caption = i18n("Delete it and download again?"); break; case ExistingTransfer: caption = i18n("Download it again?"); break; default: break; } } else if (type == Destination) { switch (warning) { case ExistingFinishedTransfer: case ExistingTransfer: caption = i18n("File already downloaded. Download anyway?"); break; case ExistingFile: caption = i18n("File already exists"); break; default: break; } } QScopedPointer dialog(new ExistingTransferDialog(message(url, type, warning), caption, parent)); return dialog->exec(); } ///Non static methods following void UrlChecker::clear() { m_correctUrls.clear(); m_splitErrorUrls.clear(); m_cancle = false; m_overwriteAll = false; m_autoRenameAll = false; m_skipAll = false; } UrlChecker::UrlType UrlChecker::type() const { return m_type; } void UrlChecker::setType(UrlChecker::UrlType type) { clear(); m_type = type; } UrlChecker::UrlError UrlChecker::addUrl(const QUrl &url) { const UrlError error = checkUrl(url, m_type); if (error == NoError) { m_correctUrls << url; } else { m_splitErrorUrls[error] << url; } return error; } bool UrlChecker::addUrls(const QList &urls) { bool worked = true; foreach (const QUrl &url, urls) { const UrlError error = addUrl(url); if (error != NoError) { worked = false; } } return worked; } void UrlChecker::existingTransfers() { m_correctUrls = hasExistingTransferMessages(correctUrls(), m_type); } QUrl UrlChecker::checkExistingFile(const QUrl &source, const QUrl &destination) { QUrl newDestination = destination; //any url is ignored if (m_cancle) { return QUrl(); } if (Settings::filesOverwrite()) { m_overwriteAll = true; } else if (Settings::filesAutomaticRename()) { m_autoRenameAll = true; } if (wouldOverwrite(source, destination)) { KIO::RenameDialog_Options args = KIO::RenameDialog_MultipleItems | KIO::RenameDialog_Skip | KIO::RenameDialog_Overwrite; QScopedPointer dlg(new KIO::RenameDialog(KGet::m_mainWindow, i18n("File already exists"), source, destination, args)); ///in the following cases no dialog needs to be shown if (m_skipAll) { //only existing are ignored return QUrl(); } else if (m_overwriteAll) { FileDeleter::deleteFile(newDestination); return newDestination; } else if (m_autoRenameAll) { newDestination = dlg->autoDestUrl(); return newDestination; } ///now show the dialog and look at the result const int result = dlg->exec(); switch (result) { case KIO::R_OVERWRITE: { //delete the file, that way it won't show up in future calls of this method FileDeleter::deleteFile(newDestination); return newDestination; } case KIO::R_OVERWRITE_ALL: { //delete the file, that way it won't show up in future calls of this method FileDeleter::deleteFile(newDestination); m_overwriteAll = true; return newDestination; } case KIO::R_RENAME: //call it again, as there is no check on the user input return checkExistingFile(source, dlg->newDestUrl()); case KIO::R_AUTO_RENAME: newDestination = dlg->autoDestUrl(); m_autoRenameAll = true; return newDestination; case KIO::R_SKIP: return QUrl(); case KIO::R_AUTO_SKIP: m_skipAll = true; return QUrl(); case KIO::R_CANCEL: m_cancle = true; return QUrl(); default: return QUrl(); } } return newDestination; } QList UrlChecker::correctUrls() const { return m_correctUrls; } QList UrlChecker::errorUrls() const { QList urls; QHash>::const_iterator it; QHash>::const_iterator itEnd = m_splitErrorUrls.constEnd(); for (it = m_splitErrorUrls.constBegin(); it != itEnd; ++it) { urls << (*it); } return urls; } QHash> UrlChecker::splitErrorUrls() const { return m_splitErrorUrls; } void UrlChecker::displayErrorMessages() { QHash>::const_iterator it; QHash>::const_iterator itEnd = m_splitErrorUrls.constEnd(); for (it = m_splitErrorUrls.constBegin(); it != itEnd; ++it) { QString m; if (it->count() > 1) { m = message(*it, m_type, it.key()); } else if (!it->isEmpty()) { m = message(it->first(), m_type, it.key()); } if (!m.isEmpty()) { KGet::showNotification(KGet::m_mainWindow, "error", m); } } } diff --git a/core/verificationmodel.cpp b/core/verificationmodel.cpp index 5b752fac..3ab72cf9 100644 --- a/core/verificationmodel.cpp +++ b/core/verificationmodel.cpp @@ -1,221 +1,221 @@ /************************************************************************** * Copyright (C) 2009-2011 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. * * * * 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 "verificationmodel.h" #include "verifier.h" #include #include "kget_debug.h" -#include +#include #include #include struct VerificationModelPrivate { VerificationModelPrivate() { } ~VerificationModelPrivate() { } QStringList types; QStringList checksums; QList verificationStatus; }; VerificationModel::VerificationModel(QObject *parent) : QAbstractTableModel(parent), d(new VerificationModelPrivate) { } QVariant VerificationModel::data(const QModelIndex &index, int role) const { if (!index.isValid()) { return QVariant(); } if ((index.column() == VerificationModel::Type) && (role == Qt::DisplayRole)) { return d->types.at(index.row()); } else if ((index.column() == VerificationModel::Checksum) && (role == Qt::DisplayRole)) { return d->checksums.at(index.row()); } else if (index.column() == VerificationModel::Verified) { const int status = d->verificationStatus.at(index.row()); if (role == Qt::DecorationRole) { switch (status) { case Verifier::Verified: return QIcon::fromTheme("dialog-ok"); case Verifier::NotVerified: return QIcon::fromTheme("dialog-close"); case Verifier::NoResult: default: return QIcon::fromTheme(QString()); } } else if (role == Qt::EditRole) { return status; } } return QVariant(); } Qt::ItemFlags VerificationModel::flags(const QModelIndex &index) const { if (!index.isValid()) { return 0; } Qt::ItemFlags flags = Qt::ItemIsEnabled | Qt::ItemIsSelectable; if (index.column() == VerificationModel::Type) { flags |= Qt::ItemIsEditable; } else if (index.column() == VerificationModel::Checksum) { flags |= Qt::ItemIsEditable; } return flags; } bool VerificationModel::setData(const QModelIndex &index, const QVariant &value, int role) { if (!index.isValid() || index.row() >= d->types.count()) { return false; } if ((index.column() == VerificationModel::Type) && role == Qt::EditRole) { const QString type = value.toString(); if (Verifier::supportedVerficationTypes().contains(type) && !d->types.contains(type)) { d->types[index.row()] = type; emit dataChanged(index, index); return true; } } else if ((index.column() == VerificationModel::Checksum) && role == Qt::EditRole) { const QModelIndex typeIndex = index.sibling(index.row(), VerificationModel::Type); const QString type = typeIndex.data().toString(); const QString checksum = value.toString(); if (Verifier::isChecksum(type, checksum)) { d->checksums[index.row()] = checksum; emit dataChanged(index, index); return true; } } else if (index.column() == VerificationModel::Verified && role == Qt::EditRole) { d->verificationStatus[index.row()] = value.toInt(); emit dataChanged(index, index); return true; } return false; } int VerificationModel::rowCount(const QModelIndex &parent) const { Q_UNUSED(parent) return d->types.length(); } int VerificationModel::columnCount(const QModelIndex &parent) const { Q_UNUSED(parent) return 3; } QVariant VerificationModel::headerData(int section, Qt::Orientation orientation, int role) const { if ((orientation != Qt::Horizontal) || (role != Qt::DisplayRole)) { return QVariant(); } if (section == VerificationModel::Type) { return i18nc("the type of the hash, e.g. MD5", "Type"); } else if (section == VerificationModel::Checksum) { return i18nc("the used hash for verification", "Hash"); } else if (section == VerificationModel::Verified) { return i18nc("verification-result of a file, can be true/false", "Verified"); } return QVariant(); } bool VerificationModel::removeRows(int row, int count, const QModelIndex &parent) { if (parent.isValid() || (row < 0) || (count < 1) || (row + count > rowCount())) { return false; } beginRemoveRows(parent, row, row + count - 1); while (count) { d->types.removeAt(row); d->checksums.removeAt(row); d->verificationStatus.removeAt(row); --count; } endRemoveRows(); return true; } void VerificationModel::addChecksum(const QString &type, const QString &checksum, int verified) { if (!Verifier::isChecksum(type, checksum)) { qCWarning(KGET_DEBUG) << "Could not add checksum.\nType:" << type << "\nChecksum:" << checksum; return; } //if the hashtype already exists in the model, then replace it int position = d->types.indexOf(type); if (position > -1) { d->checksums[position] = checksum; const QModelIndex index = this->index(position, VerificationModel::Checksum, QModelIndex()); emit dataChanged(index, index); return; } int rows = rowCount(); beginInsertRows(QModelIndex(), rows, rows); d->types.append(type); d->checksums.append(checksum.toLower()); d->verificationStatus.append(verified); endInsertRows(); } void VerificationModel::addChecksums(const QHash &checksums) { QHash::const_iterator it; QHash::const_iterator itEnd = checksums.constEnd(); for (it = checksums.constBegin(); it != itEnd; ++it) { addChecksum(it.key(), it.value()); } } void VerificationModel::setVerificationStatus(const QString &type, int verified) { const int position = d->types.indexOf(type); if (position > -1) { d->verificationStatus[position] = verified; const QModelIndex index = this->index(position, VerificationModel::Verified, QModelIndex()); emit dataChanged(index, index); } } diff --git a/core/verificationthread.cpp b/core/verificationthread.cpp index 5b1db9d1..db2a1a2a 100644 --- a/core/verificationthread.cpp +++ b/core/verificationthread.cpp @@ -1,189 +1,189 @@ /************************************************************************** * Copyright (C) 2009-2011 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. * * * * 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 "verificationthread.h" #include "verifier.h" #include "kget_debug.h" -#include +#include #include VerificationThread::VerificationThread(QObject *parent) : QThread(parent), m_abort(false), m_length(0), m_type(Nothing) { } VerificationThread::~VerificationThread() { m_mutex.lock(); m_abort = true; m_mutex.unlock(); wait(); } void VerificationThread::verifiy(const QString &type, const QString &checksum, const QUrl &file) { QMutexLocker locker(&m_mutex); m_types.append(type); m_checksums.append(checksum); m_files.append(file); m_type = Verify; if (!isRunning()) { start(); } } void VerificationThread::findBrokenPieces(const QString &type, const QList checksums, KIO::filesize_t length, const QUrl &file) { QMutexLocker locker(&m_mutex); m_types.clear(); m_types.append(type); m_checksums = checksums; m_files.clear(); m_files.append(file); m_length = length; m_type = BrokenPieces; if (!isRunning()) { start(); } } void VerificationThread::run() { if (m_type == Nothing) { return; } if (m_type == Verify) { doVerify(); } else if (m_type == BrokenPieces) { doBrokenPieces(); } } void VerificationThread::doVerify() { m_mutex.lock(); bool run = m_files.count(); m_mutex.unlock(); while (run && !m_abort) { m_mutex.lock(); const QString type = m_types.takeFirst(); const QString checksum = m_checksums.takeFirst(); const QUrl url = m_files.takeFirst(); m_mutex.unlock(); if (type.isEmpty() || checksum.isEmpty()) { m_mutex.lock(); run = m_files.count(); m_mutex.unlock(); continue; } const QString hash = Verifier::checksum(url, type, &m_abort); qCDebug(KGET_DEBUG) << "Type:" << type << "Calculated checksum:" << hash << "Entered checksum:" << checksum; const bool fileVerified = (hash == checksum); if (m_abort) { return; } m_mutex.lock(); if (!m_abort) { emit verified(type, fileVerified, url); emit verified(fileVerified); } run = m_files.count(); m_mutex.unlock(); } } void VerificationThread::doBrokenPieces() { m_mutex.lock(); const QString type = m_types.takeFirst(); const QStringList checksums = m_checksums; m_checksums.clear(); const QUrl url = m_files.takeFirst(); const KIO::filesize_t length = m_length; m_mutex.unlock(); QList broken; if (QFile::exists(url.toString())) { QFile file(url.toString()); if (!file.open(QIODevice::ReadOnly)) { emit brokenPieces(broken, length); return; } const KIO::filesize_t fileSize = file.size(); if (!length || !fileSize) { emit brokenPieces(broken, length); return; } const QStringList fileChecksums = Verifier::partialChecksums(url, type, length, &m_abort).checksums(); if (m_abort) { emit brokenPieces(broken, length); return; } if (fileChecksums.size() != checksums.size()) { qCDebug(KGET_DEBUG) << "Number of checksums differs!"; emit brokenPieces(broken, length); return; } for (int i = 0; i < checksums.size(); ++i) { if (fileChecksums.at(i) != checksums.at(i)) { const int brokenStart = length * i; qCDebug(KGET_DEBUG) << url << "broken segment" << i << "start" << brokenStart << "length" << length; broken.append(brokenStart); } } } emit brokenPieces(broken, length); } diff --git a/core/verifier.cpp b/core/verifier.cpp index 89a385f7..48affc11 100644 --- a/core/verifier.cpp +++ b/core/verifier.cpp @@ -1,645 +1,645 @@ /************************************************************************** * Copyright (C) 2009-2011 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. * * * * 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 "verifier_p.h" #include "verificationmodel.h" #include "../dbus/dbusverifierwrapper.h" #include "verifieradaptor.h" #include "settings.h" #include #include #include "kget_debug.h" -#include +#include #ifdef HAVE_QCA2 #include #endif //TODO use mutable to make some methods const? const QStringList VerifierPrivate::SUPPORTED = (QStringList() << "sha512" << "sha384" << "sha256" << "ripmed160" << "sha1" << "md5" << "md4"); const QString VerifierPrivate::MD5 = QString("md5"); const int VerifierPrivate::DIGGESTLENGTH[] = {128, 96, 64, 40, 40, 32, 32}; const int VerifierPrivate::MD5LENGTH = 32; const int VerifierPrivate::PARTSIZE = 500 * 1024; VerifierPrivate::~VerifierPrivate() { delete model; qDeleteAll(partialSums.begin(), partialSums.end()); } QString VerifierPrivate::calculatePartialChecksum(QFile *file, const QString &type, KIO::fileoffset_t startOffset, int pieceLength, KIO::filesize_t fileSize, bool *abortPtr) { if (!file) { return QString(); } if (!fileSize) { fileSize = file->size(); } //longer than the file, so adapt it if (static_cast(fileSize) < startOffset + pieceLength) { pieceLength = fileSize - startOffset; } #ifdef HAVE_QCA2 QCA::Hash hash(type); //it can be that QCA2 does not support md5, e.g. when Qt is compiled locally QCryptographicHash md5Hash(QCryptographicHash::Md5); const bool useMd5 = (type == MD5); #else //NO QCA2 if (type != MD5) { return QString(); } QCryptographicHash hash(QCryptographicHash::Md5); #endif //HAVE_QCA2 //we only read 512kb each time, to save RAM int numData = pieceLength / PARTSIZE; KIO::fileoffset_t dataRest = pieceLength % PARTSIZE; if (!numData && !dataRest) { return QString(); } int k = 0; for (k = 0; k < numData; ++k) { if (!file->seek(startOffset + PARTSIZE * k)) { return QString(); } if (abortPtr && *abortPtr) { return QString(); } QByteArray data = file->read(PARTSIZE); #ifdef HAVE_QCA2 if (useMd5) { md5Hash.addData(data); } else { hash.update(data); } #else //NO QCA2 hash.addData(data); #endif //HAVE_QCA2 } //now read the rest if (dataRest) { if (!file->seek(startOffset + PARTSIZE * k)) { return QString(); } QByteArray data = file->read(dataRest); #ifdef HAVE_QCA2 if (useMd5) { md5Hash.addData(data); } else { hash.update(data); } #else //NO QCA2 hash.addData(data); #endif //HAVE_QCA2 } #ifdef HAVE_QCA2 return (useMd5 ? QString(md5Hash.result()) : QString(QCA::arrayToHex(hash.final().toByteArray()))); #else //NO QCA2 return QString(hash.result()); #endif //HAVE_QCA2 } QStringList VerifierPrivate::orderChecksumTypes(Verifier::ChecksumStrength strength) const { QStringList checksumTypes; if (strength == Verifier::Weak) { for (int i = SUPPORTED.count() - 1; i >= 0; --i) { checksumTypes.append(SUPPORTED.at(i)); } checksumTypes.move(0, 1); //md4 second position } else if (strength == Verifier::Strong) { for (int i = SUPPORTED.count() - 1; i >= 0; --i) { checksumTypes.append(SUPPORTED.at(i)); } checksumTypes.move(1, checksumTypes.count() - 1); //md5 second last position checksumTypes.move(0, checksumTypes.count() - 1); //md4 last position } else if (strength == Verifier::Strongest) { checksumTypes = SUPPORTED; } return checksumTypes; } Verifier::Verifier(const QUrl &dest, QObject *parent) : QObject(parent), d(new VerifierPrivate(this)) { d->dest = dest; d->status = NoResult; static int dBusObjIdx = 0; d->dBusObjectPath = "/KGet/Verifiers/" + QString::number(dBusObjIdx++); DBusVerifierWrapper *wrapper = new DBusVerifierWrapper(this); new VerifierAdaptor(wrapper); QDBusConnection::sessionBus().registerObject(d->dBusObjectPath, wrapper); qRegisterMetaType("KIO::filesize_t"); qRegisterMetaType("KIO::fileoffset_t"); qRegisterMetaType >("QList"); d->model = new VerificationModel(); connect(&d->thread, SIGNAL(verified(QString,bool,QUrl)), this, SLOT(changeStatus(QString,bool))); connect(&d->thread, SIGNAL(brokenPieces(QList,KIO::filesize_t)), this, SIGNAL(brokenPieces(QList,KIO::filesize_t))); } Verifier::~Verifier() { delete d; } QString Verifier::dBusObjectPath() const { return d->dBusObjectPath; } QUrl Verifier::destination() const { return d->dest; } void Verifier::setDestination(const QUrl &destination) { d->dest = destination; } Verifier::VerificationStatus Verifier::status() const { return d->status; } VerificationModel *Verifier::model() { return d->model; } QStringList Verifier::supportedVerficationTypes() { QStringList supported; #ifdef HAVE_QCA2 QStringList supportedTypes = QCA::Hash::supportedTypes(); for (int i = 0; i < VerifierPrivate::SUPPORTED.count(); ++i) { if (supportedTypes.contains(VerifierPrivate::SUPPORTED.at(i))) { supported << VerifierPrivate::SUPPORTED.at(i); } } #endif //HAVE_QCA2 if (!supported.contains(VerifierPrivate::MD5)) { supported << VerifierPrivate::MD5; } return supported; } int Verifier::diggestLength(const QString &type) { if (type == VerifierPrivate::MD5) { return VerifierPrivate::MD5LENGTH; } #ifdef HAVE_QCA2 if (QCA::isSupported(type.toLatin1())) { return VerifierPrivate::DIGGESTLENGTH[VerifierPrivate::SUPPORTED.indexOf(type)]; } #endif //HAVE_QCA2 return 0; } bool Verifier::isChecksum(const QString &type, const QString &checksum) { const int length = diggestLength(type); const QString pattern = QString("[0-9a-z]{%1}").arg(length); //needs correct length and only word characters if (length && (checksum.length() == length) && checksum.toLower().contains(QRegExp(pattern))) { return true; } return false; } QString Verifier::cleanChecksumType(const QString &type) { QString hashType = type.toUpper(); if (hashType.contains(QRegExp("^SHA\\d+"))) { hashType.insert(3, '-'); } return hashType; } bool Verifier::isVerifyable() const { return QFile::exists(d->dest.toString()) && d->model->rowCount(); } bool Verifier::isVerifyable(const QModelIndex &index) const { int row = -1; if (index.isValid()) { row = index.row(); } if (QFile::exists(d->dest.toString()) && (row >= 0) && (row < d->model->rowCount())) { return true; } return false; } Checksum Verifier::availableChecksum(Verifier::ChecksumStrength strength) const { Checksum pair; //check if there is at least one entry QModelIndex index = d->model->index(0, 0); if (!index.isValid()) { return pair; } const QStringList available = supportedVerficationTypes(); const QStringList supported = d->orderChecksumTypes(strength); for (int i = 0; i < supported.count(); ++i) { QModelIndexList indexList = d->model->match(index, Qt::DisplayRole, supported.at(i)); if (!indexList.isEmpty() && available.contains(supported.at(i))) { QModelIndex match = d->model->index(indexList.first().row(), VerificationModel::Checksum); pair.first = supported.at(i); pair.second = match.data().toString(); break; } } return pair; } QList Verifier::availableChecksums() const { QList checksums; for (int i = 0; i < d->model->rowCount(); ++i) { const QString type = d->model->index(i, VerificationModel::Type).data().toString(); const QString hash = d->model->index(i, VerificationModel::Checksum).data().toString(); checksums << qMakePair(type, hash); } return checksums; } QPair Verifier::availablePartialChecksum(Verifier::ChecksumStrength strength) const { QPair pair; QString type; PartialChecksums *checksum = nullptr; const QStringList available = supportedVerficationTypes(); const QStringList supported = d->orderChecksumTypes(strength); for (int i = 0; i < supported.size(); ++i) { if (d->partialSums.contains(supported.at(i)) && available.contains(supported.at(i))) { type = supported.at(i); checksum = d->partialSums[type]; break; } } return QPair(type, checksum); } void Verifier::changeStatus(const QString &type, bool isVerified) { qCDebug(KGET_DEBUG) << "Verified:" << isVerified; d->status = isVerified ? Verifier::Verified : Verifier::NotVerified; d->model->setVerificationStatus(type, d->status); emit verified(isVerified); } void Verifier::verify(const QModelIndex &index) { int row = -1; if (index.isValid()) { row = index.row(); } QString type; QString checksum; if (row == -1) { Checksum pair = availableChecksum(static_cast(Settings::checksumStrength())); type = pair.first; checksum = pair.second; } else if ((row >= 0) && (row < d->model->rowCount())) { type = d->model->index(row, VerificationModel::Type).data().toString(); checksum = d->model->index(row, VerificationModel::Checksum).data().toString(); } d->thread.verifiy(type, checksum, d->dest); } void Verifier::brokenPieces() const { QPair pair = availablePartialChecksum(static_cast(Settings::checksumStrength())); QList checksums; KIO::filesize_t length = 0; if (pair.second) { checksums = pair.second->checksums(); length = pair.second->length(); } d->thread.findBrokenPieces(pair.first, checksums, length, d->dest); } QString Verifier::checksum(const QUrl &dest, const QString &type, bool *abortPtr) { QStringList supported = supportedVerficationTypes(); if (!supported.contains(type)) { return QString(); } QFile file(dest.toString()); if (!file.open(QIODevice::ReadOnly)) { return QString(); } if (type == VerifierPrivate::MD5) { QCryptographicHash hash(QCryptographicHash::Md5); hash.addData(&file); QString final = QString(hash.result()); file.close(); return final; } #ifdef HAVE_QCA2 QCA::Hash hash(type); //BEGIN taken from qca_basic.h and slightly adopted to allow abort char buffer[1024]; int len; while ((len=file.read(reinterpret_cast(buffer), sizeof(buffer))) > 0) { hash.update(buffer, len); if (abortPtr && *abortPtr) { hash.final(); file.close(); return QString(); } } //END QString final = QString(QCA::arrayToHex(hash.final().toByteArray())); file.close(); return final; #endif //HAVE_QCA2 return QString(); } PartialChecksums Verifier::partialChecksums(const QUrl &dest, const QString &type, KIO::filesize_t length, bool *abortPtr) { QStringList checksums; QStringList supported = supportedVerficationTypes(); if (!supported.contains(type)) { return PartialChecksums(); } QFile file(dest.toString()); if (!file.open(QIODevice::ReadOnly)) { return PartialChecksums(); } const KIO::filesize_t fileSize = file.size(); if (!fileSize) { return PartialChecksums(); } int numPieces = 0; //the piece length has been defined if (length) { numPieces = fileSize / length; } else { length = VerifierPrivate::PARTSIZE; numPieces = fileSize / length; if (numPieces > 100) { numPieces = 100; length = fileSize / numPieces; } } //there is a rest, so increase numPieces by one if (fileSize % length) { ++numPieces; } PartialChecksums partialChecksums; //create all the checksums for the pieces for (int i = 0; i < numPieces; ++i) { QString hash = VerifierPrivate::calculatePartialChecksum(&file, type, length * i, length, fileSize, abortPtr); if (hash.isEmpty()) { file.close(); return PartialChecksums(); } checksums.append(hash); } partialChecksums.setLength(length); partialChecksums.setChecksums(checksums); file.close(); return partialChecksums; } void Verifier::addChecksum(const QString &type, const QString &checksum, int verified) { d->model->addChecksum(type, checksum, verified); } void Verifier::addChecksums(const QHash &checksums) { d->model->addChecksums(checksums); } void Verifier::addPartialChecksums(const QString &type, KIO::filesize_t length, const QStringList &checksums) { if (!d->partialSums.contains(type) && length && !checksums.isEmpty()) { d->partialSums[type] = new PartialChecksums(length, checksums); } } KIO::filesize_t Verifier::partialChunkLength() const { QStringList::const_iterator it; QStringList::const_iterator itEnd = VerifierPrivate::SUPPORTED.constEnd(); for (it = VerifierPrivate::SUPPORTED.constBegin(); it != itEnd; ++it) { if (d->partialSums.contains(*it)) { return d->partialSums[*it]->length(); } } return 0; } void Verifier::save(const QDomElement &element) { QDomElement e = element; e.setAttribute("verificationStatus", d->status); QDomElement verification = e.ownerDocument().createElement("verification"); for (int i = 0; i < d->model->rowCount(); ++i) { QDomElement hash = e.ownerDocument().createElement("hash"); hash.setAttribute("type", d->model->index(i, VerificationModel::Type).data().toString()); hash.setAttribute("verified", d->model->index(i, VerificationModel::Verified).data(Qt::EditRole).toInt()); QDomText value = e.ownerDocument().createTextNode(d->model->index(i, VerificationModel::Checksum).data().toString()); hash.appendChild(value); verification.appendChild(hash); } QHash::const_iterator it; QHash::const_iterator itEnd = d->partialSums.constEnd(); for (it = d->partialSums.constBegin(); it != itEnd; ++it) { QDomElement pieces = e.ownerDocument().createElement("pieces"); pieces.setAttribute("type", it.key()); pieces.setAttribute("length", (*it)->length()); QList checksums = (*it)->checksums(); for (int i = 0; i < checksums.size(); ++i) { QDomElement hash = e.ownerDocument().createElement("hash"); hash.setAttribute("piece", i); QDomText value = e.ownerDocument().createTextNode(checksums[i]); hash.appendChild(value); pieces.appendChild(hash); } verification.appendChild(pieces); } e.appendChild(verification); } void Verifier::load(const QDomElement &e) { if (e.hasAttribute("verificationStatus")) { const int status = e.attribute("verificationStatus").toInt(); switch (status) { case NoResult: d->status = NoResult; break; case NotVerified: d->status = NotVerified; break; case Verified: d->status = Verified; break; default: d->status = NotVerified; break; } } QDomElement verification = e.firstChildElement("verification"); QDomNodeList const hashList = verification.elementsByTagName("hash"); for (int i = 0; i < hashList.length(); ++i) { const QDomElement hash = hashList.item(i).toElement(); const QString value = hash.text(); const QString type = hash.attribute("type"); const int verificationStatus = hash.attribute("verified").toInt(); if (!type.isEmpty() && !value.isEmpty()) { d->model->addChecksum(type, value, verificationStatus); } } QDomNodeList const piecesList = verification.elementsByTagName("pieces"); for (int i = 0; i < piecesList.length(); ++i) { QDomElement pieces = piecesList.at(i).toElement(); const QString type = pieces.attribute("type"); const KIO::filesize_t length = pieces.attribute("length").toULongLong(); QStringList partialChecksums; const QDomNodeList partialHashList = pieces.elementsByTagName("hash"); for (int j = 0; j < partialHashList.size(); ++j)//TODO give this function the size of the file, to calculate how many hashs are needed as an additional check, do that check in addPartialChecksums?! { const QString hash = partialHashList.at(j).toElement().text(); if (hash.isEmpty()) { break; } partialChecksums.append(hash); } addPartialChecksums(type, length, partialChecksums); } } diff --git a/main.cpp b/main.cpp index 41727702..8bc79119 100644 --- a/main.cpp +++ b/main.cpp @@ -1,186 +1,186 @@ /* This file is part of the KDE project Copyright (C) 2002 by Patrick Charbonnier Based On Caitoo v.0.7.3 (c) 1998 - 2000, Matej Koss 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 +#include #include #include -#include -#include -#include -#include -#include +#include +#include +#include #include #include #include #include "core/kget.h" #include "dbus/dbuskgetwrapper.h" #include "mainadaptor.h" #include "settings.h" #include "mainwindow.h" #include "ui/newtransferdialog.h" #include "kget_version.h" class KGetApp : public QObject { public: KGetApp(QCommandLineParser *p) : kget( nullptr ), parser( p ) { } ~KGetApp() { delete kget; } int newInstance() { if (!kget) { #ifdef DEBUG kget = new MainWindow(!parser->isSet("showDropTarget"), parser->isSet("startWithoutAnimation"), parser->isSet("test")); #else kget = new MainWindow(!parser->isSet("showDropTarget"), parser->isSet("startWithoutAnimation"), false); #endif DBusKGetWrapper *wrapper = new DBusKGetWrapper(kget); new MainAdaptor(wrapper); QDBusConnection::sessionBus().registerObject("/KGet", wrapper); } else { // activate window if it is already open KStartupInfo::setNewStartupId(kget, KStartupInfo::startupId()); KWindowSystem::forceActiveWindow(kget->winId()); } if (parser->isSet("showDropTarget")) Settings::setShowDropTarget( true ); QList l; const QStringList args = parser->positionalArguments(); for (int i = 0; i < args.count(); i++) { QString txt(args.at(i)); if ( txt.endsWith( QLatin1String(".kgt"), Qt::CaseInsensitive ) ) KGet::load( txt ); else l.push_back(QUrl::fromUserInput(args.at(i))); } if (!l.isEmpty()) NewTransferDialogHandler::showNewTransferDialog(l); return 0; } public slots: void slotActivateRequested (QStringList args, const QString & /*workingDir*/) { parser->parse(args); newInstance(); } private: MainWindow * kget; QCommandLineParser *parser; }; int main(int argc, char *argv[]) { QApplication app(argc, argv); QApplication::setAttribute(Qt::AA_UseHighDpiPixmaps); KLocalizedString::setApplicationDomain("kget"); KAboutData aboutData(QStringLiteral("kget"), i18n("KGet"), QStringLiteral(KGET_VERSION_STRING), i18n("An advanced download manager by KDE"), KAboutLicense::GPL, i18n("(C) 2005 - 2014, The KGet developers\n" "(C) 2001 - 2002, Patrick Charbonnier\n" "(C) 2002, Carsten Pfeiffer\n" "(C) 1998 - 2000, Matej Koss"), i18n("kget@kde.org")); aboutData.addAuthor(i18n("Lukas Appelhans"), i18n("Maintainer, Core Developer, Torrent Plugin Author"), "l.appelhans@gmx.de"); aboutData.addAuthor(i18n("Dario Massarin"), i18n("Core Developer"), "nekkar@libero.it"); aboutData.addAuthor(i18n("Urs Wolfer"), i18n("Core Developer"), "uwolfer@kde.org"); aboutData.addAuthor(i18n("Manolo Valdes"), i18n("Core Developer, Multithreaded Plugin Author"), "nolis71cu@gmail.com"); aboutData.addAuthor(i18n("Matthias Fuchs"), i18n("Core Developer"), "mat69@gmx.net"); aboutData.addAuthor(i18n("Javier Goday"), i18n("Developer"), "jgoday@gmail.com"); aboutData.addAuthor(i18n("Aish Raj Dahal"), i18n("Google Summer of Code Student")); aboutData.addAuthor(i18n("Ernesto Rodriguez Ortiz"), i18n("Mms Plugin Author"), "eortiz@uci.cu"); aboutData.addAuthor(i18n("Patrick Charbonnier"), i18n("Former Developer"), "pch@freeshell.org"); aboutData.addAuthor(i18n("Carsten Pfeiffer"), i18n("Former Developer"), "pfeiffer@kde.org"); aboutData.addAuthor(i18n("Matej Koss"), i18n("Former Developer")); aboutData.addCredit(i18n("Joris Guisson"), i18n("BTCore (KTorrent) Developer"), "joris.guisson@gmail.com"); aboutData.addCredit(i18n("Mensur Zahirovic (Nookie)"), i18n("Design of Web Interface"), "linuxsajten@gmail.com"); // necessary to make the "Translators" tab appear in the About dialog aboutData.setTranslator( i18nc( "NAME OF TRANSLATORS", "Your names" ), i18nc( "EMAIL OF TRANSLATORS", "Your emails" ) ); KAboutData::setApplicationData(aboutData); QApplication::setWindowIcon(QIcon::fromTheme(QStringLiteral("kget"))); QCommandLineParser parser; aboutData.setupCommandLine(&parser); parser.addOption(QCommandLineOption(QStringList() << QLatin1String("showDropTarget"), i18n("Start KGet with drop target"))); parser.addOption(QCommandLineOption(QStringList() << QLatin1String("hideMainWindow"), i18n("Start KGet with hidden main window"))); parser.addOption(QCommandLineOption(QStringList() << QLatin1String("startWithoutAnimation"), i18n("Start KGet without drop target animation"))); #ifdef DEBUG parser.addOption(QCommandLineOption(QStringList() << QLatin1String("test"), i18n("Execute Unit Testing"))); #endif parser.addPositionalArgument(QLatin1String("[URL(s)]"), i18n("URL(s) to download")); parser.process(app); aboutData.processCommandLine(&parser); KDBusService dbusService(KDBusService::Unique); Kdelibs4ConfigMigrator migrate(QStringLiteral("kget")); migrate.setConfigFiles(QStringList() << QStringLiteral("kgetrc") << QStringLiteral("kget_bittorrentfactory.rc") << QStringLiteral("kget_checksumsearchfactory.rc") << QStringLiteral("kget_metalinkfactory.rc") << QStringLiteral("kget_mirrorsearchfactory.rc") << QStringLiteral("kget_mmsfactory.rc") << QStringLiteral("kget_multisegkiofactory.rc") << QStringLiteral("kget.notifyrc")); if (migrate.migrate()) { Kdelibs4Migration dataMigrator; const QString sourceBasePath = dataMigrator.saveLocation("data", QStringLiteral("kget")); const QString targetBasePath = QStandardPaths::writableLocation(QStandardPaths::GenericDataLocation) + QStringLiteral("/kget/"); QString targetFilePath; QDir sourceDir(sourceBasePath); QDir targetDir(targetBasePath); if (sourceDir.exists()) { if (!targetDir.exists()) { QDir().mkpath(targetBasePath); } QStringList fileNames = sourceDir.entryList( QDir::Files | QDir::NoDotAndDotDot | QDir::NoSymLinks); foreach (const QString &fileName, fileNames) { targetFilePath = targetBasePath + fileName; if (!QFile::exists(targetFilePath)) { QFile::copy(sourceBasePath + fileName, targetFilePath); } } } } KGetApp kApp(&parser); QObject::connect(&dbusService, &KDBusService::activateRequested, &kApp, &KGetApp::slotActivateRequested); kApp.newInstance(); return app.exec(); } diff --git a/mainwindow.cpp b/mainwindow.cpp index 3fd4971d..b078ebe8 100644 --- a/mainwindow.cpp +++ b/mainwindow.cpp @@ -1,1222 +1,1225 @@ /* This file is part of the KDE project Copyright (C) 2002 by Patrick Charbonnier Based On Caitoo v.0.7.3 (c) 1998 - 2000, Matej Koss Copyright (C) 2002 Carsten Pfeiffer Copyright (C) 2006 - 2008 Urs Wolfer Copyright (C) 2006 Dario Massarin Copyright (C) 2008 - 2011 Lukas Appelhans Copyright (C) 2009 - 2010 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 "mainwindow.h" #include "core/kget.h" #include "core/transferhandler.h" #include "core/transfergrouphandler.h" #include "core/transfertreemodel.h" #include "core/transfertreeselectionmodel.h" +#include "core/verifier.h" #include "settings.h" #include "conf/autopastemodel.h" #include "conf/preferencesdialog.h" #include "ui/viewscontainer.h" #include "ui/tray.h" #include "ui/droptarget.h" #include "ui/newtransferdialog.h" #include "ui/history/transferhistory.h" #include "ui/groupsettingsdialog.h" #include "ui/transfersettingsdialog.h" #include "ui/linkview/kget_linkview.h" #include "ui/metalinkcreator/metalinkcreator.h" #include "extensions/webinterface/httpserver.h" #ifdef DO_KGET_TEST #include "tests/testkget.h" #endif #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 "core/verifier.h" +#include +#include + +#include #include #include -#include +#include +#include #include +#include +#include #include +#include #ifdef DO_KGET_TEST #include #endif MainWindow::MainWindow(bool showMainwindow, bool startWithoutAnimation, bool doTesting, QWidget *parent) : KXmlGuiWindow( parent ), m_drop(nullptr), m_dock(nullptr), clipboardTimer(nullptr), m_startWithoutAnimation(startWithoutAnimation), m_doTesting(doTesting)/*, m_webinterface(nullptr)*/ { // do not quit the app when it has been minimized to system tray and a new transfer dialog // gets opened and closed again. qApp->setQuitOnLastWindowClosed(false); setAttribute(Qt::WA_DeleteOnClose, false); // create the model m_kget = KGet::self( this ); m_viewsContainer = new ViewsContainer(this); // create actions setupActions(); setupGUI(ToolBar | Keys | Save | Create); setCentralWidget(m_viewsContainer); // restore position, size and visibility move( Settings::mainPosition() ); setPlainCaption(i18n("KGet")); init(); if ( Settings::showMain() && showMainwindow) show(); else hide(); } MainWindow::~MainWindow() { //Save the user's transfers KGet::save(); slotSaveMyself(); // reset konqueror integration (necessary if user enabled / disabled temporarily integration from tray) slotKonquerorIntegration( Settings::konquerorIntegration() ); // the following call saves options set in above dtors Settings::self()->save(); delete m_drop; delete m_kget; } QSize MainWindow::sizeHint() const { return QSize(738, 380); } int MainWindow::transfersPercent() { int percent = 0; int activeTransfers = 0; foreach (const TransferHandler *handler, KGet::allTransfers()) { if (handler->status() == Job::Running) { activeTransfers ++; percent += handler->percent(); } } if (activeTransfers > 0) { return percent/activeTransfers; } else { return -1; } } void MainWindow::setupActions() { QAction *newDownloadAction = actionCollection()->addAction("new_download"); newDownloadAction->setText(i18n("&New Download...")); newDownloadAction->setIcon(QIcon::fromTheme("document-new")); //newDownloadAction->setHelpText(i18n("Opens a dialog to add a transfer to the list")); actionCollection()->setDefaultShortcut(newDownloadAction, QKeySequence(Qt::CTRL + Qt::Key_N)); connect(newDownloadAction, SIGNAL(triggered()), SLOT(slotNewTransfer())); QAction *openAction = actionCollection()->addAction("import_transfers"); openAction->setText(i18n("&Import Transfers...")); openAction->setIcon(QIcon::fromTheme("document-open")); //openAction->setHelpText(i18n("Imports a list of transfers")); actionCollection()->setDefaultShortcut(openAction, QKeySequence(Qt::CTRL + Qt::Key_I)); connect(openAction, SIGNAL(triggered()), SLOT(slotImportTransfers())); QAction *exportAction = actionCollection()->addAction("export_transfers"); exportAction->setText(i18n("&Export Transfers List...")); exportAction->setIcon(QIcon::fromTheme("document-export")); //exportAction->setHelpText(i18n("Exports the current transfers into a file")); actionCollection()->setDefaultShortcut(exportAction, QKeySequence(Qt::CTRL + Qt::Key_E)); connect(exportAction, SIGNAL(triggered()), SLOT(slotExportTransfers())); QAction *createMetalinkAction = actionCollection()->addAction("create_metalink"); createMetalinkAction->setText(i18n("&Create a Metalink...")); createMetalinkAction->setIcon(QIcon::fromTheme("journal-new")); //createMetalinkAction->setHelpText(i18n("Creates or modifies a metalink and saves it on disk")); connect(createMetalinkAction, SIGNAL(triggered()), SLOT(slotCreateMetalink())); QAction *priorityTop = actionCollection()->addAction("priority_top"); priorityTop->setText(i18n("Top Priority")); priorityTop->setIcon(QIcon::fromTheme("arrow-up-double")); //priorityTop->setHelpText(i18n("Download selected transfer first")); actionCollection()->setDefaultShortcut(priorityTop, QKeySequence(Qt::CTRL + Qt::Key_PageUp)); connect(priorityTop, SIGNAL(triggered()), this, SLOT(slotPriorityTop())); QAction *priorityBottom = actionCollection()->addAction("priority_bottom"); priorityBottom->setText(i18n("Least Priority")); priorityBottom->setIcon(QIcon::fromTheme("arrow-down-double")); //priorityBottom->setHelpText(i18n("Download selected transfer last")); actionCollection()->setDefaultShortcut(priorityBottom, QKeySequence(Qt::CTRL + Qt::Key_PageDown)); connect(priorityBottom, SIGNAL(triggered()), this, SLOT(slotPriorityBottom())); QAction *priorityUp = actionCollection()->addAction("priority_up"); priorityUp->setText(i18n("Increase Priority")); priorityUp->setIcon(QIcon::fromTheme("arrow-up")); //priorityUp->setHelpText(i18n("Increase priority for selected transfer")); actionCollection()->setDefaultShortcut(priorityUp, QKeySequence(Qt::CTRL + Qt::Key_Up)); connect(priorityUp, SIGNAL(triggered()), this, SLOT(slotPriorityUp())); QAction *priorityDown = actionCollection()->addAction("priority_down"); priorityDown->setText(i18n("Decrease Priority")); priorityDown->setIcon(QIcon::fromTheme("arrow-down")); //priorityDown->setHelpText(i18n("Decrease priority for selected transfer")); actionCollection()->setDefaultShortcut(priorityDown, QKeySequence(Qt::CTRL + Qt::Key_Down)); connect(priorityDown, SIGNAL(triggered()), this, SLOT(slotPriorityDown())); //FIXME: Not needed maybe because the normal delete already deletes groups? QAction *deleteGroupAction = actionCollection()->addAction("delete_groups"); deleteGroupAction->setText(i18n("Delete Group")); deleteGroupAction->setIcon(QIcon::fromTheme("edit-delete")); //deleteGroupAction->setHelpText(i18n("Delete selected group")); connect(deleteGroupAction, SIGNAL(triggered()), SLOT(slotDeleteGroup())); QAction *renameGroupAction = actionCollection()->addAction("rename_groups"); renameGroupAction->setText(i18n("Rename Group...")); renameGroupAction->setIcon(QIcon::fromTheme("edit-rename")); connect(renameGroupAction, SIGNAL(triggered()), SLOT(slotRenameGroup())); QAction *setIconGroupAction = actionCollection()->addAction("seticon_groups"); setIconGroupAction->setText(i18n("Set Icon...")); setIconGroupAction->setIcon(QIcon::fromTheme("preferences-desktop-icons")); //setIconGroupAction->setHelpText(i18n("Select a custom icon for the selected group")); connect(setIconGroupAction, SIGNAL(triggered()), SLOT(slotSetIconGroup())); m_autoPasteAction = new KToggleAction(QIcon::fromTheme("edit-paste"), i18n("Auto-Paste Mode"), actionCollection()); actionCollection()->addAction("auto_paste", m_autoPasteAction); m_autoPasteAction->setChecked(Settings::autoPaste()); m_autoPasteAction->setWhatsThis(i18n("Auto paste button toggles the auto-paste mode " "on and off.\nWhen set, KGet will periodically scan " "the clipboard for URLs and paste them automatically.")); connect(m_autoPasteAction, SIGNAL(triggered()), SLOT(slotToggleAutoPaste())); m_konquerorIntegration = new KToggleAction(QIcon::fromTheme("konqueror"), i18n("Use KGet as Konqueror Download Manager"), actionCollection()); actionCollection()->addAction("konqueror_integration", m_konquerorIntegration); connect(m_konquerorIntegration, SIGNAL(triggered(bool)), SLOT(slotTrayKonquerorIntegration(bool))); m_konquerorIntegration->setChecked(Settings::konquerorIntegration()); // local - Destroys all sub-windows and exits KStandardAction::quit(this, SLOT(slotQuit()), actionCollection()); // local - Standard configure actions KStandardAction::preferences(this, SLOT(slotPreferences()), actionCollection()); KStandardAction::configureNotifications(this, SLOT(slotConfigureNotifications()), actionCollection()); m_menubarAction = KStandardAction::showMenubar(this, SLOT(slotShowMenubar()), actionCollection()); m_menubarAction->setChecked(!menuBar()->isHidden()); // Transfer related actions actionCollection()->addAction(KStandardAction::SelectAll, "select_all", m_viewsContainer, SLOT(selectAll())); QAction *deleteSelectedAction = actionCollection()->addAction("delete_selected_download"); deleteSelectedAction->setText(i18nc("delete selected transfer item", "Remove Selected")); deleteSelectedAction->setIcon(QIcon::fromTheme("edit-delete")); // deleteSelectedAction->setHelpText(i18n("Removes selected transfer and deletes files from disk if it's not finished")); actionCollection()->setDefaultShortcut(deleteSelectedAction, QKeySequence(Qt::Key_Delete)); connect(deleteSelectedAction, SIGNAL(triggered()), SLOT(slotDeleteSelected())); QAction *deleteAllFinishedAction = actionCollection()->addAction("delete_all_finished"); deleteAllFinishedAction->setText(i18nc("delete all finished transfers", "Remove All Finished")); deleteAllFinishedAction->setIcon(QIcon::fromTheme("edit-clear-list")); // deleteAllFinishedAction->setHelpText(i18n("Removes all finished transfers and leaves all files on disk")); connect(deleteAllFinishedAction, SIGNAL(triggered()), SLOT(slotDeleteFinished())); QAction *deleteSelectedIncludingFilesAction = actionCollection()->addAction("delete_selected_download_including_files"); deleteSelectedIncludingFilesAction->setText(i18nc("delete selected transfer item and files", "Remove Selected and Delete Files")); deleteSelectedIncludingFilesAction->setIcon(QIcon::fromTheme("edit-delete")); // deleteSelectedIncludingFilesAction->setHelpText(i18n("Removes selected transfer and deletes files from disk in any case")); connect(deleteSelectedIncludingFilesAction, SIGNAL(triggered()), SLOT(slotDeleteSelectedIncludingFiles())); QAction *redownloadSelectedAction = actionCollection()->addAction("redownload_selected_download"); redownloadSelectedAction->setText(i18nc("redownload selected transfer item", "Redownload Selected")); redownloadSelectedAction->setIcon(QIcon::fromTheme("view-refresh")); connect(redownloadSelectedAction, SIGNAL(triggered()), SLOT(slotRedownloadSelected())); QAction *startAllAction = actionCollection()->addAction("start_all_download"); startAllAction->setText(i18n("Start All")); startAllAction->setIcon(QIcon::fromTheme("media-seek-forward")); // startAllAction->setHelpText(i18n("Starts / resumes all transfers")); actionCollection()->setDefaultShortcut(startAllAction, QKeySequence(Qt::CTRL + Qt::Key_R)); connect(startAllAction, SIGNAL(triggered()), SLOT(slotStartAllDownload())); QAction *startSelectedAction = actionCollection()->addAction("start_selected_download"); startSelectedAction->setText(i18n("Start Selected")); startSelectedAction->setIcon(QIcon::fromTheme("media-playback-start")); // startSelectedAction->setHelpText(i18n("Starts / resumes selected transfer")); connect(startSelectedAction, SIGNAL(triggered()), SLOT(slotStartSelectedDownload())); QAction *stopAllAction = actionCollection()->addAction("stop_all_download"); stopAllAction->setText(i18n("Pause All")); stopAllAction->setIcon(QIcon::fromTheme("media-playback-pause")); // stopAllAction->setHelpText(i18n("Pauses all transfers")); actionCollection()->setDefaultShortcut(stopAllAction, QKeySequence(Qt::CTRL + Qt::Key_P)); connect(stopAllAction, SIGNAL(triggered()), SLOT(slotStopAllDownload())); QAction *stopSelectedAction = actionCollection()->addAction("stop_selected_download"); stopSelectedAction->setText(i18n("Stop Selected")); stopSelectedAction->setIcon(QIcon::fromTheme("media-playback-pause")); //stopSelectedAction->setHelpText(i18n("Pauses selected transfer")); connect(stopSelectedAction, SIGNAL(triggered()), SLOT(slotStopSelectedDownload())); KActionMenu *startActionMenu = new KActionMenu(QIcon::fromTheme("media-playback-start"), i18n("Start"), actionCollection()); actionCollection()->addAction("start_menu", startActionMenu); startActionMenu->setDelayed(true); startActionMenu->addAction(startSelectedAction); startActionMenu->addAction(startAllAction); connect(startActionMenu, SIGNAL(triggered()), SLOT(slotStartDownload())); KActionMenu *stopActionMenu = new KActionMenu(QIcon::fromTheme("media-playback-pause"), i18n("Pause"), actionCollection()); actionCollection()->addAction("stop_menu", stopActionMenu); stopActionMenu->setDelayed(true); stopActionMenu->addAction(stopSelectedAction); stopActionMenu->addAction(stopAllAction); connect(stopActionMenu, SIGNAL(triggered()), SLOT(slotStopDownload())); QAction *openDestAction = actionCollection()->addAction("transfer_open_dest"); openDestAction->setText(i18n("Open Destination")); openDestAction->setIcon(QIcon::fromTheme("document-open")); connect(openDestAction, SIGNAL(triggered()), SLOT(slotTransfersOpenDest())); QAction *openFileAction = actionCollection()->addAction("transfer_open_file"); openFileAction->setText(i18n("Open File")); openFileAction->setIcon(QIcon::fromTheme("document-open")); connect(openFileAction, SIGNAL(triggered()), SLOT(slotTransfersOpenFile())); QAction *showDetailsAction = new KToggleAction(QIcon::fromTheme("document-properties"), i18n("Show Details"), actionCollection()); actionCollection()->addAction("transfer_show_details", showDetailsAction); connect(showDetailsAction, SIGNAL(triggered()), SLOT(slotTransfersShowDetails())); QAction *copyUrlAction = actionCollection()->addAction("transfer_copy_source_url"); copyUrlAction->setText(i18n("Copy URL to Clipboard")); copyUrlAction->setIcon(QIcon::fromTheme("edit-copy")); connect(copyUrlAction, SIGNAL(triggered()), SLOT(slotTransfersCopySourceUrl())); QAction *transferHistoryAction = actionCollection()->addAction("transfer_history"); transferHistoryAction->setText(i18n("&Transfer History")); transferHistoryAction->setIcon(QIcon::fromTheme("view-history")); actionCollection()->setDefaultShortcut(transferHistoryAction, QKeySequence(Qt::CTRL + Qt::Key_H)); connect(transferHistoryAction, SIGNAL(triggered()), SLOT(slotTransferHistory())); QAction *transferGroupSettingsAction = actionCollection()->addAction("transfer_group_settings"); transferGroupSettingsAction->setText(i18n("&Group Settings")); transferGroupSettingsAction->setIcon(QIcon::fromTheme("preferences-system")); actionCollection()->setDefaultShortcut(transferGroupSettingsAction, QKeySequence(Qt::CTRL + Qt::Key_G)); connect(transferGroupSettingsAction, SIGNAL(triggered()), SLOT(slotTransferGroupSettings())); QAction *transferSettingsAction = actionCollection()->addAction("transfer_settings"); transferSettingsAction->setText(i18n("&Transfer Settings")); transferSettingsAction->setIcon(QIcon::fromTheme("preferences-system")); actionCollection()->setDefaultShortcut(transferSettingsAction, QKeySequence(Qt::CTRL + Qt::Key_T)); connect(transferSettingsAction, SIGNAL(triggered()), SLOT(slotTransferSettings())); QAction *listLinksAction = actionCollection()->addAction("import_links"); listLinksAction->setText(i18n("Import &Links...")); listLinksAction->setIcon(QIcon::fromTheme("view-list-text")); actionCollection()->setDefaultShortcut(listLinksAction, QKeySequence(Qt::CTRL + Qt::Key_L)); connect(listLinksAction, SIGNAL(triggered()), SLOT(slotShowListLinks())); //create the download finished actions which can be displayed in the toolbar KSelectAction *downloadFinishedActions = new KSelectAction(i18n("After downloads finished action"), this);//TODO maybe with name?? actionCollection()->addAction("download_finished_actions", downloadFinishedActions); //downloadFinishedActions->setHelpText(i18n("Choose an action that is executed after all downloads have been finished.")); QAction *noAction = downloadFinishedActions->addAction(i18n("No Action")); connect(noAction, SIGNAL(triggered()), SLOT(slotDownloadFinishedActions())); downloadFinishedActions->addAction(noAction); QAction *quitAction = downloadFinishedActions->addAction(i18n("Quit KGet")); quitAction->setData(KGet::Quit); connect(quitAction, SIGNAL(triggered()), SLOT(slotDownloadFinishedActions())); downloadFinishedActions->addAction(quitAction); #ifdef HAVE_KWORKSPACE QAction *shutdownAction = downloadFinishedActions->addAction(i18n("Turn Off Computer")); shutdownAction->setData(KGet::Shutdown); connect(shutdownAction, SIGNAL(triggered()), SLOT(slotDownloadFinishedActions())); downloadFinishedActions->addAction(shutdownAction); QAction *hibernateAction = downloadFinishedActions->addAction(i18n("Hibernate Computer")); hibernateAction->setData(KGet::Hibernate); connect(hibernateAction, SIGNAL(triggered()), SLOT(slotDownloadFinishedActions())); downloadFinishedActions->addAction(hibernateAction); QAction *suspendAction = downloadFinishedActions->addAction(i18n("Suspend Computer")); suspendAction->setData(KGet::Suspend); connect(suspendAction, SIGNAL(triggered()), SLOT(slotDownloadFinishedActions())); downloadFinishedActions->addAction(suspendAction); #endif if (Settings::afterFinishActionEnabled()) { downloadFinishedActions->setCurrentItem(Settings::afterFinishAction() + 1); } else { downloadFinishedActions->setCurrentItem(0); } } void MainWindow::slotDownloadFinishedActions() { QAction *action = static_cast(QObject::sender()); bool ok; const int type = action->data().toInt(&ok); if (ok) { Settings::self()->setAfterFinishAction(type); } //only after finish actions have a number assigned Settings::self()->setAfterFinishActionEnabled(ok); Settings::self()->save(); slotNewConfig(); } void MainWindow::init() { //Here we import the user's transfers. KGet::load( QStandardPaths::writableLocation(QStandardPaths::DataLocation) + QStringLiteral("/transfers.kgt")) ; if(Settings::enableSystemTray()) { m_dock = new Tray(this); } // enable dropping setAcceptDrops(true); // enable hide toolbar setStandardToolBarMenuEnabled(true); // session management stuff - connect(kapp, SIGNAL(saveYourself()), SLOT(slotSaveMyself())); + auto disableSessionManagement = [](QSessionManager &sm) { + sm.setRestartHint(QSessionManager::RestartNever); + }; + QObject::connect(qApp, &QGuiApplication::saveStateRequest, disableSessionManagement); // set auto-resume in kioslaverc (is there a cleaner way?) KConfig cfg("kioslaverc", KConfig::NoGlobals); cfg.group(QString()).writeEntry("AutoResume", true); cfg.sync(); // DropTarget m_drop = new DropTarget(this); if (Settings::firstRun()) { if (KMessageBox::questionYesNoCancel(this ,i18n("This is the first time you have run KGet.\n" "Would you like to enable KGet as the download manager for Konqueror?"), i18n("Konqueror Integration"), KGuiItem(i18n("Enable")), KGuiItem(i18n("Do Not Enable"))) == KMessageBox::Yes) { Settings::setKonquerorIntegration(true); m_konquerorIntegration->setChecked(Settings::konquerorIntegration()); slotKonquerorIntegration(true); } m_drop->setDropTargetVisible(false); // reset the FirstRun config option Settings::setFirstRun(false); } if (Settings::showDropTarget() && !m_startWithoutAnimation) m_drop->setDropTargetVisible(true); //auto paste stuff lastClipboard = QApplication::clipboard()->text( QClipboard::Clipboard ).trimmed(); clipboardTimer = new QTimer(this); connect(clipboardTimer, SIGNAL(timeout()), SLOT(slotCheckClipboard())); if ( Settings::autoPaste() ) clipboardTimer->start(1000); /*if (Settings::webinterfaceEnabled()) m_webinterface = new HttpServer(this);*///TODO: Port to KF5 if (Settings::speedLimit()) { KGet::setGlobalDownloadLimit(Settings::globalDownloadLimit()); KGet::setGlobalUploadLimit(Settings::globalUploadLimit()); } else { KGet::setGlobalDownloadLimit(0); KGet::setGlobalUploadLimit(0); } connect(KGet::model(), SIGNAL(transfersAddedEvent(QList)), this, SLOT(slotUpdateTitlePercent())); connect(KGet::model(), SIGNAL(transfersRemovedEvent(QList)), this, SLOT(slotUpdateTitlePercent())); connect(KGet::model(), SIGNAL(transfersChangedEvent(QMap)), SLOT(slotTransfersChanged(QMap))); connect(KGet::model(), SIGNAL(groupsChangedEvent(QMap)), SLOT(slotGroupsChanged(QMap))); #ifdef DO_KGET_TEST if (m_doTesting) { // Unit testing TestKGet unitTest; QTest::qExec(&unitTest); } #endif } void MainWindow::slotToggleDropTarget() { m_drop->setDropTargetVisible(!m_drop->isVisible()); } void MainWindow::slotNewTransfer() { NewTransferDialogHandler::showNewTransferDialog(QUrl()); } void MainWindow::slotImportTransfers() { QString filename = QFileDialog::getOpenFileName(nullptr, i18nc("@title:window", "Open File"), QString(), i18n("All Openable Files") + " (*.kgt *.metalink *.meta4 *.torrent)"); if(filename.endsWith(QLatin1String(".kgt"))) { KGet::load(filename); return; } if(!filename.isEmpty()) KGet::addTransfer( QUrl( filename ) ); } void MainWindow::slotUpdateTitlePercent() { int percent = transfersPercent(); if (percent != -1) { setPlainCaption(i18nc("window title including overall download progress in percent", "KGet - %1%", percent)); } else { setPlainCaption(i18n("KGet")); } } void MainWindow::slotTransfersChanged(QMap transfers) { QMapIterator it(transfers); //QList finishedTransfers; bool update = false; while (it.hasNext()) { it.next(); //TransferHandler * transfer = it.key(); Transfer::ChangesFlags transferFlags = it.value(); if (transferFlags & Transfer::Tc_Percent || transferFlags & Transfer::Tc_Status) { update = true; break; } // qCDebug(KGET_DEBUG) << it.key() << ": " << it.value() << endl; } if (update) slotUpdateTitlePercent(); } void MainWindow::slotGroupsChanged(QMap groups) { bool update = false; foreach (const TransferGroup::ChangesFlags &groupFlags, groups) { if (groupFlags & TransferGroup::Gc_Percent) { update = true; break; } } if (update) slotUpdateTitlePercent(); } void MainWindow::slotQuit() { if (KGet::schedulerRunning()) { if (KMessageBox::warningYesNo(this, i18n("Some transfers are still running.\n" "Are you sure you want to close KGet?"), i18n("Confirm Quit"), KStandardGuiItem::quit(), KStandardGuiItem::cancel(), "ExitWithActiveTransfers") != KMessageBox::Yes) return; } Settings::self()->save(); qApp->quit(); } void MainWindow::slotPreferences() { //never reuse the preference dialog, to make sure its settings are always reloaded PreferencesDialog * dialog = new PreferencesDialog( this, Settings::self() ); dialog->setAttribute(Qt::WA_DeleteOnClose); // keep us informed when the user changes settings connect( dialog, SIGNAL(settingsChanged(QString)), this, SLOT(slotNewConfig()) ); dialog->show(); } void MainWindow::slotExportTransfers() { const QString filename = QFileDialog::getSaveFileName (this, i18nc("@title:window", "Export Transfers"), QString(), i18n("KGet Transfer List") + " (*.kgt);;" + i18n("Text File") + " (*.txt)" ); if (!filename.isEmpty()) { const bool plain = !filename.endsWith(QLatin1String(".kgt")); KGet::save(filename, plain); } } void MainWindow::slotCreateMetalink() { MetalinkCreator *dialog = new MetalinkCreator(this); dialog->setAttribute(Qt::WA_DeleteOnClose); dialog->show(); } void MainWindow::slotDeleteGroup() { QList groups = KGet::selectedTransferGroups(); if (groups.count() != KGet::allTransferGroups().count()) { KGet::delGroups(groups); } } void MainWindow::slotRenameGroup() { bool ok = true; QString groupName; foreach(TransferGroupHandler * it, KGet::selectedTransferGroups()) { groupName = QInputDialog::getText(this, i18n("Enter Group Name"), i18n("Group name:"), QLineEdit::Normal, it->name(), &ok); if(ok) it->setName(groupName); } } void MainWindow::slotSetIconGroup() { KIconDialog dialog(this); QString iconName = dialog.getIcon(); TransferTreeSelectionModel *selModel = KGet::selectionModel(); QModelIndexList indexList = selModel->selectedRows(); if (!iconName.isEmpty()) { foreach (TransferGroupHandler *group, KGet::selectedTransferGroups()) { group->setIconName(iconName); } } //emit dataChanged(indexList.first(),indexList.last()); } void MainWindow::slotStartDownload() { if (KGet::selectedTransfers().size() == 0 && KGet::selectedTransferGroups().size() == 0) { slotStartAllDownload(); } else { slotStartSelectedDownload(); } } void MainWindow::slotStartAllDownload() { KGet::setSchedulerRunning(true); } void MainWindow::slotStartSelectedDownload() { KGet::setSuspendScheduler(true); foreach (TransferHandler *transfer, KGet::selectedTransfers()) { transfer->start(); } foreach (TransferGroupHandler *group, KGet::selectedTransferGroups()) { group->start(); } KGet::setSuspendScheduler(false); } void MainWindow::slotStopDownload() { if (KGet::selectedTransfers().size() == 0 && KGet::selectedTransferGroups().size() == 0) { slotStopAllDownload(); } else { slotStopSelectedDownload(); } } void MainWindow::slotStopAllDownload() { KGet::setSchedulerRunning(false); // This line ensures that each transfer is stopped. In the handler class // the policy of the transfer will be correctly set to None foreach (TransferHandler * it, KGet::allTransfers()) it->stop(); } void MainWindow::slotStopSelectedDownload() { KGet::setSuspendScheduler(true); foreach (TransferHandler *transfer, KGet::selectedTransfers()) { transfer->stop(); } foreach (TransferGroupHandler *group, KGet::selectedTransferGroups()) { group->stop(); } KGet::setSuspendScheduler(false); } void MainWindow::slotDeleteSelected() { foreach (TransferHandler * it, KGet::selectedTransfers()) { if (it->status() != Job::Finished && it->status() != Job::FinishedKeepAlive) { if (KMessageBox::warningYesNo(this, i18np("Are you sure you want to delete the selected transfer?", "Are you sure you want to delete the selected transfers?", KGet::selectedTransfers().count()), i18n("Confirm transfer delete"), KStandardGuiItem::remove(), KStandardGuiItem::cancel()) == KMessageBox::No) { return; } break; } } const QList selectedTransfers = KGet::selectedTransfers(); if (!selectedTransfers.isEmpty()) { foreach (TransferHandler *it, selectedTransfers) { m_viewsContainer->closeTransferDetails(it);//TODO make it take QList? } KGet::delTransfers(KGet::selectedTransfers()); } else { //no transfers selected, delete groups if any are selected slotDeleteGroup(); } } void MainWindow::slotDeleteSelectedIncludingFiles() { const QList selectedTransfers = KGet::selectedTransfers(); if (!selectedTransfers.isEmpty()) { if (KMessageBox::warningYesNo(this, i18np("Are you sure you want to delete the selected transfer including files?", "Are you sure you want to delete the selected transfers including files?", selectedTransfers.count()), i18n("Confirm transfer delete"), KStandardGuiItem::remove(), KStandardGuiItem::cancel()) == KMessageBox::No) { return; } foreach (TransferHandler *it, selectedTransfers) { m_viewsContainer->closeTransferDetails(it);//TODO make it take QList? } KGet::delTransfers(KGet::selectedTransfers(), KGet::DeleteFiles); } else { //no transfers selected, delete groups if any are selected slotDeleteGroup(); } } void MainWindow::slotRedownloadSelected() { foreach(TransferHandler * it, KGet::selectedTransfers()) { KGet::redownloadTransfer(it); } } void MainWindow::slotPriorityTop() { QList selected = KGet::selectedTransfers(); TransferHandler *after = nullptr; TransferGroupHandler *group = nullptr; foreach (TransferHandler *transfer, selected) { if (!transfer) { continue; } //previous transfer was not of the same group, so after has to be reset as the group if (!group || (group != transfer->group())) { after = nullptr; group = transfer->group(); } KGet::model()->moveTransfer(transfer, group, after); after = transfer; } } void MainWindow::slotPriorityBottom() { QList selected = KGet::selectedTransfers(); QList groupTransfers; TransferHandler *after = nullptr; TransferGroupHandler *group = nullptr; foreach (TransferHandler *transfer, selected) { if (!transfer) { continue; } //previous transfer was not of the same group, so after has to be reset as the group if (!group || (group != transfer->group())) { group = transfer->group(); groupTransfers = group->transfers(); if (groupTransfers.isEmpty()) { after = nullptr; } else { after = groupTransfers.last(); } } KGet::model()->moveTransfer(transfer, group, after); after = transfer; } } void MainWindow::slotPriorityUp() { QList selected = KGet::selectedTransfers(); QList groupTransfers; TransferHandler *after = nullptr; int newIndex = -1; TransferGroupHandler *group = nullptr; foreach (TransferHandler *transfer, selected) { if (!transfer) { continue; } //previous transfer was not of the same group, so group has to be reset if (!group || (group != transfer->group())) { group = transfer->group(); groupTransfers = group->transfers(); } after = nullptr; if (!groupTransfers.isEmpty()) { int index = groupTransfers.indexOf(transfer); //do not move higher than the first place if (index > 0) { //if only Transfers at the top are select do not change their order if ((index - 1) != newIndex) { newIndex = index - 1; if (newIndex - 1 >= 0) { after = groupTransfers[newIndex - 1]; } //keep the list with the actual movements synchronized groupTransfers.move(index, newIndex); KGet::model()->moveTransfer(transfer, group, after); } else { newIndex = index; } } else { newIndex = 0; } } } } void MainWindow::slotPriorityDown() { QList selected = KGet::selectedTransfers(); QList groupTransfers; int newIndex = -1; TransferGroupHandler *group = nullptr; for (int i = selected.count() - 1; i >= 0; --i) { TransferHandler *transfer = selected[i]; if (!transfer) { continue; } //previous transfer was not of the same group, so group has to be reset if (!group || (group != transfer->group())) { group = transfer->group(); groupTransfers = group->transfers(); } if (!groupTransfers.isEmpty()) { int index = groupTransfers.indexOf(transfer); //do not move lower than the last place if ((index != -1) && (index + 1 < groupTransfers.count())) { //if only Transfers at the top are select do not change their order if ((index + 1) != newIndex) { newIndex = index + 1; TransferHandler *after = groupTransfers[newIndex]; //keep the list with the actual movements synchronized groupTransfers.move(index, newIndex); KGet::model()->moveTransfer(transfer, group, after); } else { newIndex = index; } } else { newIndex = index; } } } } void MainWindow::slotTransfersOpenDest() { QStringList openedDirs; foreach(TransferHandler * it, KGet::selectedTransfers()) { QString directory = it->dest().adjusted(QUrl::RemoveFilename).toString(); if( !openedDirs.contains( directory ) ) { new KRun(QUrl(directory), this); openedDirs.append( directory ); } } } void MainWindow::slotTransfersOpenFile() { foreach(TransferHandler * it, KGet::selectedTransfers()) { new KRun(it->dest(), this); } } void MainWindow::slotTransfersShowDetails() { foreach(TransferHandler * it, KGet::selectedTransfers()) { m_viewsContainer->showTransferDetails(it); } } void MainWindow::slotTransfersCopySourceUrl() { foreach(TransferHandler * it, KGet::selectedTransfers()) { QString sourceurl = it->source().url(); QClipboard *cb = QApplication::clipboard(); cb->setText(sourceurl, QClipboard::Selection); cb->setText(sourceurl, QClipboard::Clipboard); } } void MainWindow::slotDeleteFinished() { foreach(TransferHandler * it, KGet::finishedTransfers()) { m_viewsContainer->closeTransferDetails(it); } KGet::delTransfers(KGet::finishedTransfers()); } void MainWindow::slotConfigureNotifications() { KNotifyConfigWidget::configure(this); } void MainWindow::slotSaveMyself() { // save last parameters .. Settings::setMainPosition( pos() ); // .. and write config to disk Settings::self()->save(); } void MainWindow::slotNewToolbarConfig() { createGUI(); } void MainWindow::slotNewConfig() { // Update here properties modified in the config dialog and not // parsed often by the code. When clicking Ok or Apply of // PreferencesDialog, this function is called. if (m_drop) m_drop->setDropTargetVisible(Settings::showDropTarget(), false); if (Settings::enableSystemTray() && !m_dock) { m_dock = new Tray(this); } else if (!Settings::enableSystemTray() && m_dock) { setVisible(true); delete m_dock; m_dock = nullptr; } slotKonquerorIntegration(Settings::konquerorIntegration()); m_konquerorIntegration->setChecked(Settings::konquerorIntegration()); if (clipboardTimer) { if (Settings::autoPaste()) clipboardTimer->start(1000); else clipboardTimer->stop(); } m_autoPasteAction->setChecked(Settings::autoPaste()); /*if (Settings::webinterfaceEnabled() && !m_webinterface) { m_webinterface = new HttpServer(this); } else if (m_webinterface && !Settings::webinterfaceEnabled()) { delete m_webinterface; m_webinterface = nullptr; } else if (m_webinterface) { m_webinterface->settingsChanged(); }*///TODO: Port to KF5 if (Settings::speedLimit()) { KGet::setGlobalDownloadLimit(Settings::globalDownloadLimit()); KGet::setGlobalUploadLimit(Settings::globalUploadLimit()); } else { KGet::setGlobalDownloadLimit(0); KGet::setGlobalUploadLimit(0); } KGet::settingsChanged(); } void MainWindow::slotToggleAutoPaste() { bool autoPaste = !Settings::autoPaste(); Settings::setAutoPaste( autoPaste ); if (autoPaste) clipboardTimer->start(1000); else clipboardTimer->stop(); m_autoPasteAction->setChecked(autoPaste); } void MainWindow::slotCheckClipboard() { const QString clipData = QApplication::clipboard()->text( QClipboard::Clipboard ).trimmed(); if (clipData != lastClipboard) { lastClipboard = clipData; if (lastClipboard.isEmpty()) return; const QUrl url = QUrl(lastClipboard); if (url.isValid() && !url.scheme().isEmpty() && !url.path().isEmpty() && !url.host().isEmpty() && !url.isLocalFile()) { bool add = false; const QString urlString = url.url(); //check the combined whitelist and blacklist const QList types = Settings::autoPasteTypes(); const QList syntaxes = Settings::autoPastePatternSyntaxes(); const QStringList patterns = Settings::autoPastePatterns(); const Qt::CaseSensitivity cs = (Settings::autoPasteCaseSensitive() ? Qt::CaseSensitive : Qt::CaseInsensitive); for (int i = 0; i < types.count(); ++i) { const QRegExp::PatternSyntax syntax = (syntaxes[i] == AutoPasteModel::Wildcard ? QRegExp::Wildcard : QRegExp::RegExp2); QRegExp rx(patterns[i], cs, syntax); if (rx.exactMatch(urlString)) { add = (types[i] == AutoPasteModel::Include); break; } } if (add) { KGet::addTransfer(url); } } } } void MainWindow::slotTrayKonquerorIntegration(bool enable) { slotKonquerorIntegration(enable); if (!enable && Settings::konquerorIntegration()) { KGet::showNotification(this, "notification", i18n("KGet has been temporarily disabled as download manager for Konqueror. " "If you want to disable it forever, go to Settings->Advanced and disable \"Use " "as download manager for Konqueror\"."), "dialog-info"); /*KMessageBox::information(this, i18n("KGet has been temporarily disabled as download manager for Konqueror. " "If you want to disable it forever, go to Settings->Advanced and disable \"Use " "as download manager for Konqueror\"."), i18n("Konqueror Integration disabled"), "KonquerorIntegrationDisabled");*/ } } void MainWindow::slotKonquerorIntegration(bool konquerorIntegration) { KConfig cfgKonqueror("konquerorrc", KConfig::NoGlobals); cfgKonqueror.group("HTML Settings").writeEntry("DownloadManager", QString(konquerorIntegration ? "kget" : QString())); cfgKonqueror.sync(); } void MainWindow::slotShowMenubar() { if (m_menubarAction->isChecked()) menuBar()->show(); else menuBar()->hide(); } void MainWindow::setSystemTrayDownloading(bool running) { qCDebug(KGET_DEBUG); if (m_dock) m_dock->setDownloading(running); } void MainWindow::slotTransferHistory() { TransferHistory *history = new TransferHistory(); history->exec(); } void MainWindow::slotTransferGroupSettings() { qCDebug(KGET_DEBUG); QList list = KGet::selectedTransferGroups(); foreach(TransferGroupHandler* group, list) { QPointer settings = new GroupSettingsDialog(this, group); settings->exec(); delete settings; } } void MainWindow::slotTransferSettings() { qCDebug(KGET_DEBUG); QList list = KGet::selectedTransfers(); foreach(TransferHandler* transfer, list) { QPointer settings = new TransferSettingsDialog(this, transfer); settings->exec(); delete settings; } } /** slots for link list **/ void MainWindow::slotShowListLinks() { KGetLinkView *link_view = new KGetLinkView(this); link_view->importUrl(); link_view->show(); } void MainWindow::slotImportUrl(const QString &url) { KGetLinkView *link_view = new KGetLinkView(this); link_view->importUrl(url); link_view->show(); } /** widget events **/ void MainWindow::closeEvent( QCloseEvent * e ) { // if the event comes from out the application (close event) we decide between close or hide // if the event comes from the application (system shutdown) we say goodbye if(e->spontaneous()) { e->ignore(); if(!Settings::enableSystemTray()) slotQuit(); else hide(); } } void MainWindow::hideEvent(QHideEvent *) { Settings::setShowMain(false); } void MainWindow::showEvent(QShowEvent *) { Settings::setShowMain(true); } void MainWindow::dragEnterEvent(QDragEnterEvent * event) { event->setAccepted(event->mimeData()->hasUrls()); } void MainWindow::dropEvent(QDropEvent * event) { QList list = event->mimeData()->urls(); if (!list.isEmpty()) { if (list.count() == 1 && list.first().url().endsWith(QLatin1String(".kgt"))) { int msgBoxResult = KMessageBox::questionYesNoCancel(this, i18n("The dropped file is a KGet Transfer List"), "KGet", KGuiItem(i18n("&Download"), QIcon::fromTheme("document-save")), KGuiItem(i18n("&Load transfer list"), QIcon::fromTheme("list-add")), KStandardGuiItem::cancel()); if (msgBoxResult == 3) //Download NewTransferDialogHandler::showNewTransferDialog(list.first().url()); if (msgBoxResult == 4) //Load KGet::load(list.first().url()); } else { if (list.count() == 1) NewTransferDialogHandler::showNewTransferDialog(list.first().url()); else NewTransferDialogHandler::showNewTransferDialog(list); } } else { NewTransferDialogHandler::showNewTransferDialog(QUrl()); } } diff --git a/mainwindow.h b/mainwindow.h index a9c0d416..81107f64 100644 --- a/mainwindow.h +++ b/mainwindow.h @@ -1,165 +1,164 @@ /* This file is part of the KDE project Copyright (C) 2002 by Patrick Charbonnier Based On Caitoo v.0.7.3 (c) 1998 - 2000, Matej Koss Copyright (C) 2006 Dario Massarin Copyright (C) 2009 - 2010 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. */ #ifndef MAINWINDOW_H #define MAINWINDOW_H -#include -#include -#include +#include +#include +#include -#include #include "ui/tray.h" #include "core/transfer.h" #include "core/transfergroup.h" #ifdef HAVE_QCA2 #include #endif class ViewsContainer; class DropTarget; class DBusKgetWrapper; class HttpServer; class KGet; /** * The main window of KGet. * * Can be collapsed or expanded. */ class MainWindow : public KXmlGuiWindow { friend class DBusKGetWrapper; Q_OBJECT public: explicit MainWindow(bool showMainwindow = true, bool startWithoutAnimation = false, bool doTesting = false, QWidget *parent = nullptr); ~MainWindow(); virtual void setSystemTrayDownloading(bool running); //no slot, to make sure that MainWindow is correctly initialized before any transfers get added void init(); public slots: void slotQuit(); void slotImportUrl(const QString &url); void slotUpdateTitlePercent(); protected: // ignore/accept quit events virtual void closeEvent(QCloseEvent *) override; virtual void hideEvent(QHideEvent *) override; virtual void showEvent(QShowEvent *) override; // drag and drop virtual void dragEnterEvent(QDragEnterEvent *) override; virtual void dropEvent(QDropEvent *) override; // set sensitive initial size virtual QSize sizeHint() const override; private slots: // slots connected to actions void slotToggleDropTarget(); void slotNewTransfer(); void slotImportTransfers(); void slotExportTransfers(); void slotPreferences(); void slotDeleteGroup(); void slotRenameGroup(); void slotSetIconGroup(); void slotStartDownload(); void slotStopDownload(); void slotConfigureNotifications(); void slotToggleAutoPaste(); void slotTrayKonquerorIntegration(bool); void slotKonquerorIntegration( bool ); void slotShowMenubar(); void slotTransferGroupSettings(); void slotTransferSettings(); void slotCreateMetalink(); void slotPriorityTop(); void slotPriorityBottom(); void slotPriorityUp(); void slotPriorityDown(); void slotDownloadFinishedActions(); // transfers slots void slotStopAllDownload(); void slotStopSelectedDownload(); void slotStartAllDownload(); void slotStartSelectedDownload(); void slotDeleteSelected(); void slotDeleteSelectedIncludingFiles(); void slotRedownloadSelected(); void slotTransfersOpenDest(); void slotTransfersOpenFile(); void slotTransfersShowDetails(); void slotTransfersCopySourceUrl(); void slotDeleteFinished(); // misc slots void slotSaveMyself(); void slotNewToolbarConfig(); void slotNewConfig(); void slotCheckClipboard(); void slotTransferHistory(); // import links slots void slotShowListLinks(); //Model changes void slotTransfersChanged(QMap transfers); void slotGroupsChanged(QMap groups); private: /** * Returns the completed percents of all active transfers */ int transfersPercent(); // one-time functions void setupActions(); KGet * m_kget; // internal widgets ViewsContainer * m_viewsContainer; // separated widgets DropTarget * m_drop; Tray * m_dock; // actions KToggleAction * m_autoPasteAction; KToggleAction * m_menubarAction; KToggleAction * m_konquerorIntegration; // for autopaste function QString lastClipboard; // timer for checking clipboard - autopaste function QTimer *clipboardTimer; bool m_startWithoutAnimation; bool m_doTesting; // UnitTest flag //HttpServer *m_webinterface; #ifdef HAVE_QCA2 QCA::Initializer m_qcaInit; #endif //HAVE_QCA2 }; #endif diff --git a/tests/CMakeLists.txt b/tests/CMakeLists.txt index f3c9c820..4a23923a 100644 --- a/tests/CMakeLists.txt +++ b/tests/CMakeLists.txt @@ -1,71 +1,71 @@ include_directories( ../ ) #===========KGet=========== if(CMAKE_BUILD_TYPE MATCHES debugfull) ENABLE_TESTING() add_definitions(-DDO_KGET_TEST) set(kget_test_transfers_SRCS testkget.cpp testtransfers.cpp ../kget_debug.cpp ) qt5_add_dbus_interface(kget_test_transfers_SRCS ../dbus/org.kde.kget.main.xml kget_interface) qt5_add_dbus_interface(kget_test_transfers_SRCS ../dbus/org.kde.kget.transfer.xml transfer_interface) qt5_add_dbus_interface(kget_test_transfers_SRCS ../dbus/org.kde.kget.verifier.xml verifier_interface) #REACTIVATE #add_executable(kget_test_transfers ${kget_test_transfers_SRCS}) #add_test(kget_test_transfers kget_test_transfers) #ecm_mark_as_test(kget_test_transfers) #target_link_libraries(kget_test_transfers Qt5::Test KF5::KIOCore kgetcore) #===========Verifier=========== if (QCA2_FOUND) add_executable(verifiertest verifiertest.cpp) add_test(verifiertest verifiertest) ecm_mark_as_test(verifiertest) - target_link_libraries(verifiertest Qt5::Test KF5::KDELibs4Support ${QCA2_LIBRARIES} kgetcore) + target_link_libraries(verifiertest Qt5::Test ${QCA2_LIBRARIES} kgetcore) endif (QCA2_FOUND) #===========Scheduler=========== add_executable(schedulertest schedulertest.cpp) add_test(schedulertest schedulertest ) ecm_mark_as_test(schedulertest) - target_link_libraries(schedulertest Qt5::Test Qt5::Gui KF5::KDELibs4Support kgetcore) + target_link_libraries(schedulertest Qt5::Test Qt5::Gui kgetcore) #===========Metalinker=========== set(metalinktest_SRCS metalinktest.cpp ../ui/metalinkcreator/metalinker.cpp ../kget_debug.cpp ) add_executable( metalinktest ${metalinktest_SRCS}) add_test(metalinktest metalinktest) ecm_mark_as_test(metalinktest) - target_link_libraries(metalinktest KF5::KDELibs4Support KF5::KIOCore Qt5::Test) + target_link_libraries(metalinktest Qt5::Xml KF5::I18n KF5::KIOCore Qt5::Test) #===========FileDeleter=========== set(filedeletertest_SRCS filedeletertest.cpp ../core/filedeleter.cpp ) add_executable(filedeletertest ${filedeletertest_SRCS}) add_test(filedeletertest filedeletertest) ecm_mark_as_test(filedeletertest) - target_link_libraries(filedeletertest KF5::KIOCore Qt5::Test KF5::KDELibs4Support) + target_link_libraries(filedeletertest KF5::KIOCore Qt5::Test) endif(CMAKE_BUILD_TYPE MATCHES debugfull) diff --git a/tests/filedeletertest.cpp b/tests/filedeletertest.cpp index 684c0a07..c362aa16 100644 --- a/tests/filedeletertest.cpp +++ b/tests/filedeletertest.cpp @@ -1,79 +1,79 @@ /************************************************************************** * Copyright (C) 2011 Matthias Fuchs * * Code mostly from email from Will Stephenson * * * * 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. * * * * 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 "filedeletertest.h" #include "../core/filedeleter.h" #include #include #include +#include +#include #include -#include -#include void FileDeleterTest::fileDeleterTest() { //nothing started to delete yet - QVERIFY(!FileDeleter::isFileBeingDeleted(KUrl())); - QVERIFY(!FileDeleter::isFileBeingDeleted(KUrl("/tmp/aFile"))); + QVERIFY(!FileDeleter::isFileBeingDeleted(QUrl())); + QVERIFY(!FileDeleter::isFileBeingDeleted(QUrl::fromLocalFile("/tmp/aFile"))); //create file that is going to be deleted - KTemporaryFile file1; + QTemporaryFile file1; file1.setAutoRemove(false); QVERIFY(file1.open()); const QString fileUrl1 = file1.fileName(); QVERIFY(QFile::exists(fileUrl1)); //nothing started to delete yet - QVERIFY(!FileDeleter::isFileBeingDeleted(KUrl(fileUrl1))); + QVERIFY(!FileDeleter::isFileBeingDeleted(QUrl::fromLocalFile(fileUrl1))); //create two QObjects that will receive the result signal SignalReceiver receiver1; QSignalSpy spy1(&receiver1, SIGNAL(result())); QVERIFY(spy1.isEmpty()); SignalReceiver receiver2; QSignalSpy spy2(&receiver1, SIGNAL(result())); QVERIFY(spy2.isEmpty()); //delete the file - FileDeleter::deleteFile(KUrl(fileUrl1), &receiver1, SIGNAL(result())); - QVERIFY(FileDeleter::isFileBeingDeleted(KUrl(fileUrl1))); + FileDeleter::deleteFile(QUrl::fromLocalFile(fileUrl1), &receiver1, SIGNAL(result())); + QVERIFY(FileDeleter::isFileBeingDeleted(QUrl::fromLocalFile(fileUrl1))); //deleting twice with the same receiver, still the method should only be called once - FileDeleter::deleteFile(KUrl(fileUrl1), &receiver1, SIGNAL(result())); + FileDeleter::deleteFile(QUrl::fromLocalFile(fileUrl1), &receiver1, SIGNAL(result())); - KJob *job = FileDeleter::deleteFile(KUrl(fileUrl1)); + KJob *job = FileDeleter::deleteFile(QUrl::fromLocalFile(fileUrl1)); connect(job, SIGNAL(result(KJob*)), &receiver2, SIGNAL(result())); //removal should be done by now QTest::qWait(5000); - QVERIFY(!FileDeleter::isFileBeingDeleted(KUrl(fileUrl1))); + QVERIFY(!FileDeleter::isFileBeingDeleted(QUrl::fromLocalFile(fileUrl1))); QVERIFY(!QFile::exists(fileUrl1)); QCOMPARE(spy1.count(), 1); QCOMPARE(spy2.count(), 1); } QTEST_MAIN(FileDeleterTest) diff --git a/tests/metalinktest.cpp b/tests/metalinktest.cpp index d154c641..e55c4ac6 100644 --- a/tests/metalinktest.cpp +++ b/tests/metalinktest.cpp @@ -1,101 +1,101 @@ /*************************************************************************** * Copyright (C) 2011 Matthias Fuchs * * Code mostly from email from Will Stephenson * * * * 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. * * * * 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 "metalinktest.h" #include "../ui/metalinkcreator/metalinker.h" -#include +#include #include void MetalinkTest::testFilePath() { QFETCH(QString, string); QFETCH(bool, result); KGetMetalink::File file; file.name = string; QCOMPARE(file.isValidNameAttribute(), result); } void MetalinkTest::testFilePath_data() { QTest::addColumn("string"); QTest::addColumn("result"); QTest::newRow("traversal up relative to root") << "/../foo/bla" << false; QTest::newRow("traversal up at beginning") << "../foo/bla" << false; QTest::newRow("traversal up inside") << "bla/../foo/bla" << false; QTest::newRow("traversal up at end") << "foo/bla/.." << false; QTest::newRow("no file name") << "foo/bla/" << false; QTest::newRow("acceptable traversal down, contains path component") << "foo/bla" << true; } void MetalinkTest::testUrl() { - QFETCH(KUrl, url); + QFETCH(QUrl, url); QFETCH(bool, result); KGetMetalink::Url data; data.url = url; QCOMPARE(data.isValid(), result); } void MetalinkTest::testUrl_data() { - QTest::addColumn("url"); + QTest::addColumn("url"); QTest::addColumn("result"); - QTest::newRow("empty url") << KUrl() << false; - QTest::newRow("no host") << KUrl("http://") << false; - QTest::newRow("empty protocol") << KUrl("www.example.com") << false; - QTest::newRow("valid url") << KUrl("http://www.example.com") << true; + QTest::newRow("empty url") << QUrl() << false; + QTest::newRow("no host") << QUrl("http://") << false; + QTest::newRow("empty protocol") << QUrl("www.example.com") << false; + QTest::newRow("valid url") << QUrl("http://www.example.com") << true; } void MetalinkTest::testMetaUrl() { - QFETCH(KUrl, url); + QFETCH(QUrl, url); QFETCH(QString, type); QFETCH(bool, result); KGetMetalink::Metaurl data; data.url = url; data.type = type; QCOMPARE(data.isValid(), result); } void MetalinkTest::testMetaUrl_data() { - QTest::addColumn("url"); + QTest::addColumn("url"); QTest::addColumn("type"); QTest::addColumn("result"); - QTest::newRow("empty url") << KUrl() << "torrent" << false; - QTest::newRow("no host") << KUrl("http://") << "torrent" << false; - QTest::newRow("empty protocol") << KUrl("www.example.com") << "torrent" << false; - QTest::newRow("empty type") << KUrl("http://www.example.com") << QString() << false; - QTest::newRow("valid url") << KUrl("http://www.example.com") << "torrent" << true; + QTest::newRow("empty url") << QUrl() << "torrent" << false; + QTest::newRow("no host") << QUrl("http://") << "torrent" << false; + QTest::newRow("empty protocol") << QUrl("www.example.com") << "torrent" << false; + QTest::newRow("empty type") << QUrl("http://www.example.com") << QString() << false; + QTest::newRow("valid url") << QUrl("http://www.example.com") << "torrent" << true; } QTEST_MAIN(MetalinkTest) diff --git a/tests/testtransfers.cpp b/tests/testtransfers.cpp index 2b202d6c..590616bf 100644 --- a/tests/testtransfers.cpp +++ b/tests/testtransfers.cpp @@ -1,675 +1,675 @@ /* This file is part of the KDE project Copyright (C) 2009 Dario Massarin Copyright (C) 2010 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 "testtransfers.h" #include "core/transfergrouphandler.h" #include "core/transferhandler.h" #include "core/kget.h" #include "kget_debug.h" #include "kget_interface.h" #include "transfer_interface.h" #include "verifier_interface.h" #include #include #include #include #include -#include +#include #include //FIXME not working fine if files or transfers are existing already QHash Commands::s_stringCommands; QHash Commands::s_transferChangeEvents; Commands::Commands(const QString &source, QObject *parent) : QObject(parent), m_timerId(-1), m_source(source), m_transfer(nullptr), m_verifier(nullptr) { static int instance = 0; if (!instance++) { s_stringCommands["start"] = Start; s_stringCommands["stop"] = Stop; s_stringCommands["addchecksum"] = AddChecksum; s_stringCommands["addpartialchecksums"] = AddPartialChecksums; s_stringCommands["isverifyable"] = IsVerifyable; s_stringCommands["verify"] = Verify; s_stringCommands["findbrokenpieces"] = FindBrokenPieces; s_stringCommands["repair"] = Repair; s_stringCommands["setdirectory"] = SetDirectory; s_stringCommands["wait"] = Wait; s_stringCommands["randomaction"] = RandomAction; s_stringCommands["verified"] = Verified; s_stringCommands["changedevent"] = ChangedEvent; s_stringCommands["brokenpieces"] = BrokenPieces; s_transferChangeEvents["tc_filename"] = Transfer::Tc_FileName; s_transferChangeEvents["tc_status"] = Transfer::Tc_Status; s_transferChangeEvents["tc_totalsize"] = Transfer::Tc_TotalSize; s_transferChangeEvents["tc_percent"] = Transfer::Tc_Percent; s_transferChangeEvents["tc_downloadspeed"] = Transfer::Tc_DownloadSpeed; s_transferChangeEvents["tc_remainingtime"] = Transfer::Tc_RemainingTime; s_transferChangeEvents["tc_uploadspeed"] = Transfer::Tc_UploadSpeed; s_transferChangeEvents["tc_uploadlimit"] = Transfer::Tc_UploadLimit; s_transferChangeEvents["tc_downloadlimit"] = Transfer::Tc_DownloadLimit; s_transferChangeEvents["tc_canresume"] = Transfer::Tc_CanResume; s_transferChangeEvents["tc_downloadedsize"] = Transfer::Tc_DownloadedSize; s_transferChangeEvents["tc_uploadedsize"] = Transfer::Tc_UploadedSize; s_transferChangeEvents["tc_log"] = Transfer::Tc_Log; s_transferChangeEvents["tc_group"] = Transfer::Tc_Group; s_transferChangeEvents["tc_selection"] = Transfer::Tc_Selection; } } QList > Commands::parseCommands(const QDomElement& e, TestTransfers *transfer) { QList > commands; for (QDomElement elem = e.firstChildElement("command"); !elem.isNull(); elem = elem.nextSiblingElement("command")) { const QString typeString = elem.attribute("type").toLower(); if (!s_stringCommands.contains(typeString)) { qCDebug(KGET_DEBUG) << "Error while parsing, type" << typeString << "not supported."; QWARN("Problem while parsing."); return commands; } const int type = s_stringCommands[typeString]; QVariant data; QStringList args; for (QDomElement arg = elem.firstChildElement("arg"); !arg.isNull(); arg = arg.nextSiblingElement("arg")) { args << arg.text(); } switch (type) { case Verify: case FindBrokenPieces: case Start: case Stop: commands.append(QPair(type, data)); break; case IsVerifyable: case Verified: case Repair: if (args.count() == 1) { data = args.first(); if (data.canConvert(QVariant::Bool)) { commands.append(QPair(type, data)); break; } } qCDebug(KGET_DEBUG) << "Parsing IsVerifyable/Verified/Repair failed."; QWARN("Problem while parsing."); break; case AddChecksum: if (args.count() == 2) { data = args; commands.append(QPair(type, data)); } else { qCDebug(KGET_DEBUG) << "Parsing setHash failed."; QWARN("Problem while parsing."); } break; case AddPartialChecksums: { if (args.count() >= 3) { bool worked; QList list; list << args[0];//Type const qulonglong length = args[1].toULongLong(&worked); if (worked) { list << length; for (int i = 2; i < args.count(); ++i) { list << args[i]; } data = list; commands.append(QPair(type, data)); break; } } qCDebug(KGET_DEBUG) << "Parsing AddPartialChecksums failed."; QWARN("Problem while parsing."); break; } case BrokenPieces: { QList list; bool worked = true; foreach (const QString &arg, args) { const int value = arg.toInt(&worked); if (!worked) { break; } list << value; } if (worked) { data = list; commands.append(QPair(type, data)); break; } qCDebug(KGET_DEBUG) << "Parsing BrokenPieces failed."; QWARN("Problem while parsing."); break; } case RandomAction: { QList list; if (args.count() == 1) { data = args.takeFirst(); if (data.canConvert(QVariant::Bool) && !data.toBool()) { list << data.toBool(); data = list; commands.append(QPair(type, data)); break; } } else if (args.count() == 2) { data = args.takeFirst(); if (data.canConvert(QVariant::Bool) && data.toBool()) { list << data.toBool(); bool worked; list << args.takeFirst().toInt(&worked); if (worked) { data = list; commands.append(QPair(type, data)); break; } } } qCDebug(KGET_DEBUG) << "Parsing RandomAction failed."; QWARN("Problem while parsing."); break; } case SetDirectory: if (args.count() == 2) { data = args.last(); QString newDirectory = args.first(); if (transfer) { newDirectory.replace("${DIR}/", transfer->tempDir()); } if (!newDirectory.isEmpty() && data.canConvert(QVariant::Bool)) { const bool shouldWork = data.toBool(); QList list; list << newDirectory << shouldWork; data = list; commands.append(QPair(type, data)); break; } } qCDebug(KGET_DEBUG) << "Parsing SetDirectory failed."; QWARN("Problem while parsing."); break; case Wait: if (args.count() == 1) { bool worked; data = args.first().toInt(&worked); if (worked) { commands.append(QPair(type, data)); break; } } qCDebug(KGET_DEBUG) << "Parsing Wait failed."; QWARN("Problem while parsing."); break; case ChangedEvent: if (!args.isEmpty() && (args.count() <= 2)) { QList list; const QString typeString = args.first().toLower(); if (s_transferChangeEvents.contains(typeString)) { int value = s_transferChangeEvents[typeString]; list << value; if (args.count() == 2) { bool worked; value = args.last().toInt(&worked); if (worked) { list << value; data = list; commands.append(QPair(type, data)); break; } } else { data = list; commands.append(QPair(type, data)); break; } } } qCDebug(KGET_DEBUG) << "Parsing ChangedEvent failed" << args; QWARN("Problem while parsing."); break; default: QWARN("Default should never happen."); break; } } return commands; } QString Commands::source() const { return m_source; } void Commands::associateTransfer(OrgKdeKgetTransferInterface *transfer) { if (m_transfer) { disconnect(m_transfer, nullptr, this, nullptr); } if (m_verifier) { disconnect(m_verifier, nullptr, this, nullptr); delete m_verifier; } m_transfer = transfer; qCDebug(KGET_DEBUG) << this << "associated with" << m_transfer << m_source; QDBusPendingReply reply = m_transfer->dest(); const QString dest = reply.value(); reply = m_transfer->verifier(dest); m_verifier = new OrgKdeKgetVerifierInterface("org.kde.kget", reply.value(), QDBusConnection::sessionBus(), this); connect(m_verifier, SIGNAL(verified(bool)), this, SLOT(slotVerified(bool))); connect(m_verifier, SIGNAL(brokenPieces(QStringList,qulonglong)), this, SLOT(slotBrokenPieces(QStringList,qulonglong))); connect(m_transfer, SIGNAL(transferChangedEvent(int)), this, SLOT(slotChangedEvent(int))); } bool Commands::hasCommands() const { return !m_commands.isEmpty(); } void Commands::setCommands(const QList > &commands) { m_commands = commands; } void Commands::timerEvent(QTimerEvent *event) { Q_UNUSED(event) const int value = qrand() % 10; //70% of the cases start, in 30% stop if (value > 2) { qCDebug(KGET_DEBUG) << this << "is randomly started."; m_transfer->start(); } else { qCDebug(KGET_DEBUG) << this << "is randomly stopped"; m_transfer->stop(); } } void Commands::slotWaitEvent() { //wait is over so continue with executing commands if (!m_commands.isEmpty()) { m_commands.takeFirst(); executeCommands(); } } void Commands::executeCommands() { if (!m_transfer) { QFAIL("Calling executeCommands when no transfer is set."); } //NOTE no checks are being done if the commands are correct, this is to ensure that KGet crashes //on faulty commands making sure that those are found while (!m_commands.isEmpty()) { const QPair operation = m_commands.first(); const int type = operation.first; if (type >= CustomEvent) { return; } const QVariant command = operation.second; switch (type) { case Start: m_transfer->start(); qCDebug(KGET_DEBUG) << this << "is started."; break; case Stop: m_transfer->stop(); qCDebug(KGET_DEBUG) << this << "is stopped."; break; case AddChecksum: { QStringList hash = command.toStringList(); qCDebug(KGET_DEBUG) << this << "adding hash" << hash; QDBusPendingReply reply = m_verifier->addChecksum(hash.takeFirst(), hash.takeLast()); break; } case AddPartialChecksums: { QList list = command.toList(); qCDebug(KGET_DEBUG) << this << "adding partial hash" << list; const QString type = list.takeFirst().toString(); const qulonglong length = list.takeFirst().toULongLong(); QStringList checksums; foreach (const QVariant &value, list) { checksums << value.toString(); } m_verifier->addPartialChecksums(type, length, checksums); break; } case IsVerifyable: { const bool shouldWork = command.toBool(); QDBusPendingReply reply = m_verifier->isVerifyable(); qCDebug(KGET_DEBUG) << this << "isVerifyable" << reply.value(); QVERIFY(reply.value() == shouldWork); break; } case Verify: { qCDebug(KGET_DEBUG) << this << "verification started."; m_verifier->verify(); break; } case FindBrokenPieces: qCDebug(KGET_DEBUG) << this << "find broken pieces."; m_verifier->brokenPieces(); break; case Repair: { const bool shouldWork = command.toBool(); QDBusPendingReply dest = m_transfer->dest(); QDBusPendingReply reply = m_transfer->repair(dest.value()); const bool isRepairable = reply.value(); qCDebug(KGET_DEBUG) << this << "repair started" << isRepairable; QVERIFY(isRepairable == shouldWork); break; } case SetDirectory: { QList commands = command.toList(); const QString newDirectory = commands.takeFirst().toString(); const bool shouldWork = commands.takeFirst().toBool(); QDBusPendingReply reply = m_transfer->setDirectory(newDirectory); const bool moveStarted = reply.value(); qCDebug(KGET_DEBUG) << this << "set changing directory started" << moveStarted; QVERIFY(moveStarted == shouldWork); break; } case Wait: { const int time = command.toInt(); qCDebug(KGET_DEBUG) << this << "waiting for" << time << "msecs" << m_transfer; QTimer::singleShot(time, this, SLOT(slotWaitEvent())); return; break; } case RandomAction: { QList commands = command.toList(); const bool turnOn = commands.takeFirst().toBool(); if (m_timerId == -1) { if (turnOn) { qCDebug(KGET_DEBUG) << this << "starting random timer."; m_timerId = startTimer(commands.takeFirst().toInt()); } } else { qCDebug(KGET_DEBUG) << this << "killing random timer."; killTimer(m_timerId); m_timerId = -1; if (turnOn) { qCDebug(KGET_DEBUG) << this << "starting random timer."; m_timerId = startTimer(commands.takeFirst().toInt()); } } break; } } m_commands.takeFirst(); } } void Commands::slotChangedEvent(int event) { if (!m_transfer || m_commands.isEmpty()) { return; } const QPair operation = m_commands.first(); const int type = operation.first; if (type != ChangedEvent) { return; } const QList commands = operation.second.toList(); //at least one and maximum two commands allowed; if (commands.isEmpty() || commands.count() > 2) { return; } bool worked; const int eventType = commands.first().toInt(&worked); if (!worked || !(eventType & event)) { return; } if (commands.count() == 2) { const int compareValue = commands.last().toInt(&worked); if (!worked) { return; } switch (eventType) { case Transfer::Tc_Status: { QDBusPendingReply reply = m_transfer->status(); if (reply.value() == compareValue) { m_commands.takeFirst(); executeCommands(); } break; } case Transfer::Tc_Percent: { QDBusPendingReply reply = m_transfer->percent(); if (reply.value() >= compareValue) { qCDebug(KGET_DEBUG) << this << "ChangedEvent percent."; m_commands.takeFirst(); executeCommands(); } break; } default: break; } } else { m_commands.takeFirst(); executeCommands(); } } void Commands::slotVerified(bool verified) { if (!m_commands.isEmpty()) { const QPair operation = m_commands.first(); const int type = operation.first; if (type == Verified) { const QVariant command = operation.second; m_commands.takeFirst(); if (command.canConvert(QVariant::Bool)) { const bool shouldWork = command.toBool(); qCDebug(KGET_DEBUG) << this << "is verified" << verified; QVERIFY(verified == shouldWork); } executeCommands(); return; } } qCDebug(KGET_DEBUG) << this << "is verified" << verified; QVERIFY(verified); } void Commands::slotBrokenPieces(const QStringList &offsets, qulonglong length) { if (m_commands.isEmpty()) { return; } const QPair operation = m_commands.first(); const int type = operation.first; if (type != BrokenPieces) { return; } // QList list = brokenPieces.variant().toList();//FIXME QList compareList = operation.second.toList(); if (offsets.count() != compareList.count()) { QFAIL("Emitted brokenPieces list does not match."); } for (int i = 0; i < offsets.count(); ++i) { QVERIFY(static_cast(offsets[i].toULongLong() / length) == compareList[i].toInt()); } m_commands.takeFirst(); executeCommands(); } TestTransfers::TestTransfers() { if(!QDBusConnection::sessionBus().interface()->isServiceRegistered("org.kde.kget")) { KRun::runCommand("kget --showDropTarget --hideMainWindow", "kget", "kget", nullptr); } - m_dir.reset(new KTempDir()); + m_dir.reset(new QTemporaryDir()); qCDebug(KGET_DEBUG) << "Using temp dir:" << tempDir(); //TODO add a signal to check if the move worked!! // << "http://mirrors.isc.org/pub/kde/stable/4.3.1/src/kdeedu-4.3.1.tar.bz2" // << "http://mirrors.isc.org/pub/kde/stable/4.3.1/src/kdegames-4.3.1.tar.bz2" // << "http://mirrors.isc.org/pub/kde/stable/4.3.1/src/kdegraphics-4.3.1.tar.bz2" // << "http://mirrors.isc.org/pub/kde/stable/4.3.1/src/kdelibs-4.3.1.tar.bz2" // << "http://mirrors.isc.org/pub/kde/stable/4.3.1/src/kdelibs-experimental-4.3.1.tar.bz2" // << "http://mirrors.isc.org/pub/kde/stable/4.3.1/src/kdemultimedia-4.3.1.tar.bz2" // << "http://mirrors.isc.org/pub/kde/stable/4.3.1/src/kdenetwork-4.3.1.tar.bz2" // << "http://mirrors.isc.org/pub/kde/stable/4.3.1/src/kdepim-4.3.1.tar.bz2" // << "http://mirrors.isc.org/pub/kde/stable/4.3.1/src/kdepim-runtime-4.3.1.tar.bz2" // << "http://mirrors.isc.org/pub/kde/stable/4.3.1/src/kdepimlibs-4.3.1.tar.bz2" // << "http://mirrors.isc.org/pub/kde/stable/4.3.1/src/kdeplasma-addons-4.3.1.tar.bz2" // << "http://mirrors.isc.org/pub/kde/stable/4.3.1/src/kdesdk-4.3.1.tar.bz2" // << "http://mirrors.isc.org/pub/kde/stable/4.3.1/src/kdetoys-4.3.1.tar.bz2" // << "http://mirrors.isc.org/pub/kde/stable/4.3.1/src/kdewebdev-4.3.1.tar.bz2" // << "http://mirrors.isc.org/pub/kde/stable/4.3.1/src/oxygen-icons-4.3.1.tar.bz2"; // << "6326cff7779dfadc1b18a3a6bbe7b0750fb7ceaf" // << "576255ce66a0c089e0840bd90ea89d5705872bc8" // << "d57d9007b95607c0ee925cc963d7e14798fc69f9" // << "511532852caca9302c643fded4013ef1f57d5433" // << "7d560817a186c4b7099d321ee4a58705962a59d3" // << "ef50f869f1a6cdf91fe7808f095fccbd9463a7dd" // << "57194b5f89b37329e562951c491fa18029c0b431" // << "6fd0309dbd911e2667ec1c08d1f10f2626a54534" // << "c39b0fc1d3721fb8c6074ba6a174ad8716c6c604" // << "f4b04b21a6aa3accc530bc6c32cf0d820c611265" // << "83421181dd3a80c4ac0ff5bab111b7f71f6a1192" // << "ded236a12002b824f97856ce5dc882161ed437d2" // << "31a60deafef34a02fb7de5339eed1c750a456d3b" // << "28580c6f283fa7a6405f6a4415ebe9a4167f0992" // << "75a82d2e80d946333f63e32db56767c3ed17ba33"; } QString TestTransfers::tempDir() const { return m_dir->name(); } void TestTransfers::parseFile() { QFile file(QString::fromLocal8Bit(KDESRCDIR) + "/kget-test.xml"); if (!file.open(QIODevice::ReadOnly)) { QFAIL("XML file not found."); } QDomDocument doc; if (!doc.setContent(&file)) { file.close(); QFAIL("Not able to set content."); } file.close(); for (QDomElement elem = doc.firstChildElement("tests").firstChildElement("transfer"); !elem.isNull(); elem = elem.nextSiblingElement("transfer")) { const QString source = elem.attribute("source"); if (source.isEmpty()) { QFAIL("One transfer does not have an source attribute."); return; } Commands *transfer = new Commands(source, this); transfer->setCommands(Commands::parseCommands(elem, this)); m_commands.append(transfer); } } void TestTransfers::createTransfer() { if (m_commands.isEmpty()) { QFAIL("No commands set."); } if (!QDBusConnection::sessionBus().interface()->isServiceRegistered("org.kde.kget")) { qCDebug(KGET_DEBUG) << "Service not registered yet, retrying."; QTimer::singleShot(500, this, SLOT(createTransfer())); return; } QVERIFY(QDBusConnection::sessionBus().interface()->isServiceRegistered("org.kde.kget")); OrgKdeKgetMainInterface kgetInterface("org.kde.kget", "/KGet", QDBusConnection::sessionBus()); foreach (Commands *command, m_commands) { QDBusPendingReply reply = kgetInterface.addTransfer(command->source(), tempDir() + KUrl(command->source()).fileName(), false); reply.waitForFinished(); if (reply.value().size()) { qCDebug(KGET_DEBUG) << "TestTransfers::createTransfer -> transfer = " << reply.value(); OrgKdeKgetTransferInterface *transfer = new OrgKdeKgetTransferInterface("org.kde.kget", reply.value().first(), QDBusConnection::sessionBus(), this); command->associateTransfer(transfer); command->executeCommands(); } else { QFAIL("Reply NOT VALID!!!"); } } } void TestTransfers::simpleTest() { QVERIFY(1 == 1); parseFile(); createTransfer(); //execute as long as there are still commands left forever { bool abort = true; foreach (Commands *commands, m_commands) { if (commands->hasCommands()) { abort = false; break; } } if (abort) { break; } QTest::qWait(3000); } } QTEST_KDEMAIN(TestTransfers, GUI) diff --git a/tests/testtransfers.h b/tests/testtransfers.h index a4d76cb3..5a2b2ca2 100644 --- a/tests/testtransfers.h +++ b/tests/testtransfers.h @@ -1,107 +1,107 @@ /* This file is part of the KDE project Copyright (C) 2009 Dario Massarin Copyright (C) 2010 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. */ #ifndef _TESTTRANSFERS_H #define _TESTTRANSFERS_H #include #include #include -#include +#include class OrgKdeKgetTransferInterface; class OrgKdeKgetVerifierInterface; class TestTransfers; class Commands : public QObject { Q_OBJECT public: Commands(const QString &source, QObject *parent); QString source() const; bool hasCommands() const; void setCommands(const QList > &commands); void executeCommands(); void associateTransfer(OrgKdeKgetTransferInterface *transfer); static QList > parseCommands(const QDomElement &e, TestTransfers *transfer); enum Action { Start, Stop, AddChecksum, //QStringList() << type << hash AddPartialChecksums, //QList() << QString typ << qulonglong length << QStringList checksums IsVerifyable, //bool if verifyable Verify, FindBrokenPieces, Repair, //bool if it works SetDirectory, Wait, //int time; waits until time is over and then proceeds to executeCommands RandomAction, //QList << bool false OR QList << bool true << int timeBetweenActions; chooses automatically periodially Start or Stop CustomEvent = 100 }; /** * Event is a list of Events, events are blocking, so every command after an event * is blocked, unless the event happens, that way one can start a Repair when Verified returns false */ enum Event { Verified = CustomEvent + 1, //bool verified ChangedEvent = CustomEvent + 2, //QList() << int TransferChange << int triggerValue = optional; when the event and the triggervalue match then executing continues; e.g. QList() << Transfer::Tc_Percent << 50; //goes on when more than 50% have been downloaded BrokenPieces = CustomEvent + 3 }; protected: void timerEvent(QTimerEvent *event); private slots: void slotVerified(bool verified); void slotBrokenPieces(const QStringList &offsets, qulonglong length); void slotChangedEvent(int event); void slotWaitEvent(); private: int m_timerId; const QString m_source; QList > m_commands; OrgKdeKgetTransferInterface *m_transfer; OrgKdeKgetVerifierInterface *m_verifier; static QHash s_stringCommands; static QHash s_transferChangeEvents; }; class TestTransfers: public QObject { Q_OBJECT public: TestTransfers(); QString tempDir() const; public slots: void createTransfer(); private slots: void simpleTest(); private: void parseFile(); private: QList m_transferIfaces; QList m_commands; - QScopedPointer m_dir; + QScopedPointer m_dir; }; #endif diff --git a/tests/verifiertest.cpp b/tests/verifiertest.cpp index c9e54fa0..d3f0678a 100644 --- a/tests/verifiertest.cpp +++ b/tests/verifiertest.cpp @@ -1,354 +1,349 @@ #include "verifiertest.h" #include "../settings.h" #include "../core/verifier.h" #include - #include - -#include +#include typedef QPair Checksum; typedef QPair Checksums; Q_DECLARE_METATYPE(Checksum) Q_DECLARE_METATYPE(QList) Q_DECLARE_METATYPE(Checksums) Q_DECLARE_METATYPE(QList) Q_DECLARE_METATYPE(Verifier::ChecksumStrength) Q_DECLARE_METATYPE(QList) VerfierTest::VerfierTest(QObject *parent) : QObject(parent), m_supported(Verifier::supportedVerficationTypes()) { //create a file which will used in the test - m_tempDir.reset(new KTempDir(QDir::tempPath() + QStringLiteral("/kget_test"))); + m_tempDir.reset(new QTemporaryDir(QDir::tempPath() + QStringLiteral("/kget_test"))); QString path = m_tempDir->name(); path.append("test.txt"); QFile file(path); if (!file.open(QIODevice::WriteOnly | QIODevice::Truncate)) { qCCritical(KGET_DEBUG) << "Creating file failed:" << path; abort(); } - m_file = KUrl(path); + m_file = QUrl::fromLocalFile(path); const QByteArray data("This is a test for the KGet Verifier class.\n"); const qint64 size = data.size(); for (int i = 0; i < 50000; ++i) { if (file.write(data) != size) { qCCritical(KGET_DEBUG) << "Creating file failed:" << path; abort(); } } qCDebug(KGET_DEBUG) << "Supported types:" << m_supported; - - //Otherwise testVerify fails - qRegisterMetaType("KUrl"); } void VerfierTest::testChecksum() { QFETCH(QString, type); QFETCH(QString, checksum); if (!m_supported.contains(type)) { return; } QCOMPARE(Verifier::checksum(m_file, type, nullptr), checksum); } void VerfierTest::testChecksum_data() { QTest::addColumn("type"); QTest::addColumn("checksum"); QTest::newRow("md5 checksum") << "md5" << "1c3b1b627e4f236fdac8f6ab3ee160d1"; QTest::newRow("sha1 checksum") << "sha1" << "0a4886f5c6e45c5a879b0093a43cb06fc5568bc5"; QTest::newRow("sha256 checksum") << "sha256" << "9cf2cfe941569d8627dc3ede2897dea1c3949eb6bd124d3bca06a888492f955b"; QTest::newRow("sha384 checksum") << "sha384" << "a27d95917b3d3ff9145fd9a66cc9852f3a15e7ebac9e7112d4260e2747d45c89a949fe53d110ecc260e246f959ca30e9"; QTest::newRow("sha512 checksum") << "sha512" << "4c6d2020482ddb1c6f7275f40fb35084805f06a23e433420e46b794ef7ad9770d527c4a7f5dc50bdf7acbd3f2e0a34f00a1b7283a1b222da6835f93505db6641"; } void VerfierTest::testPartialChecksums() { QFETCH(QString, type); QFETCH(KIO::filesize_t, length); QFETCH(QStringList, checksums); QFETCH(bool, result); const PartialChecksums partial = Verifier::partialChecksums(m_file, type, length, nullptr); QCOMPARE((partial.checksums() == checksums), result); } void VerfierTest::testPartialChecksums_data() { QTest::addColumn("type"); QTest::addColumn("length"); QTest::addColumn("checksums"); QTest::addColumn("result"); QTest::newRow("md5, empty checksums") << "md5" << KIO::filesize_t(512 * 1000) << QStringList() << expectedResult(false, "md5"); QTest::newRow("md5, one checksum is too long, 500 KiB") << "md5" << KIO::filesize_t(500 * 1024) << (QStringList() << "AAAAA8c53d0109e5222e2c57d187eec33ab71" << "f13b7c9978ac7edac8b1c2b9be55abb0" <<"643086ed0d7e733629c848e94e29cdc4" << "6c349ee55737d9bc45ef1b1e75ec7bdf" << "40363ab59bbfa39f6faa4aa18ee75a6c") << expectedResult(false, "md5"); QTest::newRow("valid md5 checksums, 500 KiB") << "md5" << KIO::filesize_t(500 * 1024) << (QStringList() << "8c53d0109e5222e2c57d187eec33ab71" << "f13b7c9978ac7edac8b1c2b9be55abb0" <<"643086ed0d7e733629c848e94e29cdc4" << "6c349ee55737d9bc45ef1b1e75ec7bdf" << "40363ab59bbfa39f6faa4aa18ee75a6c") << expectedResult(true, "md5"); QTest::newRow("valid md5 checksums, 400 KiB") << "md5" << KIO::filesize_t(400 * 1024) << (QStringList() << "ce7d795bd0b1499f18d2ba8f338302d3" << "e6681cc0049c6cae347039578eaf1117" << "e66499f43f930fb013092a2d66ecdfaf" << "b2930c55fea65c11813dff09447fbe92" << "77b79bd53b62accec6367c7d7b2fc85b" << "40363ab59bbfa39f6faa4aa18ee75a6c") << expectedResult(true, "md5"); QTest::newRow("valid md5 checksums, one part with file size") << "md5" << KIO::filesize_t(2200000) << (QStringList() << "1c3b1b627e4f236fdac8f6ab3ee160d1") << expectedResult(true, "md5"); QTest::newRow("valid sha1 checksums, 1000 KiB") << "sha1" << KIO::filesize_t(1000 * 1024) << (QStringList() << "5d76447e7655fd1d4dfba458c33340757d81eb95" << "0bc9428e3b39fc34ab457d58d62f1973a1183ac2" << "48a313a958ea1c55eb9527b0141ae30742fedfdb") << expectedResult(true, "sha1"); } void VerfierTest::testIsChecksum() { QFETCH(QString, type); QFETCH(QString, checksum); QFETCH(bool, result); QCOMPARE(Verifier::isChecksum(type, checksum), result); } void VerfierTest::testIsChecksum_data() { QTest::addColumn("type"); QTest::addColumn("checksum"); QTest::addColumn("result"); const QStringList supported = Verifier::supportedVerficationTypes(); QTest::newRow("md5, too many chars") << "md5" << "1c3b1b627e4f236fdac8f6ab3ee160d1a" << expectedResult(false, "md5"); QTest::newRow("md5, correct length, one wrong char") << "md5" << "1c b1b627e4f236fdac8f6ab3ee160d1" << expectedResult(false, "md5"); QTest::newRow("md5, correct length, many wrong chars") << "md5" << "1c[b1b627e4f236fd)c8f6ab3_e160d1" << expectedResult(false, "md5"); QTest::newRow("valid md5 checksum") << "md5" << "1C3B1b627e4F236fdac8f6ab3ee160d1" << expectedResult(true, "md5"); QTest::newRow("valid sha1 checksum") << "sha1" << "0a4886f5c6e45c5a879b0093a43cb06fc5568bc5" << expectedResult(true, "sha1"); QTest::newRow("valid sha256 checksum") << "sha256" << "9cf2cfe941569d8627dc3ede2897dea1c3949eb6bd124d3bca06a888492f955b" << expectedResult(true, "sha256"); QTest::newRow("valid sha384 checksum") << "sha384" << "a27d95917b3d3ff9145fd9a66cc9852f3a15e7ebac9e7112d4260e2747d45c89a949fe53d110ecc260e246f959ca30e9" << expectedResult(true, "sha384"); QTest::newRow("valid sha512 checksum") << "sha512" << "4c6d2020482ddb1c6f7275f40fb35084805f06a23e433420e46b794ef7ad9770d527c4a7f5dc50bdf7acbd3f2e0a34f00a1b7283a1b222da6835f93505db6641" << expectedResult(true, "sha512"); } void VerfierTest::testAvailableChecksum() { QFETCH(QList, checksums); QFETCH(Verifier::ChecksumStrength, strength); QFETCH(Checksum, result); Verifier verifier(m_file); for (int i = 0; i < checksums.size(); ++i) { verifier.addChecksum(checksums[i].first, checksums[i].second); } const Checksum returned = verifier.availableChecksum(strength); QCOMPARE(returned, result); } void VerfierTest::testAvailableChecksum_data() { QTest::addColumn >("checksums"); QTest::addColumn("strength"); QTest::addColumn("result"); const Checksum md5sum("md5", "1c3b1b627e4f236fdac8f6ab3ee160d1"); const Checksum md5sumInvalid("md5", "1c b1b627e4f236fdac8f6ab3ee160d1"); const Checksum sha1sum("sha1", "0a4886f5c6e45c5a879b0093a43cb06fc5568bc5"); const Checksum sha1sumInvalid("sha1", "0a4886f5c6e45c5a879b0093a43cb06fc5568bc"); const Checksum emptysum; QTest::newRow("weak (md5), md5 should be returned") << (QList() << md5sum) << Verifier::Weak << md5sum; QTest::newRow("strong (md5), md5 should be returned") << (QList() << md5sum) << Verifier::Strong << md5sum; QTest::newRow("strongest (md5), md5 should be returned") << (QList() << md5sum) << Verifier::Strongest << md5sum; QTest::newRow("weak (md5), nothing should be returned (md5 invalid)") << (QList() << md5sumInvalid) << Verifier::Weak << emptysum; QTest::newRow("strong (md5), nothing should be returned (md5 invalid)") << (QList() << md5sumInvalid) << Verifier::Strong << emptysum; QTest::newRow("strongest (md5), nothing should be returned (md5 invalid)") << (QList() << md5sumInvalid) << Verifier::Strongest << emptysum; if (m_supported.contains("sha1")) { QTest::newRow("weak (md5, sha1), sha1 should be returned (md5 invalid)") << (QList() << md5sumInvalid << sha1sum) << Verifier::Weak << sha1sum; QTest::newRow("strong (md5, sha1), md5 should be returned (sha1 invalid)") << (QList() << md5sum << sha1sumInvalid) << Verifier::Strong << md5sum; QTest::newRow("strongest (md5, sha1), md5 should be returned (sha1 invalid)") << (QList() << md5sum << sha1sumInvalid) << Verifier::Strongest << md5sum; QTest::newRow("strong (md5, sha1), sha1 should be returned") << (QList() << md5sum << sha1sum) << Verifier::Strong << sha1sum; QTest::newRow("strong (md5, sha1), sha1 should be returned") << (QList() << md5sum << sha1sum) << Verifier::Strongest << sha1sum; QTest::newRow("strongest (md5, sha1), nothing should be returned (md5, sha1 invalid)") << (QList() << md5sumInvalid << sha1sumInvalid) << Verifier::Strongest << emptysum; } } void VerfierTest::testAvailablePartialChecksum() { QFETCH(QList, data); QFETCH(Verifier::ChecksumStrength, strength); QFETCH(Checksums, result); Verifier verifier(m_file); for (int i = 0; i < data.size(); ++i) { verifier.addPartialChecksums(data[i].first, data[i].second.length(), data[i].second.checksums()); } const QPair returned = verifier.availablePartialChecksum(strength); QVERIFY(!returned.first.isEmpty()); QVERIFY(returned.second); if (returned.second) { QCOMPARE(returned.first, result.first); QCOMPARE(returned.second->checksums(), result.second.checksums()); QCOMPARE(returned.second->length(), result.second.length()); } } void VerfierTest::testAvailablePartialChecksum_data() { QTest::addColumn >("data"); QTest::addColumn("strength"); QTest::addColumn("result"); const Checksums md5Sums("md5", PartialChecksums(400 * 1024, (QStringList() << "ce7d795bd0b1499f18d2ba8f338302d3" << "e6681cc0049c6cae347039578eaf1117" << "e66499f43f930fb013092a2d66ecdfaf" << "b2930c55fea65c11813dff09447fbe92" << "77b79bd53b62accec6367c7d7b2fc85b" << "40363ab59bbfa39f6faa4aa18ee75a6c"))); const Checksums sha1Sums("sha1", PartialChecksums(1000 * 1024, (QStringList() << "5d76447e7655fd1d4dfba458c33340757d81eb95" << "0bc9428e3b39fc34ab457d58d62f1973a1183ac2" << "48a313a958ea1c55eb9527b0141ae30742fedfdb"))); const Checksums sha512Sums("sha512", PartialChecksums(2000 * 1024, (QStringList() << "ba9c09d26d7ec7ff60671bc72ec9fed10dee851ae951fbb9e41061490fc10019076982b4c25723870bb5ff17401fdd6d21db43f6018a0604177197db384122d3" << "a63af512c40951216f20c01f5d5623af4c24b907f9a78ded98c14ab550e23764cd131961cbf45df7dfcb17b3cca7443db12de1e0540ed421579d15ccfc7863d0"))); QTest::newRow("weak (md5), md5 should be returned") << (QList() << md5Sums) << Verifier::Weak << md5Sums; QTest::newRow("strong (md5), md5 should be returned") << (QList() << md5Sums) << Verifier::Strong << md5Sums; QTest::newRow("strongest (md5), md5 should be returned") << (QList() << md5Sums) << Verifier::Strongest << md5Sums; if (m_supported.contains("sha1")) { QTest::newRow("weak (md5, sha1), md5 should be returned") << (QList() << md5Sums << sha1Sums) << Verifier::Weak << md5Sums; QTest::newRow("strong (md5, sha1), sha1 should be returned") << (QList() << md5Sums << sha1Sums) << Verifier::Strong << sha1Sums; QTest::newRow("strongest (md5, sha1), sha1 should be returned") << (QList() << md5Sums << sha1Sums) << Verifier::Strongest << sha1Sums; if (m_supported.contains("sha512")) { QTest::newRow("weak (md5, sha1, sha512), md5 should be returned") << (QList() << md5Sums << sha1Sums << sha512Sums) << Verifier::Weak << md5Sums; QTest::newRow("strong (md5, sha1, sha512), sha1 should be returned") << (QList() << md5Sums << sha1Sums << sha512Sums) << Verifier::Strong << sha1Sums; QTest::newRow("strongest (md5, sha1, sha512), sha512 should be returned") << (QList() << md5Sums << sha1Sums << sha512Sums) << Verifier::Strongest << sha512Sums; } } } bool VerfierTest::expectedResult(bool expected, const QString& type) { return (expected && m_supported.contains(type)); } void VerfierTest::testVerify() { QFETCH(QList, checksums); QFETCH(Verifier::ChecksumStrength, strength); QFETCH(bool, result); const Verifier::ChecksumStrength tempStrength = static_cast(Settings::checksumStrength()); Settings::setChecksumStrength(strength); Verifier verifier(m_file); for (int i = 0; i < checksums.size(); ++i) { verifier.addChecksum(checksums[i].first, checksums[i].second); } QSignalSpy stateSpy(&verifier, SIGNAL(verified(bool))); QVERIFY(stateSpy.isValid()); QCOMPARE(stateSpy.count(), 0); verifier.verify(); //wait for a maximum of 5 seconds, the slowest computer should be done by then for (int i = 0; !stateSpy.count() && (i < 50); ++i) { QTest::qWait(100); } QCOMPARE(stateSpy.count(), 1); const QList argument = stateSpy.takeFirst(); QCOMPARE(argument.count(), 1); QCOMPARE(argument.first().toBool(), result); Settings::setChecksumStrength(tempStrength); } void VerfierTest::testVerify_data() { QTest::addColumn >("checksums"); QTest::addColumn("strength"); QTest::addColumn("result"); const Checksum md5sumCorrect("md5", "1c3b1b627e4f236fdac8f6ab3ee160d1"); const Checksum md5sumWrong("md5", "ac3b1b627e4f236fdac8f6ab3ee160d1"); const Checksum md5sumInvalid("md5", "1c b1b627e4f236fdac8f6ab3ee160d1"); const Checksum sha1sumCorrect("sha1", "0a4886f5c6e45c5a879b0093a43cb06fc5568bc5"); const Checksum sha1sumWrong("sha1", "aa4886f5c6e45c5a879b0093a43cb06fc5568bc5"); const Checksum sha1sumInvalid("sha1", "0a4886f5c6e45c5a879b0093a43cb0"); QTest::newRow("weak (md5), should be verified") << (QList() << md5sumCorrect) << Verifier::Weak << true; QTest::newRow("strong (md5), should be verified") << (QList() << md5sumCorrect) << Verifier::Strong << true; QTest::newRow("strongest (md5), should be verified") << (QList() << md5sumCorrect) << Verifier::Strongest << true; QTest::newRow("weak (md5), should not be verified (md5 wrong)") << (QList() << md5sumWrong) << Verifier::Weak << false; if (m_supported.contains("sha1")) { QTest::newRow("weak (md5, sha1), should not be verified (md5 wrong)") << (QList() << md5sumWrong << sha1sumCorrect) << Verifier::Weak << false; QTest::newRow("weak (md5, sha1), should be verified (md5 invalid)") << (QList() << md5sumInvalid << sha1sumCorrect) << Verifier::Weak << true; QTest::newRow("strong (md5, sha1), should not be verified (sha1 wrong)") << (QList() << md5sumCorrect << sha1sumWrong) << Verifier::Strong << false; QTest::newRow("strong (md5, sha1), should be verified (sha1 invalid)") << (QList() << md5sumCorrect << sha1sumInvalid) << Verifier::Strong << true; QTest::newRow("strong (md5, sha1), should be verified") << (QList() << md5sumCorrect << sha1sumCorrect) << Verifier::Strong << true; QTest::newRow("strongest (md5, sha1), should be verified (sha1 invalid)") << (QList() << md5sumCorrect << sha1sumInvalid) << Verifier::Strongest << true; } } void VerfierTest::testBrokenPieces() { QFETCH(QList, data); QFETCH(Verifier::ChecksumStrength, strength); QFETCH(QList, offsets); QFETCH(KIO::filesize_t, length); const Verifier::ChecksumStrength tempStrength = static_cast(Settings::checksumStrength()); Settings::setChecksumStrength(strength); Verifier verifier(m_file); for (int i = 0; i < data.size(); ++i) { verifier.addPartialChecksums(data[i].first, data[i].second.length(), data[i].second.checksums()); } QSignalSpy stateSpy(&verifier, SIGNAL(brokenPieces(QList,KIO::filesize_t))); QVERIFY(stateSpy.isValid()); QCOMPARE(stateSpy.count(), 0); verifier.brokenPieces(); //wait for a maximum of 5 seconds, the slowest computer should be done by then for (int i = 0; !stateSpy.count() && (i < 50); ++i) { QTest::qWait(100); } QCOMPARE(stateSpy.count(), 1); const QList argument = stateSpy.takeFirst(); QCOMPARE(argument.count(), 2); const QList returnedOffsets = qvariant_cast >(argument[0]); QCOMPARE(returnedOffsets, offsets); const KIO::filesize_t returnedLength = qvariant_cast(argument[1]); QCOMPARE(returnedLength, length); Settings::setChecksumStrength(tempStrength); } void VerfierTest::testBrokenPieces_data() { QTest::addColumn >("data"); QTest::addColumn("strength"); QTest::addColumn >("offsets"); QTest::addColumn("length"); const QList emptyOffsets; const KIO::filesize_t md5Length = 400 * 1024; const Checksums md5sumsCorrect("md5", PartialChecksums(md5Length, (QStringList() << "ce7d795bd0b1499f18d2ba8f338302d3" << "e6681cc0049c6cae347039578eaf1117" << "e66499f43f930fb013092a2d66ecdfaf" << "b2930c55fea65c11813dff09447fbe92" << "77b79bd53b62accec6367c7d7b2fc85b" << "40363ab59bbfa39f6faa4aa18ee75a6c"))); const Checksums md5sumsWrong1("md5", PartialChecksums(md5Length, (QStringList() << "ae7d795bd0b1499f18d2ba8f338302d3" << "e6681cc0049c6cae347039578eaf1117" << "e66499f43f930fb013092a2d66ecdfaf" << "b2930c55fea65c11813dff09447fbe92" << "77b79bd53b62accec6367c7d7b2fc85b" << "40363ab59bbfa39f6faa4aa18ee75a6c"))); QList md5sumsWrong1Offsets1 = (QList() << 0); const Checksums md5sumsWrong2("md5", PartialChecksums(md5Length, (QStringList() << "ce7d795bd0b1499f18d2ba8f338302d3" << "e6681cc0049c6cae347039578eaf1117" << "d66499f43f930fb013092a2d66ecdfaf" << "b2930c55fea65c11813dff09447fbe92" << "77b79bd53b62accec6367c7d7b2fc85b" << "d0363ab59bbfa39f6faa4aa18ee75a6c"))); QList md5sumsWrong1Offsets2 = (QList() << 2 * md5Length << 5 * md5Length); const KIO::filesize_t sha1Length = 1000 * 1024; const Checksums sha1sumsCorrect("sha1", PartialChecksums(sha1Length, (QStringList() << "5d76447e7655fd1d4dfba458c33340757d81eb95" << "0bc9428e3b39fc34ab457d58d62f1973a1183ac2" << "48a313a958ea1c55eb9527b0141ae30742fedfdb"))); const Checksums sha1sumsWrong1("sha1", PartialChecksums(sha1Length, (QStringList() << "5d76447e7655fd1d4dfba458c33340757d81eb95" << "abc9428e3b39fc34ab457d58d62f1973a1183ac2" << "48a313a958ea1c55eb9527b0141ae30742fedfdb"))); QList sha1sumsWrong1Offsets1 = (QList() << sha1Length); const Checksums sha1sumsWrong2("sha1", PartialChecksums(sha1Length, (QStringList() << "ad76447e7655fd1d4dfba458c33340757d81eb95" << "abc9428e3b39fc34ab457d58d62f1973a1183ac2" << "a8a313a958ea1c55eb9527b0141ae30742fedfdb"))); QList sha1sumsWrong1Offsets2 = (QList() << 0 << sha1Length << 2 * sha1Length); QTest::newRow("weak (md5), no broken pieces") << (QList() << md5sumsCorrect) << Verifier::Weak << emptyOffsets << md5Length; QTest::newRow("weak (md5), 1 broken piece (first)") << (QList() << md5sumsWrong1) << Verifier::Weak << md5sumsWrong1Offsets1 << md5Length; QTest::newRow("weak (md5), 2 broken piece (third, sixth)") << (QList() << md5sumsWrong2) << Verifier::Weak << md5sumsWrong1Offsets2 << md5Length; if (m_supported.contains("sha1")) { QTest::newRow("weak (md5, sha1), no broken pieces (sha1 wrong)") << (QList() << md5sumsCorrect << sha1sumsWrong1) << Verifier::Weak << emptyOffsets << md5Length; QTest::newRow("strong (md5, sha1), no broken pieces (md5 wrong)") << (QList() << md5sumsWrong1 << sha1sumsCorrect) << Verifier::Strong << emptyOffsets << sha1Length; QTest::newRow("strong (md5, sha1), 3 broken pieces (first, second, third)") << (QList() << md5sumsCorrect << sha1sumsWrong2) << Verifier::Strong << sha1sumsWrong1Offsets2 << sha1Length; QTest::newRow("strongest (md5, sha1), 1 broken piece (second)") << (QList() << md5sumsCorrect << sha1sumsWrong1) << Verifier::Strongest << sha1sumsWrong1Offsets1 << sha1Length; } } QTEST_MAIN(VerfierTest) diff --git a/tests/verifiertest.h b/tests/verifiertest.h index e296c4c3..388d5274 100644 --- a/tests/verifiertest.h +++ b/tests/verifiertest.h @@ -1,70 +1,70 @@ /*************************************************************************** * Copyright (C) 2011 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. * * * * 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 . * ***************************************************************************/ #ifndef KGET_VERIFIER_TEST_H #define KGET_VERIFIER_TEST_H #include -#include +#include #ifdef HAVE_QCA2 #include #endif -class KTempDir; +class QTemporaryDir; class VerfierTest : public QObject { Q_OBJECT public: explicit VerfierTest(QObject *parent = nullptr); private slots: void testChecksum(); void testChecksum_data(); void testPartialChecksums(); void testPartialChecksums_data(); void testIsChecksum(); void testIsChecksum_data(); void testAvailableChecksum(); void testAvailableChecksum_data(); void testAvailablePartialChecksum(); void testAvailablePartialChecksum_data(); void testVerify(); void testVerify_data(); void testBrokenPieces(); void testBrokenPieces_data(); private: /** * @returns (expected && Verifier::supportedVerficationTypes().contains(type)) */ bool expectedResult(bool expected, const QString &type); private: #ifdef HAVE_QCA2 QCA::Initializer m_qcaInit; #endif //HAVE_QCA2 - QScopedPointer m_tempDir; - KUrl m_file; + QScopedPointer m_tempDir; + QUrl m_file; const QStringList m_supported; }; #endif diff --git a/transfer-plugins/bittorrent/CMakeLists.txt b/transfer-plugins/bittorrent/CMakeLists.txt index 0a952497..71b4283a 100644 --- a/transfer-plugins/bittorrent/CMakeLists.txt +++ b/transfer-plugins/bittorrent/CMakeLists.txt @@ -1,67 +1,67 @@ #Set supported mime type SET(SUPPORTED_KGET_MIMETYPES "${SUPPORTED_KGET_MIMETYPES}application/x-bittorrent;" PARENT_SCOPE) kde_enable_exceptions() include_directories( ../../ ) set(kget_bittorrentfactory_PART_SRCS bttransfer.cpp bttransferfactory.cpp bttransferhandler.cpp btdetailswidget.cpp scandlg.cpp #btdatasource.cpp #btchunkselector.cpp #btcache.cpp advanceddetails/btadvanceddetailswidget.cpp advanceddetails/chunkdownloadmodel.cpp advanceddetails/chunkdownloadview.cpp advanceddetails/fileview.cpp advanceddetails/iwfilelistmodel.cpp advanceddetails/iwfiletreemodel.cpp advanceddetails/peerview.cpp advanceddetails/peerviewmodel.cpp advanceddetails/torrentfilelistmodel.cpp advanceddetails/torrentfilemodel.cpp advanceddetails/torrentfiletreemodel.cpp advanceddetails/monitor.cpp advanceddetails/trackerview.cpp advanceddetails/trackermodel.cpp advanceddetails/webseedsmodel.cpp advanceddetails/webseedstab.cpp ../../kget_debug.cpp ) ki18n_wrap_ui(kget_bittorrentfactory_PART_SRCS btdetailswidgetfrm.ui scandlg.ui advanceddetails/chunkdownloadview.ui advanceddetails/trackerview.ui advanceddetails/webseedstab.ui ) kconfig_add_kcfg_files(kget_bittorrentfactory_PART_SRCS bittorrentsettings.kcfgc) add_library(kget_bittorrent MODULE ${kget_bittorrentfactory_PART_SRCS}) kcoreaddons_desktop_to_json(kget_bittorrent kget_bittorrentfactory.desktop) target_link_libraries(kget_bittorrent KF5::KIOCore KF5::Torrent kgetcore) install(TARGETS kget_bittorrent DESTINATION ${KGET_PLUGIN_INSTALL_DIR}) ###Build KCM-Module set(kcm_kget_bittorrentfactory_PART_SRCS btsettingswidget.cpp ../../kget_debug.cpp ) ki18n_wrap_ui(kcm_kget_bittorrentfactory_PART_SRCS btsettingswidget.ui ) kconfig_add_kcfg_files(kcm_kget_bittorrentfactory_PART_SRCS bittorrentsettings.kcfgc) add_library(kcm_kget_bittorrentfactory MODULE ${kcm_kget_bittorrentfactory_PART_SRCS}) -target_link_libraries(kcm_kget_bittorrentfactory KF5::ConfigGui KF5::ConfigWidgets KF5::KDELibs4Support KF5::I18n KF5::KIOCore KF5::KIOWidgets) +target_link_libraries(kcm_kget_bittorrentfactory KF5::ConfigGui KF5::ConfigWidgets KF5::I18n KF5::KIOCore KF5::KIOWidgets) install(TARGETS kcm_kget_bittorrentfactory DESTINATION ${PLUGIN_INSTALL_DIR}) install(FILES kget_bittorrentfactory_config.desktop DESTINATION ${SERVICES_INSTALL_DIR}) diff --git a/transfer-plugins/bittorrent/advanceddetails/btadvanceddetailswidget.cpp b/transfer-plugins/bittorrent/advanceddetails/btadvanceddetailswidget.cpp index f2948a80..c9b5ee32 100644 --- a/transfer-plugins/bittorrent/advanceddetails/btadvanceddetailswidget.cpp +++ b/transfer-plugins/bittorrent/advanceddetails/btadvanceddetailswidget.cpp @@ -1,120 +1,119 @@ /* This file is part of the KDE project Copyright (C) 2007 Lukas Appelhans Copyright (C) 2007 Joris Guisson 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 "btadvanceddetailswidget.h" #include #include #include "bttransferhandler.h" #include "bittorrentsettings.h" #include "fileview.h" #include "chunkdownloadview.h" #include "peerview.h" #include "monitor.h" #include "trackerview.h" #include "webseedstab.h" #include "kget_debug.h" #include -#include +#include #include -#include -#include -#include #include +#include +#include using namespace kt; BTAdvancedDetailsWidget::BTAdvancedDetailsWidget(BTTransferHandler * transfer) : m_transfer(transfer) { tc = m_transfer->torrentControl(); init(); //This updates the widget with the right values slotTransferChanged(transfer, 0xFFFFFFFF); connect(m_transfer, SIGNAL(transferChangedEvent(TransferHandler*,TransferHandler::ChangesFlags)), this, SLOT(slotTransferChanged(TransferHandler*,TransferHandler::ChangesFlags))); } void BTAdvancedDetailsWidget::init() { setWindowTitle(i18n("Advanced Details for %1", m_transfer->source().fileName())); resize(500, 400); QGridLayout *layout = new QGridLayout(); KTitleWidget *titleWidget = new KTitleWidget(this); titleWidget->setText(i18n("Advanced Details for %1", m_transfer->source().fileName())); titleWidget->setPixmap(QIcon::fromTheme("dialog-information")); layout->addWidget(titleWidget); tabWidget = new QTabWidget(this); layout->addWidget(tabWidget); setLayout(layout); file_view = new FileView(this); file_view->changeTC(tc, KSharedConfig::openConfig()); tabWidget->insertTab(0, file_view, QIcon::fromTheme("inode-directory"), i18n("Files")); //peer_view = new PeerView(this); //tabWidget->insertTab(1, peer_view, QIcon::fromTheme("system-users"), i18n("Peers")); //cd_view = new ChunkDownloadView(this); //cd_view->changeTC(tc); //tabWidget->insertTab(2, cd_view, QIcon::fromTheme("preferences-plugin"), i18n("Chunks")); tracker_view = new TrackerView(this); tracker_view->changeTC(tc); tabWidget->insertTab(1, tracker_view, QIcon::fromTheme("network-server"), i18n("Trackers")); webseeds_tab = new WebSeedsTab(this); webseeds_tab->changeTC(tc); tabWidget->insertTab(2, webseeds_tab, QIcon::fromTheme("network-server"), i18n("Webseeds")); monitor = new Monitor(tc, nullptr, nullptr, file_view); } void BTAdvancedDetailsWidget::hideEvent(QHideEvent * event) { Q_UNUSED(event) if (tc) tc->setMonitor(0); emit aboutToClose(); deleteLater(); } kt::Monitor* BTAdvancedDetailsWidget::torrentMonitor() const { return monitor; } void BTAdvancedDetailsWidget::slotTransferChanged(TransferHandler * transfer, TransferHandler::ChangesFlags flags) { qCDebug(KGET_DEBUG) << "BTAdvancedDetailsWidget::slotTransferChanged" ; Q_UNUSED(transfer) if (flags & BTTransfer::Tc_ChunksTotal || flags & BTTransfer::Tc_ChunksDownloaded || flags & BTTransfer::Tc_ChunksExcluded || flags & BTTransfer::Tc_ChunksLeft || flags & Transfer::Tc_DownloadSpeed || flags & Transfer::Tc_UploadSpeed) { //if (tabWidget->currentIndex() == 1) // peer_view->update(); //else if (tabWidget->currentIndex() == 2) // cd_view->update(); /*else */if (tabWidget->currentIndex() == 1) tracker_view->update(); } /*else if (m_transfer->status() == Job::Stopped) { peer_view->removeAll(); //cd_view->removeAll(); }*/ } diff --git a/transfer-plugins/bittorrent/advanceddetails/trackerview.cpp b/transfer-plugins/bittorrent/advanceddetails/trackerview.cpp index 733eef4e..bc3a3c82 100644 --- a/transfer-plugins/bittorrent/advanceddetails/trackerview.cpp +++ b/transfer-plugins/bittorrent/advanceddetails/trackerview.cpp @@ -1,233 +1,232 @@ /*************************************************************************** * Copyright (C) 2006-2007 by Joris Guisson, Ivan Vasic * * joris.guisson@gmail.com * * ivasic@gmail.com * * * * 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. * * * * 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 "trackerview.h" +#include #include #include #include -#include #include #include -#include +#include #include #include #include #include #include -#include #include "trackermodel.h" using namespace bt; namespace kt { TrackerView::TrackerView(QWidget *parent) : QWidget(parent), tc(nullptr) { setupUi(this); model = new TrackerModel(this); proxy_model = new QSortFilterProxyModel(this); proxy_model->setSortRole(Qt::UserRole); proxy_model->setSourceModel(model); m_tracker_list->setModel(proxy_model); m_tracker_list->setAllColumnsShowFocus(true); m_tracker_list->setRootIsDecorated(false); m_tracker_list->setAlternatingRowColors(true); m_tracker_list->setSortingEnabled(true); connect(m_add_tracker,SIGNAL(clicked()),this,SLOT(addClicked())); connect(m_remove_tracker,SIGNAL(clicked()),this,SLOT(removeClicked())); connect(m_change_tracker,SIGNAL(clicked()),this,SLOT(changeClicked())); connect(m_restore_defaults,SIGNAL(clicked()),this,SLOT(restoreClicked())); connect(m_tracker_list->selectionModel(),SIGNAL(currentChanged(QModelIndex,QModelIndex)), this,SLOT(currentChanged(QModelIndex,QModelIndex))); connect(m_scrape,SIGNAL(clicked()),this,SLOT(scrapeClicked())); m_add_tracker->setIcon(QIcon::fromTheme("list-add")); m_remove_tracker->setIcon(QIcon::fromTheme("list-remove")); m_restore_defaults->setIcon(QIcon::fromTheme("kt-restore-defaults")); m_change_tracker->setIcon(QIcon::fromTheme("kt-change-tracker")); setEnabled(false); torrentChanged(nullptr); } TrackerView::~TrackerView() { } void TrackerView::addClicked() { if (!tc) return; bool ok = false; QClipboard* clipboard = QApplication::clipboard(); QString text = QInputDialog::getText( this, i18n("Add Tracker"), i18n("Enter the URL of the tracker:"), QLineEdit::Normal, clipboard->text(), &ok); if (!ok) return; - KUrl url(text); + QUrl url(text); if (!url.isValid()) { KMessageBox::error(nullptr, i18n("Malformed URL.")); return; } // check for dupes if (!tc->getTrackersList()->addTracker(url,true)) { KMessageBox::sorry(nullptr,i18n("There already is a tracker named %1.",text)); } else { model->insertRow(model->rowCount(QModelIndex())); } } void TrackerView::removeClicked() { QModelIndex current = proxy_model->mapToSource(m_tracker_list->selectionModel()->currentIndex()); if (!current.isValid()) return; model->removeRow(current.row()); } void TrackerView::changeClicked() { QModelIndex current = m_tracker_list->selectionModel()->currentIndex(); if (!current.isValid()) return; bt::TrackersList* tlist = tc->getTrackersList(); bt::TrackerInterface* trk = model->tracker(proxy_model->mapToSource(current)); if (trk && trk->isEnabled()) tlist->setCurrentTracker(trk); } void TrackerView::restoreClicked() { tc->getTrackersList()->restoreDefault(); tc->updateTracker(); model->changeTC(tc); // trigger reset } void TrackerView::updateClicked() { if(!tc) return; tc->updateTracker(); } void TrackerView::scrapeClicked() { if(!tc) return; tc->scrapeTracker(); } void TrackerView::changeTC(TorrentInterface* ti) { if (tc == ti) return; setEnabled(ti != nullptr); torrentChanged(ti); update(); } void TrackerView::update() { if (tc) model->update(); } void TrackerView::torrentChanged(TorrentInterface* ti) { tc = ti; if(!tc) { m_add_tracker->setEnabled(false); m_remove_tracker->setEnabled(false); m_restore_defaults->setEnabled(false); m_change_tracker->setEnabled(false); m_scrape->setEnabled(false); model->changeTC(nullptr); } else { m_add_tracker->setEnabled(true); m_remove_tracker->setEnabled(true); m_restore_defaults->setEnabled(true); m_scrape->setEnabled(true); model->changeTC(tc); currentChanged(m_tracker_list->selectionModel()->currentIndex(),QModelIndex()); } } void TrackerView::currentChanged(const QModelIndex & current,const QModelIndex & previous) { Q_UNUSED(previous) if (!tc) { m_change_tracker->setEnabled(false); m_remove_tracker->setEnabled(false); return; } const TorrentStats & s = tc->getStats(); bt::TrackerInterface* trk = model->tracker(proxy_model->mapToSource(current)); bool enabled = trk ? trk->isEnabled() : false; m_change_tracker->setEnabled(s.running && model->rowCount(QModelIndex()) > 1 && enabled); m_remove_tracker->setEnabled(trk && tc->getTrackersList()->canRemoveTracker(trk)); } void TrackerView::saveState(KSharedConfigPtr cfg) { KConfigGroup g = cfg->group("TrackerView"); QByteArray s = m_tracker_list->header()->saveState(); g.writeEntry("state",s.toBase64()); } void TrackerView::loadState(KSharedConfigPtr cfg) { KConfigGroup g = cfg->group("TrackerView"); QByteArray s = QByteArray::fromBase64(g.readEntry("state",QByteArray())); if (!s.isNull()) { QHeaderView* v = m_tracker_list->header(); v->restoreState(s); } } } diff --git a/transfer-plugins/checksumsearch/checksumsearchfactory.cpp b/transfer-plugins/checksumsearch/checksumsearchfactory.cpp index 7c853b5e..5ac017e3 100644 --- a/transfer-plugins/checksumsearch/checksumsearchfactory.cpp +++ b/transfer-plugins/checksumsearch/checksumsearchfactory.cpp @@ -1,44 +1,43 @@ /* This file is part of the KDE project Copyright (C) 2008 Manolo Valdes 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 "checksumsearchfactory.h" #include "checksumsearchtransferdatasource.h" #include "core/scheduler.h" #include "core/transfergroup.h" #include "kget_debug.h" +#include #include -#include - K_PLUGIN_CLASS_WITH_JSON(ChecksumSearchFactory, "kget_checksumsearchfactory.json") ChecksumSearchFactory::ChecksumSearchFactory(QObject *parent, const QVariantList &args) : TransferFactory(parent, args) { } ChecksumSearchFactory::~ChecksumSearchFactory() { } TransferDataSource *ChecksumSearchFactory::createTransferDataSource(const QUrl &srcUrl, const QDomElement &type, QObject *parent) { qCDebug(KGET_DEBUG); if (type.attribute("type") == "checksumsearch") { return new ChecksumSearchTransferDataSource(srcUrl, parent); } return nullptr; } #include "checksumsearchfactory.moc" diff --git a/transfer-plugins/kio/transferKio.cpp b/transfer-plugins/kio/transferKio.cpp index cdcf60fa..be0039e3 100644 --- a/transfer-plugins/kio/transferKio.cpp +++ b/transfer-plugins/kio/transferKio.cpp @@ -1,355 +1,355 @@ /* This file is part of the KDE project Copyright (C) 2004 Dario Massarin 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 "transferKio.h" #include "core/verifier.h" #include "core/signature.h" #include "settings.h" #include #include "kget_debug.h" -#include +#include -#include -#include +#include +#include #include #include #include #include #include #include TransferKio::TransferKio(TransferGroup * parent, TransferFactory * factory, Scheduler * scheduler, const QUrl & source, const QUrl & dest, const QDomElement * e) : Transfer(parent, factory, scheduler, source, dest, e), m_copyjob(nullptr), m_movingFile(false), m_verifier(nullptr), m_signature(nullptr) { setCapabilities(Transfer::Cap_Moving | Transfer::Cap_Renaming | Transfer::Cap_Resuming);//TODO check if it really can resume } bool TransferKio::setDirectory(const QUrl& newDirectory) { QUrl newDest = newDirectory; newDest.setPath(newDest.adjusted(QUrl::RemoveFilename).toString() + m_dest.fileName()); return setNewDestination(newDest); } bool TransferKio::setNewDestination(const QUrl &newDestination) { if (newDestination.isValid() && (newDestination != dest())) { QUrl oldPath = QUrl(m_dest.path() + ".part"); if (oldPath.isValid() && QFile::exists(oldPath.toString())) { m_movingFile = true; stop(); setStatus(Job::Moving); setTransferChange(Tc_Status, true); m_dest = newDestination; if (m_verifier) { m_verifier->setDestination(newDestination); } if (m_signature) { m_signature->setDestination(newDestination); } KIO::Job *move = KIO::file_move(oldPath, QUrl(newDestination.path() + ".part"), -1, KIO::HideProgressInfo); connect(move, SIGNAL(result(KJob*)), this, SLOT(newDestResult(KJob*))); connect(move, SIGNAL(infoMessage(KJob*,QString)), this, SLOT(slotInfoMessage(KJob*,QString))); connect(move, SIGNAL(percent(KJob*,ulong)), this, SLOT(slotPercent(KJob*,ulong))); return true; } } return false; } void TransferKio::newDestResult(KJob *result) { Q_UNUSED(result)//TODO handle errors etc.! m_movingFile = false; start(); setTransferChange(Tc_FileName); } void TransferKio::start() { if (!m_movingFile && (status() != Finished)) { m_stopped = false; if(!m_copyjob) createJob(); qCDebug(KGET_DEBUG) << "TransferKio::start"; setStatus(Job::Running, i18nc("transfer state: connecting", "Connecting...."), SmallIcon("network-connect")); // should be "network-connecting", but that doesn't exist for KDE 4.0 yet setTransferChange(Tc_Status, true); } } void TransferKio::stop() { if ((status() == Stopped) || (status() == Finished)) { return; } m_stopped = true; if(m_copyjob) { m_copyjob->kill(KJob::EmitResult); m_copyjob=nullptr; } qCDebug(KGET_DEBUG) << "Stop"; setStatus(Job::Stopped); m_downloadSpeed = 0; setTransferChange(Tc_Status | Tc_DownloadSpeed, true); } void TransferKio::deinit(Transfer::DeleteOptions options) { if (options & DeleteFiles)//if the transfer is not finished, we delete the *.part-file { KIO::Job *del = KIO::del(QString(m_dest.path() + ".part"), KIO::HideProgressInfo); if (!del->exec()) { qCDebug(KGET_DEBUG) << "Could not delete part " << QString(m_dest.path() + ".part"); } }//TODO: Ask the user if he/she wants to delete the *.part-file? To discuss (boom1992) } //NOTE: INTERNAL METHODS void TransferKio::createJob() { if(!m_copyjob) { KIO::Scheduler::checkSlaveOnHold(true); m_copyjob = KIO::file_copy(m_source, m_dest, -1, KIO::HideProgressInfo); connect(m_copyjob, SIGNAL(result(KJob*)), this, SLOT(slotResult(KJob*))); connect(m_copyjob, SIGNAL(infoMessage(KJob*,QString)), this, SLOT(slotInfoMessage(KJob*,QString))); connect(m_copyjob, SIGNAL(percent(KJob*,ulong)), this, SLOT(slotPercent(KJob*,ulong))); connect(m_copyjob, SIGNAL(totalSize(KJob*,qulonglong)), this, SLOT(slotTotalSize(KJob*,qulonglong))); connect(m_copyjob, SIGNAL(processedSize(KJob*,qulonglong)), this, SLOT(slotProcessedSize(KJob*,qulonglong))); connect(m_copyjob, SIGNAL(speed(KJob*,ulong)), this, SLOT(slotSpeed(KJob*,ulong))); } } void TransferKio::slotResult( KJob * kioJob ) { qCDebug(KGET_DEBUG) << "slotResult (" << kioJob->error() << ")"; switch (kioJob->error()) { case 0: //The download has finished case KIO::ERR_FILE_ALREADY_EXIST: //The file has already been downloaded. setStatus(Job::Finished); // "ok" icon should probably be "dialog-success", but we don't have that icon in KDE 4.0 m_percent = 100; m_downloadSpeed = 0; m_downloadedSize = m_totalSize; setTransferChange(Tc_Percent | Tc_DownloadSpeed); break; default: //There has been an error qCDebug(KGET_DEBUG) << "-- E R R O R (" << kioJob->error() << ")--"; if (!m_stopped) setStatus(Job::Aborted); break; } // when slotResult gets called, the m_copyjob has already been deleted! m_copyjob=nullptr; // If it is an ftp file, there's still work to do Transfer::ChangesFlags flags = (m_source.scheme() != "ftp") ? Tc_Status : Tc_None; if (status() == Job::Finished) { if (!m_totalSize) { //downloaded elsewhere already, e.g. Konqueror if (!m_downloadedSize) { QFile file(m_dest.toLocalFile() + ".part"); m_downloadedSize = file.size(); if (!m_downloadedSize) { QFile file(m_dest.toLocalFile()); m_downloadedSize = file.size(); } } m_totalSize = m_downloadedSize; flags |= Tc_DownloadedSize; } if (m_verifier && Settings::checksumAutomaticVerification()) { m_verifier->verify(); } if (m_signature && Settings::signatureAutomaticVerification()) { m_signature->verify(); } } if (m_source.scheme() == "ftp") { KIO::StatJob * statJob = KIO::stat(m_source); connect(statJob, SIGNAL(result(KJob*)), this, SLOT(slotStatResult(KJob*))); statJob->start(); } setTransferChange(flags, true); } void TransferKio::slotInfoMessage( KJob * kioJob, const QString & msg ) { Q_UNUSED(kioJob) m_log.append(QString(msg)); } void TransferKio::slotPercent( KJob * kioJob, unsigned long percent ) { qCDebug(KGET_DEBUG) << "slotPercent"; Q_UNUSED(kioJob) m_percent = percent; setTransferChange(Tc_Percent, true); } void TransferKio::slotTotalSize( KJob * kioJob, qulonglong size ) { Q_UNUSED(kioJob) qCDebug(KGET_DEBUG) << "slotTotalSize"; setStatus(Job::Running); m_totalSize = size; setTransferChange(Tc_Status | Tc_TotalSize, true); } void TransferKio::slotProcessedSize( KJob * kioJob, qulonglong size ) { Q_UNUSED(kioJob) // qCDebug(KGET_DEBUG) << "slotProcessedSize"; if(status() != Job::Running) { setStatus(Job::Running); setTransferChange(Tc_Status); } m_downloadedSize = size; setTransferChange(Tc_DownloadedSize, true); } void TransferKio::slotSpeed( KJob * kioJob, unsigned long bytes_per_second ) { Q_UNUSED(kioJob) // qCDebug(KGET_DEBUG) << "slotSpeed"; if(status() != Job::Running) { if (m_movingFile) setStatus(Job::Moving); else setStatus(Job::Running); setTransferChange(Tc_Status); } m_downloadSpeed = bytes_per_second; setTransferChange(Tc_DownloadSpeed, true); } void TransferKio::slotVerified(bool isVerified) { if (!isVerified) { QString text = i18n("The download (%1) could not be verified. Do you want to repair it?", m_dest.fileName()); if (!verifier()->partialChunkLength()) { text = i18n("The download (%1) could not be verified. Do you want to redownload it?", m_dest.fileName()); } if (KMessageBox::warningYesNo(nullptr, text, i18n("Verification failed.")) == KMessageBox::Yes) { repair(); } } } void TransferKio::slotStatResult(KJob* kioJob) { KIO::StatJob * statJob = qobject_cast(kioJob); if (!statJob->error()) { const KIO::UDSEntry entryResult = statJob->statResult(); struct utimbuf time; time.modtime = entryResult.numberValue(KIO::UDSEntry::UDS_MODIFICATION_TIME); time.actime = QDateTime::currentDateTime().toTime_t(); utime(m_dest.toLocalFile().toUtf8().constData(), &time); } setStatus(Job::Finished); setTransferChange(Tc_Status, true); } bool TransferKio::repair(const QUrl &file) { Q_UNUSED(file) if (verifier()->status() == Verifier::NotVerified) { m_downloadedSize = 0; m_percent = 0; if(m_copyjob) { m_copyjob->kill(KJob::Quietly); m_copyjob = nullptr; } setTransferChange(Tc_DownloadedSize | Tc_Percent, true); start(); return true; } return false; } Verifier *TransferKio::verifier(const QUrl &file) { Q_UNUSED(file) if (!m_verifier) { m_verifier = new Verifier(m_dest, this); connect(m_verifier, SIGNAL(verified(bool)), this, SLOT(slotVerified(bool))); } return m_verifier; } Signature *TransferKio::signature(const QUrl &file) { Q_UNUSED(file) if (!m_signature) { m_signature = new Signature(m_dest, this); } return m_signature; } diff --git a/transfer-plugins/kio/transferKioFactory.cpp b/transfer-plugins/kio/transferKioFactory.cpp index 01b4f6fb..0e5ada7e 100644 --- a/transfer-plugins/kio/transferKioFactory.cpp +++ b/transfer-plugins/kio/transferKioFactory.cpp @@ -1,61 +1,61 @@ /* This file is part of the KDE project Copyright (C) 2004 Dario Massarin 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 "transferKioFactory.h" #include "core/scheduler.h" #include "core/transfergroup.h" #include "transferKio.h" #include "kget_debug.h" -#include -#include +#include +#include K_PLUGIN_CLASS_WITH_JSON(TransferKioFactory, "kget_kiofactory.json") TransferKioFactory::TransferKioFactory(QObject *parent, const QVariantList &args) : TransferFactory(parent, args) { } TransferKioFactory::~TransferKioFactory() { } Transfer * TransferKioFactory::createTransfer( const QUrl &srcUrl, const QUrl &destUrl, TransferGroup * parent, Scheduler * scheduler, const QDomElement * e ) { qCDebug(KGET_DEBUG) << "TransferKioFactory::createTransfer"; qWarning(KGET_DEBUG) << "KIOFACTORY createTRANSFER"; if (isSupported(srcUrl)) { return new TransferKio(parent, this, scheduler, srcUrl, destUrl, e); } return 0; } bool TransferKioFactory::isSupported(const QUrl &url) const { QString prot = url.scheme(); qDebug() << "Protocol = " << prot; return addsProtocols().contains(prot); } QStringList TransferKioFactory::addsProtocols() const { static const QStringList protocols = QStringList() << "http" << "https" << "ftp" << "sftp"; return protocols; } #include "transferKioFactory.moc" diff --git a/transfer-plugins/metalink/CMakeLists.txt b/transfer-plugins/metalink/CMakeLists.txt index 01847389..3098d72d 100644 --- a/transfer-plugins/metalink/CMakeLists.txt +++ b/transfer-plugins/metalink/CMakeLists.txt @@ -1,45 +1,45 @@ #Set supported mime type SET(SUPPORTED_KGET_MIMETYPES "${SUPPORTED_KGET_MIMETYPES}application/metalink+xml;" PARENT_SCOPE) include_directories( ../../ ) set(kget_metalinkfactory_PART_SRCS metalinkfactory.cpp abstractmetalink.cpp metalinkxml.cpp metalinkhttp.cpp fileselectiondlg.cpp ../../ui/metalinkcreator/metalinker.cpp ../../kget_debug.cpp ) ki18n_wrap_ui(kget_metalinkfactory_PART_SRCS fileselection.ui ) kconfig_add_kcfg_files(kget_metalinkfactory_PART_SRCS metalinksettings.kcfgc) add_library(kget_metalinkfactory MODULE ${kget_metalinkfactory_PART_SRCS}) kcoreaddons_desktop_to_json(kget_metalinkfactory kget_metalinkfactory.desktop) target_link_libraries(kget_metalinkfactory kgetcore) install(TARGETS kget_metalinkfactory DESTINATION ${KGET_PLUGIN_INSTALL_DIR}) ###Build KCM-Module set(kcm_kget_metalinkfactory_PART_SRCS dlgmetalink.cpp ../../kget_debug.cpp ) ki18n_wrap_ui(kcm_kget_metalinkfactory_PART_SRCS dlgmetalink.ui ) kconfig_add_kcfg_files(kcm_kget_metalinkfactory_PART_SRCS metalinksettings.kcfgc) add_library(kcm_kget_metalinkfactory MODULE ${kcm_kget_metalinkfactory_PART_SRCS}) -target_link_libraries(kcm_kget_metalinkfactory KF5::I18n KF5::ConfigGui KF5::KDELibs4Support) +target_link_libraries(kcm_kget_metalinkfactory KF5::I18n KF5::ConfigGui KF5::ConfigWidgets) install(TARGETS kcm_kget_metalinkfactory DESTINATION ${PLUGIN_INSTALL_DIR}) install(FILES kget_metalinkfactory_config.desktop DESTINATION ${SERVICES_INSTALL_DIR}) diff --git a/transfer-plugins/metalink/metalinkfactory.cpp b/transfer-plugins/metalink/metalinkfactory.cpp index 11a9f120..4cbed24e 100644 --- a/transfer-plugins/metalink/metalinkfactory.cpp +++ b/transfer-plugins/metalink/metalinkfactory.cpp @@ -1,61 +1,61 @@ /* This file is part of the KDE project Copyright (C) 2004 Dario Massarin Copyright (C) 2007 Manolo Valdes Copyright (C) 2012 Aish Raj Dahal 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 "metalinkfactory.h" #include "core/scheduler.h" #include "core/transfergroup.h" #include "metalinkhttp.h" #include "metalinkxml.h" #include "kget_debug.h" -#include +#include K_PLUGIN_CLASS_WITH_JSON(MetalinkFactory, "kget_metalinkfactory.json") MetalinkFactory::MetalinkFactory(QObject *parent, const QVariantList &args) : TransferFactory(parent, args) { } MetalinkFactory::~MetalinkFactory() { } Transfer * MetalinkFactory::createTransfer( const QUrl &srcUrl, const QUrl &destUrl, TransferGroup * parent, Scheduler * scheduler, const QDomElement * e ) { qCDebug(KGET_DEBUG) << "metalinkFactory::createTransfer"; KGetMetalink::MetalinkHttpParser *metalinkHttpChecker = new KGetMetalink::MetalinkHttpParser(srcUrl); if (metalinkHttpChecker->isMetalinkHttp()) { qCDebug(KGET_DEBUG) << "Create MetalinkHTTP"; return new MetalinkHttp(parent, this, scheduler, srcUrl, destUrl, metalinkHttpChecker, e); } // No one takes ownership of this checker metalinkHttpChecker->deleteLater(); if (isSupported(srcUrl)) { return new MetalinkXml(parent, this, scheduler, srcUrl, destUrl, e); } return nullptr; } bool MetalinkFactory::isSupported(const QUrl &url) const { return (url.fileName().endsWith(QLatin1String(".metalink")) || url.fileName().endsWith(QLatin1String(".meta4"))); } #include "metalinkfactory.moc" diff --git a/transfer-plugins/metalink/metalinkxml.cpp b/transfer-plugins/metalink/metalinkxml.cpp index d2043c53..2dda1591 100644 --- a/transfer-plugins/metalink/metalinkxml.cpp +++ b/transfer-plugins/metalink/metalinkxml.cpp @@ -1,331 +1,330 @@ /* This file is part of the KDE project Copyright (C) 2004 Dario Massarin Copyright (C) 2007 Manolo Valdes Copyright (C) 2009 Matthias Fuchs Copyright (C) 2012 Aish Raj Dahal 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 "metalinkxml.h" #include "fileselectiondlg.h" #include "metalinksettings.h" #include "core/kget.h" #include "core/transfergroup.h" #include "core/download.h" #include "core/transferdatasource.h" #include "core/filemodel.h" #include "core/urlchecker.h" #include "core/verifier.h" #include "core/signature.h" #include "kget_debug.h" +#include #include #include #include #include #include -#include - -#include +#include #include +#include #include #include -#include MetalinkXml::MetalinkXml(TransferGroup * parent, TransferFactory * factory, Scheduler * scheduler, const QUrl & source, const QUrl & dest, const QDomElement * e) : AbstractMetalink(parent, factory, scheduler, source, dest, e) { } MetalinkXml::~MetalinkXml() { } void MetalinkXml::start() { qCDebug(KGET_DEBUG) << "metalinkxml::start"; if (!m_ready) { if (m_localMetalinkLocation.isValid() && metalinkInit()) { startMetalink(); } else { downloadMetalink(); } } else { startMetalink(); } } void MetalinkXml::downloadMetalink() { m_metalinkJustDownloaded = true; setStatus(Job::Running, i18n("Downloading Metalink File...."), SmallIcon("document-save")); setTransferChange(Tc_Status, true); // make sure that the DataLocation directory exists (earlier this used to be handled by KStandardDirs) if (!QFileInfo::exists(QStandardPaths::writableLocation(QStandardPaths::DataLocation))) { QDir().mkpath(QStandardPaths::writableLocation(QStandardPaths::DataLocation)); } Download *download = new Download(m_source, QStandardPaths::writableLocation(QStandardPaths::DataLocation) + QStringLiteral("/metalinks/") + m_source.fileName()); connect(download, SIGNAL(finishedSuccessfully(QUrl,QByteArray)), SLOT(metalinkInit(QUrl,QByteArray))); } bool MetalinkXml::metalinkInit(const QUrl &src, const QByteArray &data) { qCDebug(KGET_DEBUG) << "MetalinkXml::metalinkInit"; if (!src.isEmpty()) { m_localMetalinkLocation = src; } //use the downloaded metalink-file data directly if possible if (!data.isEmpty()) { KGetMetalink::HandleMetalink::load(data, &m_metalink); } //try to parse the locally stored metalink-file if (!m_metalink.isValid() && m_localMetalinkLocation.isValid()) { KGetMetalink::HandleMetalink::load(m_localMetalinkLocation.toLocalFile(), &m_metalink); } if (!m_metalink.isValid()) { qCCritical(KGET_DEBUG) << "Unknown error when trying to load the .metalink-file. Metalink is not valid."; setStatus(Job::Aborted); setTransferChange(Tc_Status, true); return false; } //offers a dialog to download the newest version of a dynamic metalink if ((m_source.isLocalFile() || !m_metalinkJustDownloaded) && m_metalink.dynamic && (UrlChecker::checkSource(m_metalink.origin) == UrlChecker::NoError)) { if (KMessageBox::questionYesNo(nullptr, i18n("A newer version of this Metalink might exist, do you want to download it?"), i18n("Redownload Metalink")) == KMessageBox::Yes) { m_localMetalinkLocation.clear(); m_source = m_metalink.origin; downloadMetalink(); return false; } } QList::const_iterator it; QList::const_iterator itEnd = m_metalink.files.files.constEnd(); m_totalSize = 0; KIO::fileoffset_t segSize = 500 * 1024;//TODO use config here! const QUrl tempDest = QUrl(m_dest.adjusted(QUrl::RemoveFilename)); QUrl dest; for (it = m_metalink.files.files.constBegin(); it != itEnd ; ++it) { dest = tempDest; dest.setPath(tempDest.path() + (*it).name); QList urlList = (*it).resources.urls; //sort the urls according to their priority (highest first) qSort(urlList.begin(), urlList.end(), qGreater()); KIO::filesize_t fileSize = (*it).size; m_totalSize += fileSize; //create a DataSourceFactory for each separate file DataSourceFactory *dataFactory = new DataSourceFactory(this, dest, fileSize, segSize); dataFactory->setMaxMirrorsUsed(MetalinkSettings::mirrorsPerFile()); //TODO compare available file size () with the sizes of the server while downloading? connect(dataFactory, SIGNAL(capabilitiesChanged()), this, SLOT(slotUpdateCapabilities())); connect(dataFactory, SIGNAL(dataSourceFactoryChange(Transfer::ChangesFlags)), this, SLOT(slotDataSourceFactoryChange(Transfer::ChangesFlags))); connect(dataFactory->verifier(), SIGNAL(verified(bool)), this, SLOT(slotVerified(bool))); connect(dataFactory->signature(), SIGNAL(verified(int)), this, SLOT(slotSignatureVerified())); connect(dataFactory, SIGNAL(log(QString,Transfer::LogLevel)), this, SLOT(setLog(QString,Transfer::LogLevel))); //add the DataSources for (int i = 0; i < urlList.size(); ++i) { const QUrl url = urlList[i].url; if (url.isValid()) { dataFactory->addMirror(url, MetalinkSettings::connectionsPerUrl()); } } //no datasource has been created, so remove the datasource factory if (dataFactory->mirrors().isEmpty()) { delete dataFactory; } else { dataFactory->verifier()->addChecksums((*it).verification.hashes); foreach (const KGetMetalink::Pieces &pieces, (*it).verification.pieces) { dataFactory->verifier()->addPartialChecksums(pieces.type, pieces.length, pieces.hashes); } const QHash signatures = (*it).verification.signatures; QHash::const_iterator it; QHash::const_iterator itEnd = signatures.constEnd(); for (it = signatures.constBegin(); it != itEnd; ++it) { if (it.key().toLower() == "pgp") { dataFactory->signature()->setAsciiDetatchedSignature(*it); } } m_dataSourceFactory[dataFactory->dest()] = dataFactory; } } if ((m_metalink.files.files.size() == 1) && m_dataSourceFactory.size()) { m_dest = dest; } if (!m_dataSourceFactory.size()) { //TODO make this via log in the future + do not display the KMessageBox qCWarning(KGET_DEBUG) << "Download of" << m_source << "failed, no working URLs were found."; KMessageBox::error(nullptr, i18n("Download failed, no working URLs were found."), i18n("Error")); setStatus(Job::Aborted); setTransferChange(Tc_Status, true); return false; } m_ready = !m_dataSourceFactory.isEmpty(); slotUpdateCapabilities(); //the metalink-file has just been downloaded, so ask the user to choose the files that // should be downloaded /* TODO this portion seems not to be working. Need to ask boom1992 */ if (m_metalinkJustDownloaded) { QDialog *dialog = new FileSelectionDlg(fileModel()); dialog->setAttribute(Qt::WA_DeleteOnClose); connect(dialog, SIGNAL(finished(int)), this, SLOT(fileDlgFinished(int))); dialog->show(); } return true; } void MetalinkXml::startMetalink() { if (m_ready) { foreach (DataSourceFactory *factory, m_dataSourceFactory) { //specified number of files is downloaded simultanously if (m_currentFiles < MetalinkSettings::simultanousFiles()) { const int status = factory->status(); //only start factories that should be downloaded if (factory->doDownload() && (status != Job::Finished) && (status != Job::FinishedKeepAlive) && (status != Job::Running)) { ++m_currentFiles; factory->start(); } } else { break; } } } } void MetalinkXml::deinit(Transfer::DeleteOptions options) { foreach (DataSourceFactory *factory, m_dataSourceFactory) { if (options & Transfer::DeleteFiles) { factory->deinit(); } }//TODO: Ask the user if he/she wants to delete the *.part-file? To discuss (boom1992) //FIXME does that mean, that the metalink file is always removed, even if //downloaded by the user? if ((options & Transfer::DeleteTemporaryFiles) && m_localMetalinkLocation.isLocalFile()) { KIO::Job *del = KIO::del(m_localMetalinkLocation, KIO::HideProgressInfo); if (!del->exec()) { qCDebug(KGET_DEBUG) << "Could not delete " << m_localMetalinkLocation.path(); } } } void MetalinkXml::load(const QDomElement *element) { Transfer::load(element); if (!element) { return; } const QDomElement e = *element; m_localMetalinkLocation = QUrl(e.attribute("LocalMetalinkLocation")); QDomNodeList factories = e.firstChildElement("factories").elementsByTagName("factory"); //no stored information found, stop right here if (!factories.count()) { return; } while (factories.count()) { QDomDocument doc; QDomElement factory = doc.createElement("factories"); factory.appendChild(factories.item(0).toElement()); doc.appendChild(factory); DataSourceFactory *file = new DataSourceFactory(this); file->load(&factory); connect(file, SIGNAL(capabilitiesChanged()), this, SLOT(slotUpdateCapabilities())); connect(file, SIGNAL(dataSourceFactoryChange(Transfer::ChangesFlags)), this, SLOT(slotDataSourceFactoryChange(Transfer::ChangesFlags))); m_dataSourceFactory[file->dest()] = file; connect(file->verifier(), SIGNAL(verified(bool)), this, SLOT(slotVerified(bool))); connect(file->signature(), SIGNAL(verified(int)), this, SLOT(slotSignatureVerified())); connect(file, SIGNAL(log(QString,Transfer::LogLevel)), this, SLOT(setLog(QString,Transfer::LogLevel))); //start the DataSourceFactories that were Started when KGet was closed if (file->status() == Job::Running) { if (m_currentFiles < MetalinkSettings::simultanousFiles()) { ++m_currentFiles; file->start(); } else { //enough simultanous files already, so increase the number and set file to stop --> that will decrease the number again file->stop(); } } } m_ready = !m_dataSourceFactory.isEmpty(); slotUpdateCapabilities(); } void MetalinkXml::save(const QDomElement &element) { Transfer::save(element); QDomElement e = element; e.setAttribute("LocalMetalinkLocation", m_localMetalinkLocation.url()); foreach (DataSourceFactory *factory, m_dataSourceFactory) { factory->save(e); } } diff --git a/transfer-plugins/mirrorsearch/CMakeLists.txt b/transfer-plugins/mirrorsearch/CMakeLists.txt index bc115d91..54865657 100644 --- a/transfer-plugins/mirrorsearch/CMakeLists.txt +++ b/transfer-plugins/mirrorsearch/CMakeLists.txt @@ -1,37 +1,37 @@ include_directories( ../../ ) set(kget_mirrorsearchfactory_PART_SRCS mirrors.cpp mirrorsearchtransferdatasource.cpp mirrorsearchfactory.cpp ../../kget_debug.cpp ) kconfig_add_kcfg_files(kget_mirrorsearchfactory_PART_SRCS mirrorsearchsettings.kcfgc) add_library(kget_mirrorsearchfactory MODULE ${kget_mirrorsearchfactory_PART_SRCS}) kcoreaddons_desktop_to_json(kget_mirrorsearchfactory kget_mirrorsearchfactory.desktop) -target_link_libraries(kget_mirrorsearchfactory ${KDE4_KIO_LIBS} kgetcore) +target_link_libraries(kget_mirrorsearchfactory kgetcore) install(TARGETS kget_mirrorsearchfactory DESTINATION ${KGET_PLUGIN_INSTALL_DIR}) install(FILES kget_mirrorsearchfactory.kcfg DESTINATION ${KCFG_INSTALL_DIR}) ###Build KCM-Module set(kcm_kget_mirrorsearchfactory_PART_SRCS dlgmirrorsearch.cpp ../../kget_debug.cpp ) ki18n_wrap_ui(kcm_kget_mirrorsearchfactory_PART_SRCS dlgmirrorsearch.ui dlgengineediting.ui ) kconfig_add_kcfg_files(kcm_kget_mirrorsearchfactory_PART_SRCS mirrorsearchsettings.kcfgc) add_library(kcm_kget_mirrorsearchfactory MODULE ${kcm_kget_mirrorsearchfactory_PART_SRCS}) -target_link_libraries(kcm_kget_mirrorsearchfactory KF5::I18n KF5::KDELibs4Support) +target_link_libraries(kcm_kget_mirrorsearchfactory KF5::ConfigWidgets KF5::I18n KF5::WidgetsAddons) install(TARGETS kcm_kget_mirrorsearchfactory DESTINATION ${PLUGIN_INSTALL_DIR}) install(FILES kget_mirrorsearchfactory_config.desktop DESTINATION ${SERVICES_INSTALL_DIR}) diff --git a/transfer-plugins/mirrorsearch/dlgengineediting.ui b/transfer-plugins/mirrorsearch/dlgengineediting.ui index 93fe8a40..83e8b589 100644 --- a/transfer-plugins/mirrorsearch/dlgengineediting.ui +++ b/transfer-plugins/mirrorsearch/dlgengineediting.ui @@ -1,52 +1,45 @@ DlgEngineEditing 0 0 419 132 Engine name: - + true URL: - + true - - - KLineEdit - QLineEdit -
klineedit.h
-
-
diff --git a/transfer-plugins/mirrorsearch/dlgmirrorsearch.cpp b/transfer-plugins/mirrorsearch/dlgmirrorsearch.cpp index e15b6e3e..9b415bdb 100644 --- a/transfer-plugins/mirrorsearch/dlgmirrorsearch.cpp +++ b/transfer-plugins/mirrorsearch/dlgmirrorsearch.cpp @@ -1,154 +1,155 @@ /* This file is part of the KDE project Copyright (C) 2008 Manolo Valdes 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 "dlgmirrorsearch.h" #include "kget_macro.h" +#include "kget_debug.h" #include "mirrorsearchsettings.h" -#include "kget_debug.h" -#include #include #include + +#include #include #include #include DlgEngineEditing::DlgEngineEditing(QWidget *parent) : QDialog(parent) { QWidget *mainWidget = new QWidget(this); ui.setupUi(mainWidget); QVBoxLayout *mainLayout = new QVBoxLayout; setLayout(mainLayout); mainLayout->addWidget(mainWidget); setWindowTitle(i18n("Insert Engine")); setModal(true); QDialogButtonBox *buttonBox = new QDialogButtonBox(QDialogButtonBox::Ok|QDialogButtonBox::Cancel); okButton = buttonBox->button(QDialogButtonBox::Ok); okButton->setDefault(true); okButton->setShortcut(Qt::CTRL | Qt::Key_Return); connect(buttonBox, &QDialogButtonBox::accepted, this, &DlgEngineEditing::accept); connect(buttonBox, &QDialogButtonBox::rejected, this, &DlgEngineEditing::reject); mainLayout->addWidget(buttonBox); ui.engineNameLabel->setText(i18n("Engine name:")); ui.urlLabel->setText(i18n("URL:")); connect(ui.urlEdit,SIGNAL(textChanged(QString)), SLOT(slotChangeText())); connect(ui.engineNameEdit,SIGNAL(textChanged(QString)),SLOT(slotChangeText())); slotChangeText(); } DlgEngineEditing::~DlgEngineEditing() { } void DlgEngineEditing::slotChangeText() { okButton->setEnabled(!ui.urlEdit->text().isEmpty()); } QString DlgEngineEditing::engineName() const { return ui.engineNameEdit->text(); } QString DlgEngineEditing::engineUrl() const { return ui.urlEdit->text(); } KGET_EXPORT_PLUGIN_CONFIG(DlgSettingsWidget) DlgSettingsWidget::DlgSettingsWidget(QWidget *parent, const QVariantList &args) : KCModule(/*KGetFactory::componentData(),*/ parent, args) { ui.setupUi(this); ui.newEngineBt->setIcon(QIcon::fromTheme("list-add")); ui.removeEngineBt->setIcon(QIcon::fromTheme("list-remove")); connect(ui.newEngineBt, SIGNAL(clicked()), SLOT(slotNewEngine())); connect(ui.removeEngineBt, SIGNAL(clicked()), SLOT(slotRemoveEngine())); } DlgSettingsWidget::~DlgSettingsWidget() { } void DlgSettingsWidget::slotNewEngine() { DlgEngineEditing dialog; if(dialog.exec()) { addSearchEngineItem(dialog.engineName(), dialog.engineUrl()); changed(); } } void DlgSettingsWidget::slotRemoveEngine() { QList selectedItems = ui.enginesTreeWidget->selectedItems(); foreach(QTreeWidgetItem * selectedItem, selectedItems) delete(selectedItem); changed(); } void DlgSettingsWidget::load() { loadSearchEnginesSettings(); } void DlgSettingsWidget::addSearchEngineItem(const QString &name, const QString &url) { ui.enginesTreeWidget->addTopLevelItem(new QTreeWidgetItem(QStringList() << name << url)); changed(); } void DlgSettingsWidget::loadSearchEnginesSettings() { ui.enginesTreeWidget->clear();//Cleanup things first QStringList enginesNames = MirrorSearchSettings::self()->searchEnginesNameList(); QStringList enginesUrls = MirrorSearchSettings::self()->searchEnginesUrlList(); for(int i = 0; i < enginesNames.size(); i++) { addSearchEngineItem(enginesNames[i], enginesUrls[i]); } } void DlgSettingsWidget::saveSearchEnginesSettings() { QStringList enginesNames; QStringList enginesUrls; for(int i = 0; i < ui.enginesTreeWidget->topLevelItemCount(); i++) { enginesNames.append(ui.enginesTreeWidget->topLevelItem(i)->text(0)); enginesUrls.append(ui.enginesTreeWidget->topLevelItem(i)->text(1)); } MirrorSearchSettings::self()->setSearchEnginesNameList(enginesNames); MirrorSearchSettings::self()->setSearchEnginesUrlList(enginesUrls); MirrorSearchSettings::self()->save(); } void DlgSettingsWidget::save() { qCDebug(KGET_DEBUG); saveSearchEnginesSettings(); MirrorSearchSettings::self()->save(); } #include "dlgmirrorsearch.moc" diff --git a/transfer-plugins/mirrorsearch/dlgmirrorsearch.ui b/transfer-plugins/mirrorsearch/dlgmirrorsearch.ui index 2bb57415..d41fbf4a 100644 --- a/transfer-plugins/mirrorsearch/dlgmirrorsearch.ui +++ b/transfer-plugins/mirrorsearch/dlgmirrorsearch.ui @@ -1,79 +1,79 @@ DlgMirrorSearch 0 0 403 439 Search Engines false Engine Name URL Qt::Horizontal 91 32 New Engine... Remove KTitleWidget QWidget -
ktitlewidget.h
+
KTitleWidget
diff --git a/transfer-plugins/mirrorsearch/mirrors.cpp b/transfer-plugins/mirrorsearch/mirrors.cpp index 4b2da54d..db6b161e 100644 --- a/transfer-plugins/mirrorsearch/mirrors.cpp +++ b/transfer-plugins/mirrorsearch/mirrors.cpp @@ -1,103 +1,103 @@ /* This file is part of the KDE project Copyright (C) 2006 Manolo Valdes 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 "mirrors.h" #include "mirrorsearchsettings.h" #include "kget_debug.h" -#include +#include mirror::mirror() { if( !MirrorSearchSettings::searchEnginesUrlList().isEmpty()) m_search_engine = MirrorSearchSettings::searchEnginesUrlList().takeFirst(); } void mirror::search(const QUrl &url, QObject *receiver, const char *member) { qCDebug(KGET_DEBUG); m_url = url; if (m_url.path() != m_url.fileName()) { m_Urls << m_url; } search(m_url.fileName(),receiver,member); } void mirror::search(const QString &fileName, QObject *receiver, const char *member) { qCDebug(KGET_DEBUG); QUrl search(m_search_engine.replace("${filename}",fileName)); m_job = KIO::get(search, KIO::NoReload, KIO::HideProgressInfo); connect(m_job,SIGNAL(data(KIO::Job*,QByteArray)), SLOT(slotData(KIO::Job*,QByteArray))); connect(m_job,SIGNAL(result(KJob*)), SLOT(slotResult(KJob*))); connect(this,SIGNAL(urls(QList&)),receiver,member); } void mirror::slotData(KIO::Job *, const QByteArray& data) { qCDebug(KGET_DEBUG); if (data.size() == 0) return; m_data.append(data); } void mirror::slotResult( KJob *job ) { qCDebug(KGET_DEBUG); m_job = nullptr; int minUrlsNeeded = static_cast(!m_Urls.isEmpty()); if( job->error() ) { deleteLater(); return; } QString str(m_data); int start = 0, posOfTagA = 0, posOfTagHref = 0, hrefEnd = 0; while ((posOfTagA = str.indexOf(" minUrlsNeeded) emit urls(m_Urls); deleteLater(); } void MirrorSearch ( const QUrl &url, QObject *receiver, const char *member ) { mirror *searcher = new mirror(); searcher->search(url, receiver, member); } void MirrorSearch ( const QString &fileName, QObject *receiver, const char *member ) { mirror *searcher = new mirror(); searcher->search(fileName, receiver, member); } diff --git a/transfer-plugins/mirrorsearch/mirrorsearchfactory.cpp b/transfer-plugins/mirrorsearch/mirrorsearchfactory.cpp index a90c2afe..34bf0178 100644 --- a/transfer-plugins/mirrorsearch/mirrorsearchfactory.cpp +++ b/transfer-plugins/mirrorsearch/mirrorsearchfactory.cpp @@ -1,43 +1,43 @@ /* This file is part of the KDE project Copyright (C) 2008 Manolo Valdes 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 "mirrorsearchfactory.h" #include "mirrorsearchtransferdatasource.h" #include "core/scheduler.h" #include "core/transfergroup.h" #include "kget_debug.h" -#include +#include #include K_PLUGIN_CLASS_WITH_JSON(MirrorSearchFactory, "kget_mirrorsearchfactory.json") MirrorSearchFactory::MirrorSearchFactory(QObject *parent, const QVariantList &args) : TransferFactory(parent, args) { } MirrorSearchFactory::~MirrorSearchFactory() { } TransferDataSource *MirrorSearchFactory::createTransferDataSource(const QUrl &srcUrl, const QDomElement &type, QObject *parent) { qCDebug(KGET_DEBUG); if (type.attribute("type") == "search") { return new MirrorSearchTransferDataSource(srcUrl, parent); } return nullptr; } #include "mirrorsearchfactory.moc" diff --git a/transfer-plugins/mirrorsearch/mirrorsearchtransferdatasource.cpp b/transfer-plugins/mirrorsearch/mirrorsearchtransferdatasource.cpp index b1d0ddc0..6ad59202 100644 --- a/transfer-plugins/mirrorsearch/mirrorsearchtransferdatasource.cpp +++ b/transfer-plugins/mirrorsearch/mirrorsearchtransferdatasource.cpp @@ -1,48 +1,48 @@ /* This file is part of the KDE project Copyright (C) 2008 Manolo Valdes 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 "mirrorsearchtransferdatasource.h" #include "mirrors.h" #include "kget_debug.h" -#include +#include MirrorSearchTransferDataSource::MirrorSearchTransferDataSource(const QUrl &srcUrl, QObject *parent) : TransferDataSource(srcUrl, parent) { m_filename = m_sourceUrl.fileName(); qCDebug(KGET_DEBUG) << m_filename; } void MirrorSearchTransferDataSource::start() { qCDebug(KGET_DEBUG); if(!m_filename.isEmpty()) MirrorSearch (m_filename, this, SLOT(slotSearchUrls(QList&))); } void MirrorSearchTransferDataSource::stop() { qCDebug(KGET_DEBUG); } void MirrorSearchTransferDataSource::addSegments(const QPair &segmentSize, const QPair &segmentRange) { Q_UNUSED(segmentSize) Q_UNUSED(segmentRange) qCDebug(KGET_DEBUG); } void MirrorSearchTransferDataSource::slotSearchUrls(QList& Urls) { emit data(Urls); } diff --git a/transfer-plugins/mmsthreads/mmstransfer.cpp b/transfer-plugins/mmsthreads/mmstransfer.cpp index 33321aa6..9c4992e5 100755 --- a/transfer-plugins/mmsthreads/mmstransfer.cpp +++ b/transfer-plugins/mmsthreads/mmstransfer.cpp @@ -1,204 +1,204 @@ /* This file is part of the KDE project Copyright (C) 2011 Ernesto Rodriguez Ortiz 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 "mmstransfer.h" #include "kget_debug.h" -#include +#include #include #include MmsTransfer::MmsTransfer(TransferGroup * parent, TransferFactory * factory, Scheduler * scheduler, const QUrl & source, const QUrl &dest, const QDomElement * e) : Transfer(parent, factory, scheduler, source, dest, e), m_mmsdownload(NULL), m_amountThreads(MmsSettings::threads()), m_retryDownload(false) { // make sure that the DataLocation directory exists (earlier this used to be handled by KStandardDirs) if (!QFileInfo::exists(QStandardPaths::writableLocation(QStandardPaths::DataLocation))) { QDir().mkpath(QStandardPaths::writableLocation(QStandardPaths::DataLocation)); } m_fileTemp = QStandardPaths::writableLocation(QStandardPaths::DataLocation) + QLatin1Char('/') + m_dest.fileName(); qCDebug(KGET_DEBUG) << "Mms transfer initialized: " + m_source.toString(); } MmsTransfer::~MmsTransfer() { /** If m_mmsdownload is not deleted we delete it before end.*/ if (m_mmsdownload) { m_mmsdownload->quit(); m_mmsdownload->deleteLater(); } } void MmsTransfer::start() { /** Starting the download, is created the thread m_mmsdownload and is started the download*/ if (m_mmsdownload || status() == Finished) { return; } setStatus(Job::Running, i18nc("transfer state: running", "Running...."), QIcon::fromTheme("media-playback-start").pixmap(16)); m_mmsdownload = new MmsDownload(m_source.toString(), m_dest.toLocalFile(), m_fileTemp, m_amountThreads); connect(m_mmsdownload, SIGNAL(finished()), this, SLOT(slotResult())); connect(m_mmsdownload, SIGNAL(signBrokenUrl()), this, SLOT(slotBrokenUrl())); connect(m_mmsdownload, SIGNAL(signNotAllowMultiDownload()), this, SLOT(slotNotAllowMultiDownload())); connect(m_mmsdownload, SIGNAL(signTotalSize(qulonglong)), this, SLOT(slotTotalSize(qulonglong))); connect(m_mmsdownload, SIGNAL(signDownloaded(qulonglong)), this, SLOT(slotProcessedSizeAndPercent(qulonglong))); connect(m_mmsdownload, SIGNAL(signSpeed(ulong)), this, SLOT(slotSpeed(ulong))); connect(m_mmsdownload, SIGNAL(signRestartDownload(int)), this, SLOT(slotConnectionsErrors(int))); m_mmsdownload->start(); setTransferChange(Tc_Status, true); } void MmsTransfer::stop() { /** The download is stopped, we call m_mmsdownload->stopTransfer() and when all threads * are finish m_mmsdownload will be deleted in MmsTransfer::slotResult(). */ if ((status() == Stopped) || (status() == Finished)) { return; } if (m_mmsdownload) { if (m_mmsdownload->threadsAlive() > 0) { m_mmsdownload->stopTransfer(); } } setStatus(Job::Stopped, i18nc("transfer state: stopped", "Stopped"), QIcon::fromTheme("process-stop").pixmap(16)); m_downloadSpeed = 0; setTransferChange(Tc_Status | Tc_DownloadSpeed, true); } void MmsTransfer::deinit(Transfer::DeleteOptions options) { /** Deleting the temporary file and the unfinish file*/ if (options & Transfer::DeleteFiles) { KIO::Job *del = KIO::del(QUrl::fromLocalFile(m_fileTemp), KIO::HideProgressInfo); if (!del->exec()) { qCDebug(KGET_DEBUG) << "Could not delete " << m_fileTemp; } del = KIO::del(m_dest, KIO::HideProgressInfo); if (!del->exec()) { qCDebug(KGET_DEBUG) << "Could not delete " << m_dest.path(); } } } void MmsTransfer::slotResult() { /** This slot is connected with the signal finish of m_mmsdownload*/ /** Deleting m_mmsdownload.*/ m_mmsdownload->deleteLater(); m_mmsdownload = NULL; /** If the download end without problems is changed the status to Finished and is deleted * the temporary file where is saved the status of all threads that download the file. */ if (m_downloadedSize == m_totalSize && m_totalSize != 0) { setStatus(Job::Finished, i18nc("Transfer State:Finished","Finished"), QIcon::fromTheme("dialog-ok").pixmap(16)); m_percent = 100; m_downloadSpeed = 0; setTransferChange(Tc_Status | Tc_Percent | Tc_DownloadSpeed, true); KIO::Job *del = KIO::del(QUrl::fromLocalFile(m_fileTemp), KIO::HideProgressInfo); if (!del->exec()) { qCDebug(KGET_DEBUG) << "Could not delete " << m_fileTemp; } } /** If m_retryDownload == true then some threads has fail to connect, so the download was * stopped in MmsTransfer::slotConnectionsErrors() and here when all the connected thread * are finished we delete the temporary file and we start again the download using the amount * of threads defined in MmsTransfer::slotConnectionsErrors(). */ if (m_retryDownload) { m_retryDownload = false; KIO::Job *del = KIO::del(QUrl::fromLocalFile(m_fileTemp), KIO::HideProgressInfo); if (!del->exec()) { qCDebug(KGET_DEBUG) << "Could not delete " << m_fileTemp; } start(); } } void MmsTransfer::slotTotalSize(qulonglong size) { m_totalSize = size; setTransferChange(Tc_TotalSize, true); } void MmsTransfer::slotSpeed(ulong speed) { m_downloadSpeed = (status() == Running) ? speed : 0; setTransferChange(Tc_DownloadSpeed, true); } void MmsTransfer::slotProcessedSizeAndPercent(qulonglong size) { m_downloadedSize = size; m_percent = (m_downloadedSize * 100) / m_totalSize; setTransferChange(Tc_DownloadedSize | Tc_Percent, true); } void MmsTransfer::slotBrokenUrl() { setError(i18n("Download failed, could not access this URL."), QIcon::fromTheme("dialog-cancel").pixmap(16), Job::NotSolveable); setTransferChange(Tc_Status, true); } void MmsTransfer::slotNotAllowMultiDownload() { /** Some stream not allow seek in to a position, so we can't use more than one thread to * download the file, this is notify to the user because the download will take longer. */ KGet::showNotification(nullptr, "notification", i18n("This URL does not allow multiple connections,\n" "the download will take longer.")); } void MmsTransfer::slotConnectionsErrors(int connections) { /** Here is called stop() for stop the download, set a new amount of thread * and set m_retryDownload = true for restart the download when mmsdownload is finish and * emit a singal connected with MmsTransfer::slotResult(), see in MmsTransfer::slotResult() * for understand when its started again the download. */ stop(); m_retryDownload = true; if (connections) { m_amountThreads = connections; } else { m_amountThreads--; } } diff --git a/transfer-plugins/mmsthreads/mmstransferfactory.cpp b/transfer-plugins/mmsthreads/mmstransferfactory.cpp index 7312b4ff..bb37cd0b 100755 --- a/transfer-plugins/mmsthreads/mmstransferfactory.cpp +++ b/transfer-plugins/mmsthreads/mmstransferfactory.cpp @@ -1,72 +1,72 @@ /* This file is part of the KDE project Copyright (C) 2011 Ernesto Rodriguez Ortiz 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 "mmstransferfactory.h" #include "mmssettings.h" #include "mmstransfer.h" -#include +#include #include "kget_debug.h" -#include +#include K_PLUGIN_CLASS_WITH_JSON(MmsTransferFactory, "kget_mmsfactory.json") MmsTransferFactory::MmsTransferFactory(QObject *parent, const QVariantList &args) : TransferFactory(parent, args) {} MmsTransferFactory::~MmsTransferFactory() {} Transfer * MmsTransferFactory::createTransfer( const QUrl &srcUrl, const QUrl &destUrl, TransferGroup * parent, Scheduler * scheduler, const QDomElement * e ) { qCDebug(KGET_DEBUG) << "MmsTransferFactory::createTransfer"; QString prot = srcUrl.scheme(); qCDebug(KGET_DEBUG) << "Protocol = " << prot; if (prot == "mms" || prot == "mmsh") { return new MmsTransfer(parent, this, scheduler, srcUrl, destUrl, e); } return nullptr; } QWidget * MmsTransferFactory::createDetailsWidget( TransferHandler * transfer ) { Q_UNUSED(transfer) return nullptr; //Temporary!! } const QList MmsTransferFactory::actions(TransferHandler *handler) { Q_UNUSED(handler) return QList(); } bool MmsTransferFactory::isSupported(const QUrl &url) const { QString prot = url.scheme(); qCDebug(KGET_DEBUG) << "Protocol = " << prot; return (prot == "mms" || prot == "mmsh"); } #include "mmstransferfactory.moc" diff --git a/transfer-plugins/multisegmentkio/multisegkiodatasource.cpp b/transfer-plugins/multisegmentkio/multisegkiodatasource.cpp index 966537e5..10aa6e89 100644 --- a/transfer-plugins/multisegmentkio/multisegkiodatasource.cpp +++ b/transfer-plugins/multisegmentkio/multisegkiodatasource.cpp @@ -1,265 +1,265 @@ /* This file is part of the KDE project Copyright (C) 2008 Manolo Valdes 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 "multisegkiodatasource.h" #include "segment.h" #include "core/transfer.h" #include "kget_debug.h" -#include +#include MultiSegKioDataSource::MultiSegKioDataSource(const QUrl &srcUrl, QObject *parent) : TransferDataSource(srcUrl, parent), m_size(0), m_canResume(false), m_started(false) { qCDebug(KGET_DEBUG) << "Create MultiSegKioDataSource for" << m_sourceUrl << this; setCapabilities(capabilities() | Transfer::Cap_FindFilesize); } MultiSegKioDataSource::~MultiSegKioDataSource() { qCDebug(KGET_DEBUG) << this; } void MultiSegKioDataSource::start() { qCDebug(KGET_DEBUG) << this; m_started = true; foreach (Segment *segment, m_segments) { segment->startTransfer(); } } void MultiSegKioDataSource::stop() { qCDebug(KGET_DEBUG) << this << m_segments.count() << "segments stopped."; m_started = false; foreach (Segment *segment, m_segments) { if (segment->findingFileSize()) { qCDebug(KGET_DEBUG) << "Removing findingFileSize segment" << this; m_segments.removeAll(segment); segment->deleteLater(); } else { segment->stopTransfer(); } } } QList > MultiSegKioDataSource::assignedSegments() const { QList > assigned; foreach (Segment *segment, m_segments) { assigned.append(segment->assignedSegments()); } return assigned; } void MultiSegKioDataSource::addSegments(const QPair &segmentSize, const QPair &segmentRange) { Segment *segment = new Segment(m_sourceUrl, segmentSize, segmentRange, this); m_segments.append(segment); connect(segment, SIGNAL(canResume()), this, SLOT(slotCanResume())); connect(segment, SIGNAL(totalSize(KIO::filesize_t,QPair)), this, SLOT(slotTotalSize(KIO::filesize_t,QPair))); connect(segment, SIGNAL(data(KIO::fileoffset_t,QByteArray,bool&)), this, SIGNAL(data(KIO::fileoffset_t,QByteArray,bool&))); connect(segment, SIGNAL(finishedSegment(Segment*,int,bool)), this, SLOT(slotFinishedSegment(Segment*,int,bool))); connect(segment, SIGNAL(error(Segment*,QString,Transfer::LogLevel)), this, SLOT(slotError(Segment*,QString,Transfer::LogLevel))); connect(segment, SIGNAL(finishedDownload(KIO::filesize_t)), this, SLOT(slotFinishedDownload(KIO::filesize_t))); connect(segment, SIGNAL(urlChanged(QUrl)), this, SLOT(slotUrlChanged(QUrl))); if (m_started) { segment->startTransfer(); } } void MultiSegKioDataSource::slotUrlChanged(const QUrl &url) { if (m_sourceUrl != url) { emit urlChanged(m_sourceUrl, url); m_sourceUrl = url; } } void MultiSegKioDataSource::findFileSize(KIO::fileoffset_t segmentSize) { addSegments(qMakePair(segmentSize, segmentSize), qMakePair(-1, -1)); Segment *segment = m_segments.last(); segment->startTransfer(); } void MultiSegKioDataSource::slotSpeed(ulong downloadSpeed) { m_speed = downloadSpeed; emit speed(m_speed); } void MultiSegKioDataSource::slotFinishedSegment(Segment *segment, int segmentNum, bool connectionFinished) { if (connectionFinished) { m_segments.removeAll(segment); segment->deleteLater(); } emit finishedSegment(this, segmentNum, connectionFinished); } void MultiSegKioDataSource::setSupposedSize(KIO::filesize_t supposedSize) { m_supposedSize = supposedSize; //check if the size is correct slotTotalSize(m_size); } void MultiSegKioDataSource::slotTotalSize(KIO::filesize_t size, const QPair &range) { qCDebug(KGET_DEBUG) << "Size found for" << m_sourceUrl << size << "bytes"; m_size = size; //findFileSize was called if ((range.first != -1) && (range.second != -1)) { emit foundFileSize(this, size, range); } //the filesize is not what it should be, maybe using a wrong mirror if (m_size && m_supposedSize && (m_size != m_supposedSize)) { qCDebug(KGET_DEBUG) << "Size does not match for" << m_sourceUrl << this; emit broken(this, WrongDownloadSize); } } void MultiSegKioDataSource::slotCanResume() { qCDebug(KGET_DEBUG) << this; if (!m_canResume) { m_canResume = true; setCapabilities(capabilities() | Transfer::Cap_Resuming); } } int MultiSegKioDataSource::currentSegments() const { return m_segments.count(); } Segment *MultiSegKioDataSource::mostUnfinishedSegments(int *unfin) const { int unfinished = 0; Segment *seg = nullptr; foreach (Segment *segment, m_segments) { if (segment->countUnfinishedSegments() > unfinished) { unfinished = segment->countUnfinishedSegments(); seg = segment; } } if (unfin) { *unfin = unfinished; } return seg; } int MultiSegKioDataSource::countUnfinishedSegments() const { int unfinished = 0; mostUnfinishedSegments(&unfinished); return unfinished; } QPair MultiSegKioDataSource::split() { QPair unassigned = qMakePair(-1, -1); Segment *seg = mostUnfinishedSegments(); if (seg) { unassigned = seg->split(); } return unassigned; } QPair MultiSegKioDataSource::removeConnection() { QPair unassigned = qMakePair(-1, -1); Segment *seg = mostUnfinishedSegments(); if (seg) { unassigned = seg->assignedSegments(); m_segments.removeAll(seg); seg->deleteLater(); } return unassigned; } bool MultiSegKioDataSource::tryMerge(const QPair &segmentSize, const QPair &segmentRange) { foreach (Segment *segment, m_segments) { if (segment->merge(segmentSize, segmentRange)) { return true; } } return false; } void MultiSegKioDataSource::slotError(Segment *segment, const QString &errorText, Transfer::LogLevel logLevel) { qCDebug(KGET_DEBUG) << "Error" << errorText << "segment" << segment; const QPair size = segment->segmentSize(); const QPair range = segment->assignedSegments(); m_segments.removeAll(segment); segment->deleteLater(); emit log(errorText, logLevel); if (m_segments.isEmpty()) { qCDebug(KGET_DEBUG) << this << "has broken segments."; emit brokenSegments(this, range); } else { //decrease the number of maximum paralell downloads, maybe the server does not support so many connections if (m_paralellSegments > 1) { --m_paralellSegments; } qCDebug(KGET_DEBUG) << this << "reducing connections to" << m_paralellSegments << "and freeing range of semgents" << range; if (!tryMerge(size, range)) { emit freeSegments(this, range); } } } void MultiSegKioDataSource::slotFinishedDownload(KIO::filesize_t size) { stop(); emit finishedDownload(this, size); } void MultiSegKioDataSource::slotRestartBrokenSegment() { qCDebug(KGET_DEBUG) << this; start(); } diff --git a/transfer-plugins/multisegmentkio/segment.cpp b/transfer-plugins/multisegmentkio/segment.cpp index ec782d0b..00cbbb43 100644 --- a/transfer-plugins/multisegmentkio/segment.cpp +++ b/transfer-plugins/multisegmentkio/segment.cpp @@ -1,377 +1,377 @@ /* This file is part of the KDE project Copyright (C) 2006 Manolo Valdes 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 "segment.h" #include "multisegkiosettings.h" #include #include "kget_debug.h" -#include +#include #include #include Segment::Segment(const QUrl &src, const QPair &segmentSize, const QPair &segmentRange, QObject *parent) : QObject(parent), m_findFilesize((segmentRange.first == -1) && (segmentRange.second == -1)), m_canResume(true), m_status(Stopped), m_currentSegment(segmentRange.first), m_endSegment(segmentRange.second), m_errorCount(0), m_offset(segmentSize.first * segmentRange.first), m_currentSegSize(segmentSize.first), m_bytesWritten(0), m_getJob(nullptr), m_url(src), m_segSize(segmentSize) { //last segment if (m_endSegment - m_currentSegment == 0) { m_currentSegSize = m_segSize.second; } if (m_findFilesize) { m_offset = 0; m_currentSegSize = 0; m_currentSegment = 0; m_endSegment = 0; m_totalBytesLeft = 0; } else { m_totalBytesLeft = m_segSize.first * (m_endSegment - m_currentSegment) + m_segSize.second; } } Segment::~Segment() { if (m_getJob) { qCDebug(KGET_DEBUG) << "Closing transfer ..."; m_getJob->kill(KJob::Quietly); } } bool Segment::findingFileSize() const { return m_findFilesize; } bool Segment::createTransfer() { qCDebug(KGET_DEBUG) << " -- " << m_url; if ( m_getJob ) return false; m_getJob = KIO::get(m_url, KIO::Reload, KIO::HideProgressInfo); m_getJob->suspend(); m_getJob->addMetaData( "errorPage", "false" ); m_getJob->addMetaData( "AllowCompressedPage", "false" ); if (m_offset) { m_canResume = false;//FIXME set m_canResume to false by default!! m_getJob->addMetaData( "resume", KIO::number(m_offset) ); connect(m_getJob, SIGNAL(canResume(KIO::Job*,KIO::filesize_t)), SLOT(slotCanResume(KIO::Job*,KIO::filesize_t))); } #if 0 //TODO: we disable that code till it's implemented in kdelibs, also we need to think, which settings we should use if (Settings::speedLimit()) { m_getJob->addMetaData( "speed-limit", KIO::number(Settings::transferSpeedLimit() * 1024) ); } #endif connect(m_getJob, SIGNAL(totalSize(KJob*,qulonglong)), this, SLOT(slotTotalSize(KJob*,qulonglong))); connect(m_getJob, SIGNAL(data(KIO::Job*,QByteArray)), SLOT(slotData(KIO::Job*,QByteArray))); connect(m_getJob, SIGNAL(result(KJob*)), SLOT(slotResult(KJob*))); connect(m_getJob, SIGNAL(redirection(KIO::Job *,const QUrl &)), SLOT(slotRedirection(KIO::Job *, const QUrl &))); return true; } void Segment::slotRedirection(KIO::Job* , const QUrl &url) { m_url = url; emit urlChanged(url); } void Segment::slotCanResume( KIO::Job* job, KIO::filesize_t offset ) { Q_UNUSED(job) Q_UNUSED(offset) qCDebug(KGET_DEBUG); m_canResume = true; emit canResume(); } void Segment::slotTotalSize(KJob *job, qulonglong size) { Q_UNUSED(job) qCDebug(KGET_DEBUG) << "Size found for" << m_url; if (m_findFilesize) { int numSegments = size / m_segSize.first; KIO::fileoffset_t rest = size % m_segSize.first; if (rest) { ++numSegments; m_segSize.second = rest; } m_endSegment = numSegments - 1; m_currentSegment = 0; m_currentSegSize = (numSegments == 1 ? m_segSize.second : m_segSize.first); m_totalBytesLeft = size; emit totalSize(size, qMakePair(m_currentSegment, m_endSegment)); m_findFilesize = false; } else { emit totalSize(size, qMakePair(-1, -1)); } } bool Segment::startTransfer () { qCDebug(KGET_DEBUG) << m_url; if (!m_getJob) { createTransfer(); } if (m_getJob && (m_status != Running)) { setStatus(Running, false); m_getJob->resume(); return true; } return false; } bool Segment::stopTransfer() { qCDebug(KGET_DEBUG); setStatus(Stopped, false); if (m_getJob) { if (m_getJob) { m_getJob->kill(KJob::EmitResult); } return true; } return false; } void Segment::slotResult( KJob *job ) { qCDebug(KGET_DEBUG) << "Job:" << job << m_url << "error:" << job->error(); m_getJob = nullptr; //clear the buffer as the download might be moved around if (m_status == Stopped) { m_buffer.clear(); } if ( !m_buffer.isEmpty() ) { if (m_findFilesize && !job->error()) { qCDebug(KGET_DEBUG) << "Looping until write the buffer ..." << m_url; slotWriteRest(); return; } } if (!m_totalBytesLeft && !m_findFilesize) { setStatus(Finished); return; } if( m_status == Killed ) { return; } if (job->error() && (m_status == Running)) { emit error(this, job->errorString(), Transfer::Log_Error); } } void Segment::slotData(KIO::Job *, const QByteArray& _data) { // Check if the transfer allows resuming... if (m_offset && !m_canResume) { qCDebug(KGET_DEBUG) << m_url << "does not allow resuming."; stopTransfer(); setStatus(Killed, false ); const QString errorText = KIO::buildErrorString(KIO::ERR_CANNOT_RESUME, m_url.toString()); emit error(this, errorText, Transfer::Log_Warning); return; } m_buffer.append(_data); if (!m_findFilesize && m_totalBytesLeft && static_cast(m_buffer.size()) >= m_totalBytesLeft) { qCDebug(KGET_DEBUG) << "Segment::slotData() buffer full. stoping transfer...";//TODO really stop it? is this even needed? if (m_getJob) { m_getJob->kill(KJob::Quietly); m_getJob = nullptr; } m_buffer.truncate(m_totalBytesLeft); slotWriteRest(); } else { /* write to the local file only if the buffer has more than 100kbytes this hack try to avoid too much cpu usage. it seems to be due KIO::Filejob so remove it when it works property */ if (m_buffer.size() > MultiSegKioSettings::saveSegSize() * 1024) writeBuffer(); } } bool Segment::writeBuffer() { qCDebug(KGET_DEBUG) << "Segment::writeBuffer() sending:" << m_buffer.size() << "from job:" << m_getJob; if (m_buffer.isEmpty()) { return false; } bool worked = false; emit data(m_offset, m_buffer, worked); if (worked) { m_currentSegSize -= m_buffer.size(); if (!m_findFilesize) { m_totalBytesLeft -= m_buffer.size(); } m_offset += m_buffer.size(); m_bytesWritten += m_buffer.size(); m_buffer.clear(); qCDebug(KGET_DEBUG) << "Segment::writeBuffer() updating segment record of job:" << m_getJob << "--" << m_totalBytesLeft << "bytes left"; } //finding filesize, so no segments defined yet if (m_findFilesize) { return worked; } //check which segments have been finished bool finished = false; //m_currentSegSize being smaller than 1 means that at least one segment has been finished while (m_currentSegSize <= 0 && !finished) { finished = (m_currentSegment == m_endSegment); emit finishedSegment(this, m_currentSegment, finished); if (!finished) { ++m_currentSegment; m_currentSegSize += (m_currentSegment == m_endSegment ? m_segSize.second : m_segSize.first); } } return worked; } void Segment::slotWriteRest() { if (m_buffer.isEmpty()) { return; } qCDebug(KGET_DEBUG) << this; if (writeBuffer()) { m_errorCount = 0; if (m_findFilesize) { emit finishedDownload(m_bytesWritten); } return; } if (++m_errorCount >= 100) { qWarning() << "Failed to write to the file:" << m_url << this; emit error(this, i18n("Failed to write to the file."), Transfer::Log_Error); } else { qCDebug(KGET_DEBUG) << "Wait 50 msec:" << this; QTimer::singleShot(50, this, SLOT(slotWriteRest())); } } void Segment::setStatus(Status stat, bool doEmit) { m_status = stat; if (doEmit) emit statusChanged(this); } QPair Segment::assignedSegments() const { return QPair(m_currentSegment, m_endSegment); } QPair Segment::segmentSize() const { return m_segSize; } int Segment::countUnfinishedSegments() const { return m_endSegment - m_currentSegment; } QPair Segment::split() { if (m_getJob) { m_getJob->suspend(); } QPair freed = QPair(-1, -1); const int free = std::ceil((countUnfinishedSegments() + 1) / static_cast(2)); if (!free) { qCDebug(KGET_DEBUG) << "None freed, start:" << m_currentSegment << "end:" << m_endSegment; if (m_getJob) { m_getJob->resume(); } return freed; } const int newEnd = m_endSegment - free; freed = QPair(newEnd + 1, m_endSegment); qCDebug(KGET_DEBUG) << "Start:" << m_currentSegment << "old end:" << m_endSegment << "new end:" << newEnd << "freed:" << freed; m_endSegment = newEnd; m_totalBytesLeft -= m_segSize.first * (free - 1) + m_segSize.second; //end changed, so in any case the lastSegSize should be the normal segSize if (free) { m_segSize.second = m_segSize.first; } if (m_getJob) { m_getJob->resume(); } return freed; } bool Segment::merge(const QPair &segmentSize, const QPair &segmentRange) { if (m_endSegment + 1 == segmentRange.first) { m_endSegment = segmentRange.second; m_segSize.second = segmentSize.second; m_totalBytesLeft += segmentSize.first * (m_endSegment - segmentRange.first) + m_segSize.second; return true; } return false; } diff --git a/transfer-plugins/multisegmentkio/transfermultisegkiofactory.cpp b/transfer-plugins/multisegmentkio/transfermultisegkiofactory.cpp index 40315285..42bf73e0 100644 --- a/transfer-plugins/multisegmentkio/transfermultisegkiofactory.cpp +++ b/transfer-plugins/multisegmentkio/transfermultisegkiofactory.cpp @@ -1,98 +1,98 @@ /* This file is part of the KDE project Copyright (C) 2004 Dario Massarin Copyright (C) 2006 Manolo Valdes 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 "transfermultisegkiofactory.h" #include "core/scheduler.h" #include "core/transfergroup.h" #include "multisegkiosettings.h" #include "transfermultisegkio.h" #include "multisegkiodatasource.h" #include #include "kget_debug.h" -#include +#include #include "kget_macro.h" K_PLUGIN_CLASS_WITH_JSON(TransferMultiSegKioFactory, "kget_multisegkiofactory.json") TransferMultiSegKioFactory::TransferMultiSegKioFactory(QObject *parent, const QVariantList &args) : TransferFactory(parent, args) { } TransferMultiSegKioFactory::~TransferMultiSegKioFactory() { } Transfer * TransferMultiSegKioFactory::createTransfer( const QUrl &srcUrl, const QUrl &destUrl, TransferGroup * parent, Scheduler * scheduler, const QDomElement * e ) { qCDebug(KGET_DEBUG); if (isSupported(srcUrl) && (!e || !e->firstChildElement("factories").isNull())) { return new TransferMultiSegKio(parent, this, scheduler, srcUrl, destUrl, e); } return nullptr; } TransferHandler * TransferMultiSegKioFactory::createTransferHandler(Transfer * transfer, Scheduler * scheduler) { return new TransferHandler(transfer, scheduler); } QWidget * TransferMultiSegKioFactory::createDetailsWidget( TransferHandler * transfer ) { Q_UNUSED(transfer) return nullptr; //Temporary!! } const QList TransferMultiSegKioFactory::actions(TransferHandler *handler) { Q_UNUSED(handler) return QList(); } TransferDataSource * TransferMultiSegKioFactory::createTransferDataSource(const QUrl &srcUrl, const QDomElement &type, QObject *parent) { qCDebug(KGET_DEBUG); //only use this TransferDataSource if no type is specified and the protocolls match if (!type.attribute("type").isEmpty()) { return nullptr; } if (isSupported(srcUrl)) { return new MultiSegKioDataSource(srcUrl, parent); } return nullptr; } bool TransferMultiSegKioFactory::isSupported(const QUrl &url) const { QString prot = url.scheme(); qCDebug(KGET_DEBUG) << "Protocol = " << prot; return addsProtocols().contains(prot); } QStringList TransferMultiSegKioFactory::addsProtocols() const { static const QStringList protocols = QStringList() << "http" << "https" << "ftp" << "sftp"; return protocols; } #include "transfermultisegkiofactory.moc" diff --git a/ui/droptarget.cpp b/ui/droptarget.cpp index e7f6cd29..c5e2433c 100644 --- a/ui/droptarget.cpp +++ b/ui/droptarget.cpp @@ -1,458 +1,458 @@ /* This file is part of the KDE project Copyright (C) 2002 Patrick Charbonnier Based On Caitoo v.0.7.3 (c) 1998 - 2000, Matej Koss Copyright (C) 2008 Urs Wolfer 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 "ui/droptarget.h" #include "core/kget.h" #include "core/transferhandler.h" #include "core/transfergrouphandler.h" #include "core/transfertreemodel.h" #include "settings.h" #include "mainwindow.h" #include "ui/newtransferdialog.h" -#include -#include -#include +#include #include -#include +#include -#include +#include #include +#include +#include +#include #include #include #include -#include #include -#include +#include #define TARGET_SIZE 64 #define TARGET_ANI_MS 20 #define TARGET_TOOLTIP_MS 1000 DropTarget::DropTarget(MainWindow * mw) : QWidget(nullptr, Qt::WindowStaysOnTopHint | Qt::FramelessWindowHint), parentWidget(mw), animTimer(nullptr), showInformation(false) { KWindowSystem::setState(winId(), NET::SkipTaskbar); QRect screenGeo = qApp->desktop()->screenGeometry(Settings::dropPosition()); if ((screenGeo.x() + screenGeo.width() >= Settings::dropPosition().x() && screenGeo.y() + screenGeo.height() >= Settings::dropPosition().y()) && Settings::dropPosition().y() >= 0 && Settings::dropPosition().x() >= 0) position = QPoint(Settings::dropPosition()); else position = QPoint(screenGeo.x() + screenGeo.width() / 2, screenGeo.y() + screenGeo.height() / 2); setFixedSize(TARGET_SIZE, TARGET_SIZE); if(Settings::dropSticky()) KWindowSystem::setState(winId(), KWindowSystem::Sticky); cachedPixmap = QIcon::fromTheme("kget").pixmap(TARGET_SIZE); if (!cachedPixmap.mask().isNull()) { QBitmap mask(size()); mask.fill(Qt::color0); QBitmap pixMask = cachedPixmap.mask(); QPainter p(&mask); p.drawPixmap((mask.width() - pixMask.width())/2, (mask.height() - pixMask.height())/2, pixMask); setMask(mask); } else setMask(QBitmap()); // popup menu for right mouse button popupMenu = new QMenu(this); popupMenu->addSection(mw->windowTitle()); QAction * downloadAction = mw->actionCollection()->action("start_all_download"); popupMenu->addAction( downloadAction ); connect(downloadAction, &QAction::toggled, this, &DropTarget::slotStartStopToggled); popupMenu->addSeparator(); pop_show = popupMenu->addAction( QString(), this, SLOT(toggleMinimizeRestore()) ); popupMenu->addAction(parentWidget->actionCollection()->action("show_drop_target")); pop_sticky = popupMenu->addAction(i18nc("fix position for droptarget", "Sticky"), this, SLOT(toggleSticky())); pop_sticky->setCheckable(true); pop_sticky->setChecked(Settings::dropSticky()); popupMenu->addSeparator(); popupMenu->addAction( mw->actionCollection()->action("preferences") ); QAction *quitAction = new QAction(this); quitAction->setText(i18n("Quit KGet")); quitAction->setIcon(QIcon::fromTheme("system-shutdown")); connect(quitAction, SIGNAL(triggered()), mw, SLOT(slotQuit())); popupMenu->addAction(quitAction); isdragging = false; // Enable dropping setAcceptDrops(true); if ( Settings::showDropTarget() && Settings::firstRun() ) { showInformation = true; } animTimer = new QTimer(this); popupTimer = new QTimer(this); setMouseTracking(true); connect(KGet::model(), SIGNAL(transfersChangedEvent(QMap)), this, SLOT(slotToolTipUpdate())); connect(popupTimer, &QTimer::timeout, this, &DropTarget::slotToolTipTimer); } DropTarget::~DropTarget() { Settings::setDropPosition( pos() ); Settings::setShowDropTarget( !isHidden() ); Settings::self()->save(); // unsigned long state = KWindowSystem::windowInfo(kdrop->winId()).state(); // // state will be 0L if droptarget is hidden. Sigh. // config->writeEntry("State", state ? state : DEFAULT_DOCK_STATE ); } void DropTarget::setDropTargetVisible( bool shown, bool internal ) { if (shown == !isHidden()) return; if ( internal ) Settings::setShowDropTarget( shown ); if (!shown) { Settings::setDropPosition( pos() ); position = pos(); if ( Settings::animateDropTarget() ) playAnimationHide(); else hide(); } else { if ( Settings::animateDropTarget() ) { playAnimationShow(); } else { move(position); show(); } slotToolTipUpdate(); } } void DropTarget::playAnimationShow() { if (animTimer->isActive()) animTimer->stop(); animTimer->disconnect(); connect(animTimer, &QTimer::timeout, this, &DropTarget::slotAnimateShow); move(position.x(), -TARGET_SIZE); ani_y = -1; ani_vy = 0; show(); animTimer->start(TARGET_ANI_MS); } void DropTarget::playAnimationHide() { if (animTimer->isActive()) animTimer->stop(); animTimer->disconnect(); connect(animTimer, &QTimer::timeout, this, &DropTarget::slotAnimateHide); ani_y = (float)y(); ani_vy = 0; animTimer->start(TARGET_ANI_MS); } void DropTarget::playAnimationSync() { if (animTimer->isActive()) animTimer->stop(); animTimer->disconnect(); connect(animTimer, &QTimer::timeout, this, &DropTarget::slotAnimateSync); ani_y = (float)y(); ani_vy = -1; animTimer->start(TARGET_ANI_MS); } void DropTarget::slotStartStopToggled( bool started ) { if ( started && Settings::animateDropTarget() ) playAnimationSync(); } /** widget events */ void DropTarget::dragEnterEvent(QDragEnterEvent * event) { event->setAccepted(event->mimeData()->hasUrls() || event->mimeData()->hasText()); } void DropTarget::dropEvent(QDropEvent * event) { QList list = event->mimeData()->urls(); QString str; if (!list.isEmpty()) { if (list.count() == 1 && list.first().url().endsWith(QLatin1String(".kgt"))) { int msgBoxResult = KMessageBox::questionYesNoCancel(this, i18n("The dropped file is a KGet Transfer List"), "KGet", KGuiItem(i18n("&Download"), QIcon::fromTheme("document-save")), KGuiItem(i18n("&Load transfer list"), QIcon::fromTheme("list-add")), KStandardGuiItem::cancel()); if (msgBoxResult == 3) //Download NewTransferDialogHandler::showNewTransferDialog(list.first().url()); if (msgBoxResult == 4) //Load KGet::load(list.first().url()); } else { if (list.count() == 1) { str = event->mimeData()->text(); NewTransferDialogHandler::showNewTransferDialog(str); } else NewTransferDialogHandler::showNewTransferDialog(list); } } else { NewTransferDialogHandler::showNewTransferDialog(); } if ( Settings::animateDropTarget() ) playAnimationSync(); } void DropTarget::closeEvent( QCloseEvent * e ) { - if( kapp->sessionSaving() ) + if( qApp->isSavingSession() ) e->ignore(); else { setVisible( false ); e->accept(); } } void DropTarget::mousePressEvent(QMouseEvent * e) { // If the user click on the droptarget, stop any animation that is going on if(animTimer) { animTimer->stop(); } if (e->button() == Qt::LeftButton) { isdragging = true; dx = e->globalPos().x() - pos().x(); dy = e->globalPos().y() - pos().y(); } else if (e->button() == Qt::RightButton) { pop_show->setText(parentWidget->isHidden() ? i18n("Show Main Window") : i18n("Hide Main Window") ); popupMenu->popup(e->globalPos()); } else if (e->button() == Qt::MidButton) { //Here we paste the transfer QString newtransfer = QApplication::clipboard()->text(); newtransfer = newtransfer.trimmed(); if(!newtransfer.isEmpty()) KGet::addTransfer(QUrl(newtransfer), QString(), QString(), QString(), true); } } void DropTarget::mouseReleaseEvent(QMouseEvent *) { isdragging = false; } void DropTarget::mouseDoubleClickEvent(QMouseEvent * e) { if (e->button() == Qt::LeftButton) toggleMinimizeRestore(); } void DropTarget::mouseMoveEvent(QMouseEvent * e) { Q_UNUSED(e) if ( isdragging && !Settings::dropSticky() ) { move( QCursor::pos().x() - dx, QCursor::pos().y() - dy ); e->accept(); } } void DropTarget::enterEvent(QEvent * event) { Q_UNUSED(event) popupTimer->start(2000); } void DropTarget::leaveEvent(QEvent * event) { Q_UNUSED(event) popupTimer->stop(); } void DropTarget::paintEvent( QPaintEvent * ) { QPainter p(this); p.drawPixmap(0, 0, cachedPixmap); } void DropTarget::toggleSticky() { Settings::setDropSticky( !Settings::dropSticky() ); pop_sticky->setChecked(Settings::dropSticky()); if ( Settings::dropSticky() ) KWindowSystem::setState(winId(), KWindowSystem::SkipTaskbar | KWindowSystem::StaysOnTop | KWindowSystem::Sticky); else KWindowSystem::clearState(winId(), KWindowSystem::Sticky); } void DropTarget::toggleMinimizeRestore() { bool nextState = parentWidget->isHidden(); Settings::setShowMain( nextState ); parentWidget->setVisible( nextState ); if(nextState) { KWindowSystem::activateWindow(static_cast(parentWidget)->winId()); } } /** widget animations */ void DropTarget::slotAnimateShow() { static float dT = TARGET_ANI_MS / 1000.0; ani_vy -= ani_y * 30 * dT; ani_vy *= 0.95; ani_y += ani_vy * dT; move(x(), qRound(position.y() * (1 + ani_y))); if ( fabs(ani_y) < 0.01 && fabs(ani_vy) < 0.01 && animTimer->isActive() ) { animTimer->stop(); if (showInformation) KPassivePopup::message(i18n("Drop Target"), i18n("You can drag download links into the drop target."), this); } } void DropTarget::slotAnimateHide() { static float dT = TARGET_ANI_MS / 1000.0; ani_vy += -2000 * dT; float new_y = y() + ani_vy * dT; if ( new_y < -height() ) { animTimer->stop(); hide(); move( x(), qRound(ani_y) ); } else move( x(), qRound(new_y) ); } void DropTarget::slotAnimateSync() { static float dT = TARGET_ANI_MS / 1000.0; ani_vy += 4 * dT; // from -1 to 1 in 0.5 seconds float i = 2 * M_PI * ani_vy; // from -2PI to 2PI float j = (i == 0.0) ? 1 : (sin( i ) / i) * (1 + fabs(ani_vy)); if ( ani_vy >= 1 ) { animTimer->stop(); move( x(), qRound(ani_y) ); } else move( x(), qRound(ani_y + 6*j) ); } void DropTarget::slotToolTipUpdate() { QStringList dataList; QString data; foreach (TransferHandler *transfer, KGet::allTransfers()) { data.clear(); switch (transfer->status()) { case Job::Finished: data = i18nc("%1 filename, %2 total size, %3 status", "%1(%2) %3", transfer->source().fileName(), KIO::convertSize(transfer->totalSize()), transfer->statusText()); break; case Job::Running: data = i18nc("%1 filename, %2 percent complete, %3 downloaded out of %4 total size", "%1(%2% %3/%4) Speed:%5/s", transfer->source().fileName(), transfer->percent(), KIO::convertSize(transfer->downloadedSize()), KIO::convertSize(transfer->totalSize()), KIO::convertSize(transfer->downloadSpeed())); break; default: data = i18nc("%1 filename, %2 percent complete, %3 downloaded out of %4 total size, %5 status", "%1(%2% %3/%4) %5", transfer->source().fileName(), transfer->percent(), KIO::convertSize(transfer->downloadedSize()), KIO::convertSize(transfer->totalSize()), transfer->statusText()); break; } dataList << data; } if (!dataList.empty()) tooltipText = dataList.join("\n"); else tooltipText = i18n("Ready"); } void DropTarget::slotToolTipTimer() { if (!popupMenu->isVisible() && isVisible() && mask().contains(mapFromGlobal(QCursor::pos()))) QToolTip::showText(QCursor::pos(),tooltipText,this,rect()); } void DropTarget::slotClose() { setVisible( false ); } diff --git a/ui/linkview/kget_linkview.cpp b/ui/linkview/kget_linkview.cpp index cb93d3af..e5995759 100644 --- a/ui/linkview/kget_linkview.cpp +++ b/ui/linkview/kget_linkview.cpp @@ -1,476 +1,476 @@ /* This file is part of the KDE project Copyright (C) 2002 Carsten Pfeiffer Copyright (C) 2007 Urs Wolfer 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 "kget_linkview.h" #include "settings.h" #include "kget_sortfilterproxymodel.h" #include "core/kget.h" #include "core/linkimporter.h" #include "ui/newtransferdialog.h" #include "kget_debug.h" -#include +#include #include #include #include #include #include #include #include #include #include #include KGetLinkView::KGetLinkView(QWidget *parent) : KGetSaveSizeDialog("KGetLinkView", parent), m_linkImporter(nullptr), m_nameAction(nullptr), m_urlAction(nullptr) { setAttribute(Qt::WA_DeleteOnClose); setWindowTitle(i18n("Import Links")); /*if (parent) { KWindowInfo info = KWindowSystem::windowInfo(parent->winId(), NET::WMDesktop, NET::WMDesktop); KWindowSystem::setCurrentDesktop(info.desktop()); KWindowSystem::forceActiveWindow(parent->winId()); }*///TODO: Port all KWindowSystem stuff // proxy model to filter links m_proxyModel = new KGetSortFilterProxyModel(1, this); m_proxyModel->setDynamicSortFilter(true); m_proxyModel->setFilterCaseSensitivity(Qt::CaseInsensitive); ui.setupUi(this); m_proxyModel->setShowWebContent(ui.showWebContent->isChecked()); ui.filterMode->addItem(i18n("Contains"), KGetSortFilterProxyModel::Contain); ui.filterMode->addItem(i18n("Does Not Contain"), KGetSortFilterProxyModel::DoesNotContain); // set the Icons ui.importLinks->setIcon(QIcon::fromTheme("document-import")); ui.showCombo->addItem(QIcon::fromTheme("view-list-icons"), i18n("All"), KGetSortFilterProxyModel::NoFilter); ui.showCombo->addItem(QIcon::fromTheme("video-x-generic"), i18n("Videos"), KGetSortFilterProxyModel::VideoFiles); ui.showCombo->addItem(QIcon::fromTheme("image-x-generic"), i18n("Images"), KGetSortFilterProxyModel::ImageFiles); ui.showCombo->addItem(QIcon::fromTheme("audio-x-generic"), i18n("Audio"), KGetSortFilterProxyModel::AudioFiles); ui.showCombo->addItem(QIcon::fromTheme("package-x-generic"), i18n("Archives"), KGetSortFilterProxyModel::CompressedFiles ); ui.treeView->setModel(m_proxyModel); ui.progressBar->hide(); //creates pattern syntax menu for the text filter m_patternSyntaxMenu = new QMenu(i18nc("of a filter, e.g. RegExp or Wildcard", "Pattern Syntax"), this); QAction *wildcardAction = new QAction(i18n("Escape Sequences"), this); wildcardAction->setCheckable(true); wildcardAction->setChecked(Settings::linkViewFilterPatternSyntax() == Wildcard); QAction *regExpAction = new QAction(i18n("Regular Expression"), this); regExpAction->setCheckable(true); regExpAction->setChecked(Settings::linkViewFilterPatternSyntax() == RegExp); QActionGroup *actionGroup = new QActionGroup(this); actionGroup->addAction(wildcardAction); actionGroup->addAction(regExpAction); m_patternSyntaxMenu->addActions(actionGroup->actions()); //Filter for name/url actions QActionGroup *columnGroup = new QActionGroup(this); m_nameAction = new QAction(i18nc("name of a file", "Name"), this); m_nameAction->setCheckable(true); m_nameAction->setChecked(true); m_urlAction = new QAction(i18n("URL"), this); m_urlAction->setCheckable(true); columnGroup->addAction(m_nameAction); columnGroup->addAction(m_urlAction); connect(columnGroup, SIGNAL(triggered(QAction*)), this, SLOT(slotFilterColumn(QAction*))); connect(wildcardAction, SIGNAL(toggled(bool)), this, SLOT(wildcardPatternToggled(bool))); connect(ui.treeView, SIGNAL(doubleClicked(QModelIndex)), this, SLOT(uncheckItem(QModelIndex))); connect(ui.textFilter, SIGNAL(textChanged(QString)), SLOT(setTextFilter(QString))); connect(ui.textFilter, SIGNAL(aboutToShowContextMenu(QMenu*)), this, SLOT(contextMenuDisplayed(QMenu*))); connect(ui.filterMode, SIGNAL(currentIndexChanged(int)), this, SLOT(slotFilterModeChanged(int))); connect(ui.showCombo, SIGNAL(currentIndexChanged(int)), this, SLOT(slotMimeTypeChanged(int))); connect(ui.showCombo, SIGNAL(currentIndexChanged(int)), SLOT(updateSelectionButtons())); connect(ui.urlRequester, SIGNAL(textChanged(QString)), SLOT(updateImportButtonStatus(QString))); connect(ui.urlRequester, SIGNAL(urlSelected(QUrl)), SLOT(slotStartImport())); connect(ui.selectAll, SIGNAL(clicked()), this, SLOT(checkAll())); connect(ui.deselectAll, SIGNAL(clicked()), this, SLOT(uncheckAll())); connect(ui.invertSelection, SIGNAL(clicked()), this, SLOT(slotInvertSelection())); connect(this, SIGNAL(accepted()), this, SLOT(slotStartLeech())); connect(ui.showWebContent, SIGNAL(stateChanged(int)), m_proxyModel, SLOT(setShowWebContent(int))); connect(ui.importLinks, SIGNAL(clicked()), this, SLOT(slotStartImport())); connect(ui.treeView->selectionModel(), SIGNAL(selectionChanged(QItemSelection,QItemSelection)), SLOT(selectionChanged())); connect(ui.dialogButtonBox, SIGNAL(rejected()), SLOT(reject())); connect(ui.dialogButtonBox, SIGNAL(accepted()), SLOT(accept())); m_downloadButton = ui.dialogButtonBox->addButton(i18nc("Download the items which have been selected","&Download"), QDialogButtonBox::AcceptRole); m_downloadButton->setIcon(QIcon::fromTheme("kget")); checkClipboard(); } KGetLinkView::~KGetLinkView() { delete(m_linkImporter); } void KGetLinkView::checkClipboard() { QString clipboardContent = QApplication::clipboard()->text(QClipboard::Clipboard); if (clipboardContent.length() > 0) { delete m_linkImporter; m_linkImporter = new LinkImporter(this); m_linkImporter->checkClipboard(clipboardContent); slotImportFinished(); } } void KGetLinkView::setLinks(const QStringList &links) { m_links = links; showLinks(m_links, false); } void KGetLinkView::showLinks(const QStringList &links, bool urlRequestVisible) { ui.importWidget->setVisible(urlRequestVisible); QStandardItemModel *model = new QStandardItemModel(0, 5, this); model->setHeaderData(0, Qt::Horizontal, i18n("Auxiliary header")); model->setHeaderData(1, Qt::Horizontal, i18n("File Name")); model->setHeaderData(2, Qt::Horizontal, i18n("Description")); model->setHeaderData(3, Qt::Horizontal, i18nc("list header: type of file", "File Type")); model->setHeaderData(4, Qt::Horizontal, i18n("Location (URL)")); foreach (const QString &linkitem, links) { QUrl url; QMimeDatabase db; QMimeType mt; if (linkitem.contains(QLatin1String("url "), Qt::CaseInsensitive) && linkitem.contains(QLatin1String("type "), Qt::CaseInsensitive)) { const QStringList items = linkitem.split(QLatin1Char(' '), QString::SkipEmptyParts); const int count = items.count(); int index = items.indexOf(QLatin1String("url")); if (index > -1 && index+1 < count) url = items.at(index+1); index = items.indexOf(QLatin1String("type")); if (index > -1 && index+1 < count) mt = db.mimeTypeForName(items.at(index+1)); } else { url = linkitem; mt = db.mimeTypeForFile(linkitem, QMimeDatabase::MatchExtension); } qCDebug(KGET_DEBUG) << "Adding:" << linkitem; QString file = url.fileName(); if (file.isEmpty()) file = QString(url.host()); QString mimeTypeName, mimeTypeIcon, mimeTypeComment; if (mt.isValid()) { mimeTypeName = mt.name(); mimeTypeIcon = mt.iconName(); mimeTypeComment = mt.comment(); } QStandardItem *item = new QStandardItem(file); item->setIcon(QIcon::fromTheme(mimeTypeIcon)); item->setCheckable(true); item->setCheckState(Qt::Checked); item->setData(QVariant(url.fileName()), Qt::DisplayRole); item->setData(QVariant(mimeTypeName), Qt::UserRole); // used for filtering DownloadFilterType QList items; QStandardItem *number = new QStandardItem(); number->setData(model->rowCount(), Qt::DisplayRole);//used for initial sorting items << number; items << item; items << new QStandardItem(); items << new QStandardItem(mimeTypeComment); items << new QStandardItem(url.toDisplayString()); model->insertRow(model->rowCount(), items); } connect(model, SIGNAL(itemChanged(QStandardItem*)), this, SLOT(selectionChanged())); m_proxyModel->setSourceModel(model); m_proxyModel->setFilterKeyColumn(1); m_proxyModel->sort(0); ui.treeView->header()->hideSection(0); ui.treeView->setColumnWidth(1, 200); // make the filename column bigger by default selectionChanged(); // adapt buttons to the new situation } void KGetLinkView::slotMimeTypeChanged(int index) { m_proxyModel->setFilterType(ui.showCombo->itemData(index).toInt()); } void KGetLinkView::slotFilterModeChanged(int index) { m_proxyModel->setFilterMode(ui.filterMode->itemData(index).toInt()); } void KGetLinkView::slotFilterColumn(QAction *action) { //FIXME make this not depend on "magic numbers"? m_proxyModel->setFilterColumn(action == m_urlAction ? 4 : 1); } void KGetLinkView::slotStartLeech() { QStandardItemModel *model = qobject_cast(m_proxyModel->sourceModel()); if (model) { QList urls; for (int row = 0; row < model->rowCount(); row++) { QStandardItem *checkeableItem = model->item(row, 1); if (checkeableItem->checkState() == Qt::Checked) { urls.append(QUrl(model->data(model->index(row, 4)).toString())); } } NewTransferDialogHandler::showNewTransferDialog(urls); } } void KGetLinkView::setPageUrl( const QString& url ) { setWindowTitle( i18n( "Links in: %1 - KGet", url ) ); } void KGetLinkView::importUrl(const QString &url) { if (url.isEmpty()) { QUrl clipboardUrl = QUrl(QApplication::clipboard()->text(QClipboard::Clipboard).trimmed()); if (clipboardUrl.isValid() && ((!clipboardUrl.scheme().isEmpty() && !clipboardUrl.host().isEmpty()) || (clipboardUrl.isLocalFile()))) { ui.urlRequester->setUrl(clipboardUrl); } } else { ui.urlRequester->setUrl(QUrl(url)); slotStartImport(); } } void KGetLinkView::selectionChanged() { QStandardItemModel *model = qobject_cast(m_proxyModel->sourceModel()); if (model) { const int modelRowCount = model->rowCount(); bool buttonEnabled = false; int count = 0; for (int row = 0; row < modelRowCount; row++) { QStandardItem *checkeableItem = model->item(row, 1); if ((checkeableItem->checkState() == Qt::Checked)) { buttonEnabled = true; // only count the checked files that are currently visible if (m_proxyModel->mapFromSource(model->index(row, 1)).isValid()) { count++; } } } ui.selectAll->setEnabled( !(!modelRowCount || count == m_proxyModel->rowCount() ) ); ui.deselectAll->setEnabled( count > 0 ); ui.invertSelection->setEnabled( count > 0 ); m_downloadButton->setEnabled(buttonEnabled); } } void KGetLinkView::setTextFilter(const QString &text) { // TODO: escape user input for avoding malicious user input! (FiNEX) QString temp = text.isEmpty() ? ui.textFilter->text() : text; if (Settings::linkViewFilterPatternSyntax() == Wildcard) { m_proxyModel->setFilterWildcard(temp); } else { QRegExp rx(temp, Qt::CaseSensitive, QRegExp::RegExp2); m_proxyModel->setFilterRegExp(rx); } updateSelectionButtons(); } void KGetLinkView::updateSelectionButtons() { const bool isFiltered = !ui.textFilter->text().isEmpty() || (ui.showCombo->currentIndex() != KGetSortFilterProxyModel::NoFilter); ui.selectAll->setText(isFiltered ? i18n("&Select All Filtered") : i18n("&Select All")); ui.deselectAll->setText(isFiltered ? i18n("D&eselect All Filtered") : i18n("D&eselect All")); selectionChanged(); } void KGetLinkView::checkAll() { QStandardItemModel *itemsModel = qobject_cast(m_proxyModel->sourceModel()); if (itemsModel) { for (int row = 0; row < m_proxyModel->rowCount(); row++) { const QModelIndex index = m_proxyModel->mapToSource(m_proxyModel->index(row, 3)); QStandardItem *item = itemsModel->item(index.row(), 1); item->setCheckState(Qt::Checked); } } } void KGetLinkView::uncheckAll() { QStandardItemModel *itemsModel = qobject_cast(m_proxyModel->sourceModel()); if (itemsModel) { for (int row = 0; row < m_proxyModel->rowCount(); row++) { const QModelIndex index = m_proxyModel->mapToSource(m_proxyModel->index(row, 3)); QStandardItem *item = itemsModel->item(index.row(), 1); item->setCheckState(Qt::Unchecked); } } } void KGetLinkView::uncheckItem(const QModelIndex &index) { QStandardItemModel *model = qobject_cast(m_proxyModel->sourceModel()); if (model) { if (index.column() != 0) { QStandardItem *item = model->itemFromIndex(model->index(m_proxyModel->mapToSource(index).row(), 1)); item->setCheckState(item->checkState() == Qt::Checked ? Qt::Unchecked : Qt::Checked); } } } void KGetLinkView::slotCheckSelected() { QStandardItemModel *model = qobject_cast(m_proxyModel->sourceModel()); if (model) { foreach(const QModelIndex &index, ui.treeView->selectionModel()->selectedIndexes()) { QModelIndex sourceIndex = m_proxyModel->mapToSource(index); QStandardItem *item = model->item(sourceIndex.row(), 1); item->setCheckState(Qt::Checked); } } } void KGetLinkView::slotInvertSelection() { QStandardItemModel *itemsModel = qobject_cast(m_proxyModel->sourceModel()); if (itemsModel) { for (int row = 0; row < m_proxyModel->rowCount(); row++) { const QModelIndex index = m_proxyModel->mapToSource(m_proxyModel->index(row, 3)); QStandardItem *item = itemsModel->item(index.row(), 1); item->setCheckState((item->checkState() == Qt::Checked) ? Qt::Unchecked : Qt::Checked); } } } void KGetLinkView::slotStartImport() { delete m_linkImporter; m_linkImporter = new LinkImporter(ui.urlRequester->url(), this); connect(m_linkImporter, SIGNAL(progress(int)), SLOT(slotImportProgress(int))); connect(m_linkImporter, SIGNAL(finished()), SLOT(slotImportFinished())); if (!ui.urlRequester->url().isLocalFile()) { m_linkImporter->copyRemoteFile(); } m_linkImporter->start(); ui.progressBar->show(); } void KGetLinkView::slotImportProgress(int progress) { ui.progressBar->setValue(progress); } void KGetLinkView::slotImportFinished() { ui.progressBar->hide(); m_links = m_linkImporter->links(); showLinks(m_links, true); } void KGetLinkView::updateImportButtonStatus(const QString &text) { bool enabled = false; if (!text.isEmpty()) { QUrl url(text); if (url.isValid()) { enabled = true; } } ui.importLinks->setEnabled(enabled); } void KGetLinkView::contextMenuDisplayed(QMenu *menu) { menu->addSeparator(); menu->addMenu(m_patternSyntaxMenu); menu->addSeparator()->setText(i18n("Filter Column")); menu->addAction(m_nameAction); menu->addAction(m_urlAction); } void KGetLinkView::wildcardPatternToggled(bool enabled) { if (enabled) { Settings::setLinkViewFilterPatternSyntax(Wildcard); } else { Settings::setLinkViewFilterPatternSyntax(RegExp); } } diff --git a/ui/metalinkcreator/generalwidget.cpp b/ui/metalinkcreator/generalwidget.cpp index 92f916bb..469714a7 100644 --- a/ui/metalinkcreator/generalwidget.cpp +++ b/ui/metalinkcreator/generalwidget.cpp @@ -1,125 +1,129 @@ /*************************************************************************** * 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. * * * * 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 "generalwidget.h" -#include +#include #include "metalinker.h" GeneralWidget::GeneralWidget(QWidget *parent) : QWidget (parent) { ui.setupUi(this); ui.dynamic->setToolTip(ui.labelDynamic->toolTip()); connect(ui.publishedGroupBox, &QGroupBox::toggled, this, &GeneralWidget::slotPublishedEnabled); connect(ui.updatedGroupBox, &QGroupBox::toggled, this, &GeneralWidget::slotUpdatedEnabled); } void GeneralWidget::load(const KGetMetalink::Metalink &metalink) const { ui.origin->setUrl(metalink.origin); ui.dynamic->setChecked(metalink.dynamic); ui.publishedGroupBox->setChecked(metalink.published.isValid()); ui.use_publishedtimeoffset->setChecked(metalink.published.timeZoneOffset.isValid()); if (metalink.published.isValid()) { ui.published->setDateTime(metalink.published.dateTime); ui.publishedoffset->setTime(metalink.published.timeZoneOffset); ui.publishedNegative->setChecked(metalink.published.negativeOffset); } else { ui.published->setDateTime(QDateTime::currentDateTime()); - int offset = KSystemTimeZones::local().currentOffset(); + QDateTime a = QDateTime::currentDateTime(); + a.setTimeZone(QTimeZone::systemTimeZone()); + int offset = a.offsetFromUtc(); const bool negativeOffset = (offset < 0); offset = abs(offset); QTime time = QTime(0, 0, 0); time = time.addSecs(abs(offset)); ui.publishedoffset->setTime(time); //to not enable publishedNegative block the signals ui.use_publishedtimeoffset->blockSignals(true); ui.use_publishedtimeoffset->setChecked(true); ui.use_publishedtimeoffset->blockSignals(false); ui.publishedNegative->setChecked(negativeOffset); } ui.updatedGroupBox->setChecked(metalink.updated.isValid()); ui.use_updatedtimeoffset->setChecked(metalink.updated.timeZoneOffset.isValid()); if (metalink.updated.isValid()) { ui.updated->setDateTime(metalink.updated.dateTime); ui.updatedoffset->setTime(metalink.updated.timeZoneOffset); ui.updatedNegative->setChecked(metalink.updated.negativeOffset); } else { ui.updated->setDateTime(QDateTime::currentDateTime()); - int offset = KSystemTimeZones::local().currentOffset(); + QDateTime a = QDateTime::currentDateTime(); + a.setTimeZone(QTimeZone::systemTimeZone()); + int offset = a.offsetFromUtc(); const bool negativeOffset = (offset < 0); QTime time = QTime(0, 0, 0); time = time.addSecs(abs(offset)); ui.updatedoffset->setTime(time); //to not enable publishedNegative block the signals ui.use_updatedtimeoffset->blockSignals(true); ui.use_updatedtimeoffset->setChecked(true); ui.use_updatedtimeoffset->blockSignals(false); ui.updatedNegative->setChecked(negativeOffset); } } void GeneralWidget::save(KGetMetalink::Metalink *metalink) { metalink->origin = QUrl(ui.origin->text()); metalink->dynamic = ui.dynamic->isChecked(); metalink->published.clear(); if (ui.publishedGroupBox->isChecked()) { metalink->published.dateTime = ui.published->dateTime(); if (ui.use_publishedtimeoffset->isChecked()) { metalink->published.timeZoneOffset = ui.publishedoffset->time(); } } metalink->updated.clear(); if (ui.updatedGroupBox->isChecked()) { metalink->updated.dateTime = ui.updated->dateTime(); if (ui.use_updatedtimeoffset->isChecked()) { metalink->updated.timeZoneOffset = ui.updatedoffset->time(); } } } void GeneralWidget::slotPublishedEnabled(bool enabled) { if (enabled) { ui.publishedNegative->setEnabled(ui.use_publishedtimeoffset->isChecked()); } } void GeneralWidget::slotUpdatedEnabled(bool enabled) { if (enabled) { ui.updatedNegative->setEnabled(ui.use_updatedtimeoffset->isChecked()); } } diff --git a/ui/metalinkcreator/metalinkcreator.cpp b/ui/metalinkcreator/metalinkcreator.cpp index 303cf71a..62701385 100644 --- a/ui/metalinkcreator/metalinkcreator.cpp +++ b/ui/metalinkcreator/metalinkcreator.cpp @@ -1,395 +1,395 @@ /*************************************************************************** * 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. * * * * 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 "metalinkcreator.h" #include "filedlg.h" #include "dragdlg.h" #include "localemodels.h" #include "generalwidget.h" -#include +#include #include -#include #include +#include +#include #include +#include -#include #include #include #include -#include //TODO for 4.4 look at the changes of the newest Draft --> what elements have to be added/removed FileWidget::FileWidget(QWidget *parent) : QWidget(parent) { setAcceptDrops(true); } void FileWidget::dragEnterEvent(QDragEnterEvent *event) { if (event->mimeData()->hasUrls()) { event->acceptProposedAction(); } } void FileWidget::dropEvent(QDropEvent *event) { QList urls = event->mimeData()->urls(); event->acceptProposedAction(); if (!urls.isEmpty()) { emit urlsDropped(urls); } } MetalinkCreator::MetalinkCreator(QWidget *parent) : KAssistantDialog(parent), m_needUrlCount(0), m_countrySort(nullptr), m_languageModel(nullptr), m_languageSort(nullptr), m_introduction(nullptr), m_generalPage(nullptr), m_filesModel(nullptr) { create(); connect(finishButton(), &QPushButton::clicked, this, &MetalinkCreator::slotSave); connect(this, SIGNAL(currentPageChanged(KPageWidgetItem*,KPageWidgetItem*)), this, SLOT(slotUpdateAssistantButtons(KPageWidgetItem*,KPageWidgetItem*))); qRegisterMetaType("KGetMetalink::File"); connect(&m_thread, SIGNAL(fileResult(KGetMetalink::File)), this, SLOT(slotAddFile(KGetMetalink::File))); connect(&m_thread, SIGNAL(finished()), this, SLOT(slotThreadFinished())); setWindowTitle(i18n("Create a Metalink")); } MetalinkCreator::~MetalinkCreator() { } void MetalinkCreator::slotUpdateAssistantButtons(KPageWidgetItem *to, KPageWidgetItem *from) { //once we leave the introduction page the data is being loaded if (m_introduction && m_generalPage && (from == m_introduction) && (to == m_generalPage)) { load(); } //it is impossible to return to the introduction page backButton()->setEnabled(to != m_generalPage); if (!m_filesModel->rowCount()) { uiFiles.infoWidget->setText(i18n("Add at least one file.")); } else if (m_needUrlCount) { uiFiles.infoWidget->setText(i18n("You need to set mirrors for the entries with an icon.")); } uiFiles.infoWidget->setVisible(!m_filesModel->rowCount() || m_needUrlCount); //only enable finish when the metalink is valid (i.e. no required data missing) //and the thread is not running finishButton()->setEnabled(metalink.isValid() && !m_thread.isRunning()); } void MetalinkCreator::create() { createIntroduction(); m_general = new GeneralWidget(this); m_generalPage = addPage(m_general, i18n("General optional information for the metalink.")); QTimer::singleShot(0, this, SLOT(slotDelayedCreation())); } void MetalinkCreator::slotDelayedCreation() { CountryModel *countryModel = new CountryModel(this); countryModel->setupModelData(KLocale::global()->allCountriesList()); m_countrySort = new QSortFilterProxyModel(this); m_countrySort->setSourceModel(countryModel); m_countrySort->sort(0); m_languageModel = new LanguageModel(this); m_languageModel->setupModelData(KLocale::global()->allLanguagesList()); m_languageSort = new QSortFilterProxyModel(this); m_languageSort->setSourceModel(m_languageModel); m_languageSort->sort(0); createFiles(); slotUpdateIntroductionNextButton(); } void MetalinkCreator::load() { QUrl url = uiIntroduction.load->url(); if (uiIntroduction.loadButton->isChecked() && url.isValid()) { if (!KGetMetalink::HandleMetalink::load(url, &metalink)) { KMessageBox::error(this, i18n("Unable to load: %1", url.toString()), i18n("Error")); } } m_general->load(metalink); loadFiles(); } void MetalinkCreator::slotSave() { m_general->save(&metalink); QUrl url = uiIntroduction.save->url(); if (url.isValid()) { if(!KGetMetalink::HandleMetalink::save(url, &metalink)) { KMessageBox::error(this, i18n("Unable to save to: %1", url.toString()), i18n("Error")); } } } void MetalinkCreator::createIntroduction() { QWidget *widget = new QWidget(this); uiIntroduction.setupUi(widget); uiIntroduction.save->setFilter("*.meta4|" + i18n("Metalink Version 4.0 file (*.meta4)") + "\n*.metalink|" + i18n("Metalink Version 3.0 file (*.metalink)")); uiIntroduction.save->setAcceptMode(QFileDialog::AcceptSave); connect(uiIntroduction.save, SIGNAL(textChanged(QString)), this, SLOT(slotUpdateIntroductionNextButton())); connect(uiIntroduction.load, SIGNAL(textChanged(QString)), this, SLOT(slotUpdateIntroductionNextButton())); connect(uiIntroduction.loadButton, SIGNAL(toggled(bool)), this, SLOT(slotUpdateIntroductionNextButton())); m_introduction = addPage(widget, i18n("Define the saving location.")); setValid(m_introduction, false); } void MetalinkCreator::slotUpdateIntroductionNextButton() { bool enableNext = false; //check if a save location and if selected if also a load location has been specified and if the m_countrySort has been created enableNext = uiIntroduction.save->url().isValid() && m_countrySort; if (enableNext && uiIntroduction.loadButton->isChecked()) { enableNext = uiIntroduction.load->url().isValid(); } setValid(m_introduction, enableNext); } void MetalinkCreator::createFiles() { m_handler = new DirectoryHandler(this); connect(m_handler, SIGNAL(finished()), this, SLOT(slotOpenDragDlg())); FileWidget *widget = new FileWidget(this); uiFiles.setupUi(widget); m_filesModel = new QStandardItemModel(0, 1, this); uiFiles.files->setModel(m_filesModel); uiFiles.infoWidget->setCloseButtonVisible(false); uiFiles.infoWidget->setMessageType(KMessageWidget::Information); uiFiles.add_local_file->setIcon(QIcon::fromTheme("list-add")); KGuiItem::assign(uiFiles.add_file, KStandardGuiItem::add()); KGuiItem::assign(uiFiles.properties_file, KStandardGuiItem::properties()); uiFiles.properties_file->setEnabled(false); KGuiItem::assign(uiFiles.remove_file, KStandardGuiItem::remove()); uiFiles.remove_file->setEnabled(false); uiFiles.dragDrop->hide(); connect(uiFiles.add_local_file, SIGNAL(clicked(bool)), this, SLOT(slotAddLocalFilesClicked())); connect(uiFiles.add_file, SIGNAL(clicked(bool)), this, SLOT(slotAddClicked())); connect(uiFiles.remove_file, SIGNAL(clicked(bool)), this, SLOT(slotRemoveFile())); connect(uiFiles.properties_file, SIGNAL(clicked(bool)), this, SLOT(slotFileProperties())); connect(uiFiles.files->selectionModel(), SIGNAL(selectionChanged(QItemSelection,QItemSelection)), this, SLOT(slotUpdateFilesButtons())); connect(widget, SIGNAL(urlsDropped(QList)), m_handler, SLOT(slotFiles(QList))); addPage(widget, i18nc("file as in file on hard drive", "Files")); } void MetalinkCreator::loadFiles() { foreach (const KGetMetalink::File &file, metalink.files.files) { QStandardItem *item = new QStandardItem(file.name); if (!file.resources.isValid()) { ++m_needUrlCount; item->setIcon(QIcon::fromTheme("edit-delete")); } m_filesModel->insertRow(m_filesModel->rowCount(), item); } } void MetalinkCreator::slotUpdateFilesButtons() { const QModelIndexList indexes = uiFiles.files->selectionModel()->selectedRows(); uiFiles.remove_file->setEnabled(indexes.count()); bool propertiesEnabled = (indexes.count() == 1); uiFiles.properties_file->setEnabled(propertiesEnabled); } void MetalinkCreator::slotAddLocalFilesClicked() { QPointer dialog = new QFileDialog(this); dialog->setFileMode(QFileDialog::ExistingFiles); if (dialog->exec() == QDialog::Accepted) { m_handler->slotFiles(dialog->selectedUrls()); } delete dialog; } void MetalinkCreator::slotAddFile() { QStandardItem *item = new QStandardItem(m_tempFile.name); m_filesModel->insertRow(m_filesModel->rowCount(), item); metalink.files.files.append(m_tempFile); m_tempFile.clear(); slotUpdateAssistantButtons(nullptr, m_files); } void MetalinkCreator::slotAddFile(const KGetMetalink::File &file) { QStandardItem *item = new QStandardItem(file.name); if (!file.resources.isValid()) { ++m_needUrlCount; item->setIcon(QIcon::fromTheme("edit-delete")); } m_filesModel->insertRow(m_filesModel->rowCount(), item); metalink.files.files.append(file); slotUpdateAssistantButtons(nullptr, m_files); } void MetalinkCreator::slotFileEdited(const QString &oldFileName, const QString &newFileName) { Q_UNUSED(oldFileName) const QModelIndex index = uiFiles.files->selectionModel()->selectedRows().first(); QStandardItem *item = m_filesModel->itemFromIndex(index); item->setText(newFileName); //had no url but has it now if (!item->icon().isNull()) { --m_needUrlCount; item->setIcon(QIcon()); } slotUpdateAssistantButtons(nullptr, m_files); } void MetalinkCreator::slotRemoveFile() { while (uiFiles.files->selectionModel()->hasSelection()) { const QModelIndex index = uiFiles.files->selectionModel()->selectedRows().first(); const QString filePath = index.data().toString(); for (int i = 0; i < metalink.files.files.size(); ++i) { if (metalink.files.files.at(i).name == filePath) { //the entry had not url, so do not count it anymore if (!index.data(Qt::DecorationRole).isNull()) { --m_needUrlCount; } metalink.files.files.removeAt(i); break; } } m_filesModel->removeRow(index.row()); } slotUpdateFilesButtons(); slotUpdateAssistantButtons(nullptr, m_files); } void MetalinkCreator::slotAddClicked() { //no old stored data should be used m_tempFile.clear(); fileDlg(&m_tempFile); } void MetalinkCreator::fileDlg(KGetMetalink::File *file, bool edit) { QStringList currentNames; for (int i = 0; i < m_filesModel->rowCount(); ++i) { currentNames.append(m_filesModel->index(i, 0).data().toString()); } FileDlg *fileDlg = new FileDlg(file, currentNames, m_countrySort, m_languageSort, this, edit); fileDlg->setAttribute(Qt::WA_DeleteOnClose); fileDlg->setWindowModality(Qt::ApplicationModal); fileDlg->show(); connect(fileDlg, SIGNAL(addFile()), this, SLOT(slotAddFile())); connect(fileDlg, SIGNAL(fileEdited(QString,QString)), this, SLOT(slotFileEdited(QString,QString))); } void MetalinkCreator::slotFileProperties() { const QModelIndex index = uiFiles.files->selectionModel()->selectedRows().first(); const QString fileName = index.data().toString(); //search the selected file in metalink for (int i = 0; i < metalink.files.files.count(); ++i) { if (metalink.files.files.at(i).name == fileName) { fileDlg(&metalink.files.files[i], true); break; } } } void MetalinkCreator::slotOpenDragDlg() { m_tempResources.clear(); m_tempCommonData.clear(); DragDlg *dragDlg = new DragDlg(&m_tempResources, &m_tempCommonData, m_countrySort, m_languageSort, this); dragDlg->setAttribute(Qt::WA_DeleteOnClose); dragDlg->show(); connect(dragDlg, SIGNAL(usedTypes(QStringList,bool)), this, SLOT(slotHandleDropped(QStringList,bool))); } void MetalinkCreator::slotHandleDropped(const QStringList &types, bool createPartial) { uiFiles.progressBar->setMaximum(0); uiFiles.dragDrop->show(); m_thread.setData(m_handler->takeFiles(), types, createPartial, m_tempResources, m_tempCommonData); } void MetalinkCreator::slotThreadFinished() { uiFiles.progressBar->setMaximum(10); uiFiles.dragDrop->hide(); slotUpdateAssistantButtons(nullptr, m_files); } diff --git a/ui/metalinkcreator/metalinker.cpp b/ui/metalinkcreator/metalinker.cpp index a03ea93e..47fe09b6 100644 --- a/ui/metalinkcreator/metalinker.cpp +++ b/ui/metalinkcreator/metalinker.cpp @@ -1,1421 +1,1423 @@ /*************************************************************************** * Copyright (C) 2009 Matthias Fuchs * * Copyright (C) 2012 Aish Raj Dahal * * * * 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. * * * * 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 "metalinker.h" #include -#include #include +#include +#include -#include "kget_debug.h" +#include +#include "kget_debug.h" #include "kget_version.h" -#include -#include const QString KGetMetalink::Metalink::KGET_DESCRIPTION = QStringLiteral("KGet/" KGET_VERSION_STRING); const uint KGetMetalink::Metalink::MAX_URL_PRIORITY = 999999; const uint KGetMetalink::Metalink_v3::MAX_PREFERENCE = 100;//as defined in Metalink specification 3.0 2nd edition namespace KGetMetalink { QString addaptHashType(const QString &type, bool loaded); } /** * Adapts type to the way the hash is internally stored * @param type the hash-type * @param loaded @c true if the hash has been loaded, false if it should be saved * @note metalink wants sha1 in the form "sha-1", though * the metalinker uses it internally in the form "sha1", this function * transforms it to the correct form, it is only needed internally */ QString KGetMetalink::addaptHashType(const QString &type, bool loaded) { QString t = type; if (loaded) { t.replace("sha-", "sha"); } else { t.replace("sha", "sha-"); } return t; } void KGetMetalink::DateConstruct::setData(const QDateTime &dateT, const QTime &timeZoneOff, bool negOff) { dateTime = dateT; timeZoneOffset = timeZoneOff; negativeOffset = negOff; } void KGetMetalink::DateConstruct::setData(const QString &dateConstruct) { if (dateConstruct.isEmpty()) { return; } const QString exp = "yyyy-MM-ddThh:mm:ss"; const int length = exp.length(); dateTime = QDateTime::fromString(dateConstruct.left(length), exp); if (dateTime.isValid()) { int index = dateConstruct.indexOf('+', length - 1); if (index > -1) { timeZoneOffset = QTime::fromString(dateConstruct.mid(index + 1), "hh:mm"); } else { index = dateConstruct.indexOf('-', length - 1); if (index > -1) { negativeOffset = true; timeZoneOffset = QTime::fromString(dateConstruct.mid(index + 1), "hh:mm"); } } } } bool KGetMetalink::DateConstruct::isNull() const { return dateTime.isNull(); } bool KGetMetalink::DateConstruct::isValid() const { return dateTime.isValid(); } QString KGetMetalink::DateConstruct::toString() const { QString string; if (dateTime.isValid()) { string += dateTime.toString(Qt::ISODate); } if (timeZoneOffset.isValid()) { string += (negativeOffset ? '-' : '+'); string += timeZoneOffset.toString("hh:mm"); } else if (!string.isEmpty()) { string += 'Z'; } return string; } void KGetMetalink::DateConstruct::clear() { dateTime = QDateTime(); timeZoneOffset = QTime(); } void KGetMetalink::UrlText::clear() { name.clear(); url.clear(); } void KGetMetalink::CommonData::load(const QDomElement &e) { identity = e.firstChildElement("identity").text(); version = e.firstChildElement("version").text(); description = e.firstChildElement("description").text(); logo = QUrl(e.firstChildElement("logo").text()); copyright = e.firstChildElement("copyright").text(); const QDomElement publisherElem = e.firstChildElement("publisher"); publisher.name = publisherElem.attribute("name"); publisher.url = QUrl(publisherElem.attribute("url")); for (QDomElement elemRes = e.firstChildElement("language"); !elemRes.isNull(); elemRes = elemRes.nextSiblingElement("language")) { languages << elemRes.text(); } for (QDomElement elemRes = e.firstChildElement("os"); !elemRes.isNull(); elemRes = elemRes.nextSiblingElement("os")) { oses << elemRes.text(); } } void KGetMetalink::CommonData::save(QDomElement &e) const { QDomDocument doc = e.ownerDocument(); if (!copyright.isEmpty()) { QDomElement elem = doc.createElement("copyright"); QDomText text = doc.createTextNode(copyright); elem.appendChild(text); e.appendChild(elem); } if (!description.isEmpty()) { QDomElement elem = doc.createElement("description"); QDomText text = doc.createTextNode(description); elem.appendChild(text); e.appendChild(elem); } if (!identity.isEmpty()) { QDomElement elem = doc.createElement("identity"); QDomText text = doc.createTextNode(identity); elem.appendChild(text); e.appendChild(elem); } if (!logo.isEmpty()) { QDomElement elem = doc.createElement("logo"); QDomText text = doc.createTextNode(logo.url()); elem.appendChild(text); e.appendChild(elem); } if (!publisher.isEmpty()) { QDomElement elem = doc.createElement("publisher"); elem.setAttribute("url", publisher.url.url()); elem.setAttribute("name", publisher.name); e.appendChild(elem); } if (!version.isEmpty()) { QDomElement elem = doc.createElement("version"); QDomText text = doc.createTextNode(version); elem.appendChild(text); e.appendChild(elem); } foreach (const QString &language, languages) { QDomElement elem = doc.createElement("language"); QDomText text = doc.createTextNode(language); elem.appendChild(text); e.appendChild(elem); } foreach (const QString &os, oses) { QDomElement elem = doc.createElement("os"); QDomText text = doc.createTextNode(os); elem.appendChild(text); e.appendChild(elem); } } void KGetMetalink::CommonData::clear() { identity.clear(); version.clear(); description.clear(); oses.clear(); logo.clear(); languages.clear(); publisher.clear(); copyright.clear(); } bool KGetMetalink::Metaurl::operator<(const KGetMetalink::Metaurl &other) const { return (this->priority > other.priority) || (this->priority == 0); } void KGetMetalink::Metaurl::load(const QDomElement &e) { type = e.attribute("mediatype").toLower(); priority = e.attribute("priority").toUInt(); if (priority > Metalink::MAX_URL_PRIORITY) { priority = Metalink::MAX_URL_PRIORITY; } name = e.attribute("name"); url = QUrl(e.text()); } void KGetMetalink::Metaurl::save(QDomElement &e) const { QDomDocument doc = e.ownerDocument(); QDomElement metaurl = doc.createElement("metaurl"); if (priority) { metaurl.setAttribute("priority", priority); } if (!name.isEmpty()) { metaurl.setAttribute("name", name); } metaurl.setAttribute("mediatype", type); QDomText text = doc.createTextNode(url.url()); metaurl.appendChild(text); e.appendChild(metaurl); } bool KGetMetalink::Metaurl::isValid() { return url.isValid() && !url.host().isEmpty() && !url.scheme().isEmpty() && !type.isEmpty(); } void KGetMetalink::Metaurl::clear() { type.clear(); priority = 0; name.clear(); url.clear(); } bool KGetMetalink::Url::operator<(const KGetMetalink::Url &other) const { bool smaller = (this->priority > other.priority) || ((this->priority == 0) && (other.priority != 0)); if (!smaller && (this->priority == other.priority)) { QString countryCode;// = KLocale::global()->country();//TODO: Port if (!countryCode.isEmpty()) { smaller = (other.location.toLower() == countryCode.toLower()); } } return smaller; } void KGetMetalink::Url::load(const QDomElement &e) { location = e.attribute("location").toLower(); priority = e.attribute("priority").toUInt(); if (priority > Metalink::MAX_URL_PRIORITY) { priority = Metalink::MAX_URL_PRIORITY; } url = QUrl(e.text()); } void KGetMetalink::Url::save(QDomElement &e) const { QDomDocument doc = e.ownerDocument(); QDomElement elem = doc.createElement("url"); if (priority) { elem.setAttribute("priority", priority); } if (!location.isEmpty()) { elem.setAttribute("location", location); } QDomText text = doc.createTextNode(url.url()); elem.appendChild(text); e.appendChild(elem); } bool KGetMetalink::Url::isValid() { return url.isValid() && !url.host().isEmpty() && !url.scheme().isEmpty(); } void KGetMetalink::Url::clear() { priority = 0; location.clear(); url.clear(); } void KGetMetalink::Resources::load(const QDomElement &e) { for (QDomElement elem = e.firstChildElement("url"); !elem.isNull(); elem = elem.nextSiblingElement("url")) { Url url; url.load(elem); if (url.isValid()) { urls.append(url); } } for (QDomElement elem = e.firstChildElement("metaurl"); !elem.isNull(); elem = elem.nextSiblingElement("metaurl")) { Metaurl metaurl; metaurl.load(elem); if (metaurl.isValid()) { metaurls.append(metaurl); } } } void KGetMetalink::Resources::save(QDomElement &e) const { foreach (const Metaurl &metaurl, metaurls) { metaurl.save(e); } foreach (const Url &url, urls) { url.save(e); } } void KGetMetalink::Resources::clear() { urls.clear(); metaurls.clear(); } void KGetMetalink::Pieces::load(const QDomElement &e) { type = addaptHashType(e.attribute("type"), true); length = e.attribute("length").toULongLong(); QDomNodeList hashesList = e.elementsByTagName("hash"); for (int i = 0; i < hashesList.count(); ++i) { QDomElement element = hashesList.at(i).toElement(); hashes.append(element.text()); } } void KGetMetalink::Pieces::save(QDomElement &e) const { QDomDocument doc = e.ownerDocument(); QDomElement pieces = doc.createElement("pieces"); pieces.setAttribute("type", addaptHashType(type, false)); pieces.setAttribute("length", length); for (int i = 0; i < hashes.size(); ++i) { QDomElement hash = doc.createElement("hash"); QDomText text = doc.createTextNode(hashes.at(i)); hash.appendChild(text); pieces.appendChild(hash); } e.appendChild(pieces); } void KGetMetalink::Pieces::clear() { type.clear(); length = 0; hashes.clear(); } void KGetMetalink::Verification::load(const QDomElement &e) { for (QDomElement elem = e.firstChildElement("hash"); !elem.isNull(); elem = elem.nextSiblingElement("hash")) { QString type = elem.attribute("type"); const QString hash = elem.text(); if (!type.isEmpty() && !hash.isEmpty()) { type = addaptHashType(type, true); hashes[type] = hash; } } for (QDomElement elem = e.firstChildElement("pieces"); !elem.isNull(); elem = elem.nextSiblingElement("pieces")) { Pieces piecesItem; piecesItem.load(elem); pieces.append(piecesItem); } for (QDomElement elem = e.firstChildElement("signature"); !elem.isNull(); elem = elem.nextSiblingElement("signature")) { QString type = elem.attribute("mediatype"); if (type == "application/pgp-signature") {//FIXME with 4.5 make it handle signatures by default with mime-type type = "pgp"; } const QString siganture = elem.text(); if (!type.isEmpty() && !siganture.isEmpty()) { signatures[type] = siganture; } } } void KGetMetalink::Verification::save(QDomElement &e) const { QDomDocument doc = e.ownerDocument(); QHash::const_iterator it; QHash::const_iterator itEnd = hashes.constEnd(); for (it = hashes.constBegin(); it != itEnd; ++it) { QDomElement hash = doc.createElement("hash"); hash.setAttribute("type", addaptHashType(it.key(), false)); QDomText text = doc.createTextNode(it.value()); hash.appendChild(text); e.appendChild(hash); } foreach (const Pieces &item, pieces) { item.save(e); } itEnd = signatures.constEnd(); for (it = signatures.constBegin(); it != itEnd; ++it) { QString type = it.key(); if (type == "pgp") {//FIXME with 4.5 make it handle signatures by default with mime-type type = "application/pgp-signature"; } QDomElement hash = doc.createElement("signature"); hash.setAttribute("mediatype", type); QDomText text = doc.createTextNode(it.value()); hash.appendChild(text); e.appendChild(hash); } } void KGetMetalink::Verification::clear() { hashes.clear(); pieces.clear(); } bool KGetMetalink::File::isValid() const { return isValidNameAttribute() && resources.isValid(); } void KGetMetalink::File::load(const QDomElement &e) { data.load(e); name = QUrl::fromPercentEncoding(e.attribute("name").toAscii()); size = e.firstChildElement("size").text().toULongLong(); verification.load(e); resources.load(e); } void KGetMetalink::File::save(QDomElement &e) const { if (isValid()) { QDomDocument doc = e.ownerDocument(); QDomElement file = doc.createElement("file"); file.setAttribute("name", name); if (size) { QDomElement elem = doc.createElement("size"); QDomText text = doc.createTextNode(QString::number(size)); elem.appendChild(text); file.appendChild(elem); } data.save(file); resources.save(file); verification.save(file); e.appendChild(file); } } void KGetMetalink::File::clear() { name.clear(); verification.clear(); size = 0; data.clear(); resources.clear(); } bool KGetMetalink::File::isValidNameAttribute() const { if (name.isEmpty()) { qCCritical(KGET_DEBUG) << "Name attribute of Metalink::File is empty."; return false; } if (name.endsWith('/')) { qCCritical(KGET_DEBUG) << "Name attribute of Metalink::File does not contain a file name:" << name; return false; } const QStringList components = name.split('/'); if (name.startsWith('/') || components.contains("..") || components.contains(".")) { qCCritical(KGET_DEBUG) << "Name attribute of Metalink::File contains directory traversal directives:" << name; return false; } return true; } bool KGetMetalink::Files::isValid() const { if (files.isEmpty()) { return false; } QStringList fileNames; foreach (const File &file, files) { fileNames << file.name; if (!file.isValid()) { return false; } } //The value of name must be unique for each file while (!fileNames.isEmpty()) { const QString fileName = fileNames.takeFirst(); if (fileNames.contains(fileName)) { qCCritical(KGET_DEBUG) << "Metalink::File name" << fileName << "exists multiple times."; return false; } } return true; } void KGetMetalink::Files::load(const QDomElement &e) { for (QDomElement elem = e.firstChildElement("file"); !elem.isNull(); elem = elem.nextSiblingElement("file")) { File file; file.load(elem); files.append(file); } } void KGetMetalink::Files::save(QDomElement &e) const { if (e.isNull()) { return; } foreach (const File &file, files) { file.save(e); } } void KGetMetalink::Files::clear() { files.clear(); } bool KGetMetalink::Metalink::isValid() const { return files.isValid(); } void KGetMetalink::Metalink::load(const QDomElement &e) { QDomDocument doc = e.ownerDocument(); const QDomElement metalink = doc.firstChildElement("metalink"); xmlns = metalink.attribute("xmlns"); generator = metalink.firstChildElement("generator").text(); updated.setData(metalink.firstChildElement("updated").text()); published.setData(metalink.firstChildElement("published").text()); updated.setData(metalink.firstChildElement("updated").text()); const QDomElement originElem = metalink.firstChildElement("origin"); origin = QUrl(metalink.firstChildElement("origin").text()); if (originElem.hasAttribute("dynamic")) { bool worked = false; dynamic = originElem.attribute("dynamic").toInt(&worked); if (!worked) { dynamic = (originElem.attribute("dynamic") == "true"); } } files.load(e); } QDomDocument KGetMetalink::Metalink::save() const { QDomDocument doc; QDomProcessingInstruction header = doc.createProcessingInstruction("xml", "version=\"1.0\" encoding=\"UTF-8\""); doc.appendChild(header); QDomElement metalink = doc.createElement("metalink"); metalink.setAttribute("xmlns", "urn:ietf:params:xml:ns:metalink"); //the xmlns value is ignored, instead the data format described in the specification is always used QDomElement elem = doc.createElement("generator"); QDomText text = doc.createTextNode(Metalink::KGET_DESCRIPTION); //the set generator is ignored, instead when saving KGET is always used elem.appendChild(text); metalink.appendChild(elem); if (!origin.isEmpty()) { QDomElement elem = doc.createElement("origin"); QDomText text = doc.createTextNode(origin.url()); elem.appendChild(text); if (dynamic) { elem.setAttribute("dynamic", "true"); } metalink.appendChild(elem); } if (published.isValid()) { QDomElement elem = doc.createElement("published"); QDomText text = doc.createTextNode(published.toString()); elem.appendChild(text); metalink.appendChild(elem); } if (updated.isValid()) { QDomElement elem = doc.createElement("updated"); QDomText text = doc.createTextNode(updated.toString()); elem.appendChild(text); metalink.appendChild(elem); } files.save(metalink); doc.appendChild(metalink); return doc; } void KGetMetalink::Metalink::clear() { dynamic = false; xmlns.clear(); published.clear(); origin.clear(); generator.clear(); updated.clear(); files.clear(); } KGetMetalink::Metalink_v3::Metalink_v3() { } KGetMetalink::Metalink KGetMetalink::Metalink_v3::metalink() { return m_metalink; } void KGetMetalink::Metalink_v3::setMetalink(const KGetMetalink::Metalink &metalink) { m_metalink = metalink; } void KGetMetalink::Metalink_v3::load(const QDomElement &e) { QDomDocument doc = e.ownerDocument(); const QDomElement metalinkDom = doc.firstChildElement("metalink"); m_metalink.dynamic = (metalinkDom.attribute("type") == "dynamic"); m_metalink.origin = QUrl(metalinkDom.attribute("origin")); m_metalink.generator = metalinkDom.attribute("generator"); m_metalink.published = parseDateConstruct(metalinkDom.attribute("pubdate")); m_metalink.updated = parseDateConstruct(metalinkDom.attribute("refreshdate")); parseFiles(metalinkDom); } void KGetMetalink::Metalink_v3::parseFiles(const QDomElement &e) { //here we assume that the CommonData set in metalink is for every file in the metalink CommonData data; data = parseCommonData(e); const QDomElement filesElem = e.firstChildElement("files"); CommonData filesData = parseCommonData(filesElem); inheritCommonData(data, &filesData); for (QDomElement elem = filesElem.firstChildElement("file"); !elem.isNull(); elem = elem.nextSiblingElement("file")) { File file; file.name = QUrl::fromPercentEncoding(elem.attribute("name").toAscii()); file.size = elem.firstChildElement("size").text().toULongLong(); file.data = parseCommonData(elem); inheritCommonData(filesData, &file.data); file.resources = parseResources(elem); //load the verification information QDomElement veriE = elem.firstChildElement("verification"); for (QDomElement elemVer = veriE.firstChildElement("hash"); !elemVer.isNull(); elemVer = elemVer.nextSiblingElement("hash")) { QString type = elemVer.attribute("type"); QString hash = elemVer.text(); if (!type.isEmpty() && !hash.isEmpty()) { type = addaptHashType(type, true); file.verification.hashes[type] = hash; } } for (QDomElement elemVer = veriE.firstChildElement("pieces"); !elemVer.isNull(); elemVer = elemVer.nextSiblingElement("pieces")) { Pieces piecesItem; piecesItem.load(elemVer); file.verification.pieces.append(piecesItem); } for (QDomElement elemVer = veriE.firstChildElement("signature"); !elemVer.isNull(); elemVer = elemVer.nextSiblingElement("signature")) { const QString type = elemVer.attribute("type"); const QString signature = elemVer.text(); if (!type.isEmpty() && !signature.isEmpty()) { file.verification.signatures[type] = signature; } } m_metalink.files.files.append(file); } } KGetMetalink::CommonData KGetMetalink::Metalink_v3::parseCommonData(const QDomElement &e) { CommonData data; data.load(e); const QDomElement publisherElem = e.firstChildElement("publisher"); data.publisher.name = publisherElem.firstChildElement("name").text(); data.publisher.url = QUrl(publisherElem.firstChildElement("url").text()); return data; } void KGetMetalink::Metalink_v3::inheritCommonData(const KGetMetalink::CommonData &ancestor, KGetMetalink::CommonData *inheritor) { if (!inheritor) { return; } //ensure that inheritance works if (inheritor->identity.isEmpty()) { inheritor->identity = ancestor.identity; } if (inheritor->version.isEmpty()) { inheritor->version = ancestor.version; } if (inheritor->description.isEmpty()) { inheritor->description = ancestor.description; } if (inheritor->oses.isEmpty()) { inheritor->oses = ancestor.oses; } if (inheritor->logo.isEmpty()) { inheritor->logo = ancestor.logo; } if (inheritor->languages.isEmpty()) { inheritor->languages = ancestor.languages; } if (inheritor->copyright.isEmpty()) { inheritor->copyright = ancestor.copyright; } if (inheritor->publisher.isEmpty()) { inheritor->publisher = ancestor.publisher; } } KGetMetalink::Resources KGetMetalink::Metalink_v3::parseResources(const QDomElement &e) { Resources resources; QDomElement res = e.firstChildElement("resources"); for (QDomElement elemRes = res.firstChildElement("url"); !elemRes.isNull(); elemRes = elemRes.nextSiblingElement("url")) { const QString location = elemRes.attribute("location").toLower(); uint preference = elemRes.attribute("preference").toUInt(); //the maximum preference we use is MAX_PREFERENCE if (preference > MAX_PREFERENCE) { preference = MAX_PREFERENCE; } const int priority = MAX_PREFERENCE - preference + 1;//convert old preference to new priority const QUrl link = QUrl(elemRes.text()); QString type; if (link.fileName().endsWith(QLatin1String(".torrent"))) { type = "torrent"; } if (type.isEmpty()) { Url url; if (preference) { url.priority = priority; } url.location = location; url.url = link; if (url.isValid()) { resources.urls.append(url); } } else { //it might be a metaurl Metaurl metaurl; if (preference) { metaurl.priority = priority; } metaurl.url = link; metaurl.type = type; if (metaurl.isValid()) { resources.metaurls.append(metaurl); } } } return resources; } KGetMetalink::DateConstruct KGetMetalink::Metalink_v3::parseDateConstruct(const QString &data) { DateConstruct dateConstruct; if (data.isEmpty()){ return dateConstruct; } qCDebug(KGET_DEBUG) << "Parsing" << data; QString temp = data; QDateTime dateTime; QTime timeZoneOffset; //Date according to RFC 822, the year with four characters preferred //e.g.: "Mon, 15 May 2006 00:00:01 GMT", "Fri, 01 Apr 2009 00:00:01 +1030" //find the date const QString weekdayExp = "ddd, "; const bool weekdayIncluded = (temp.indexOf(',') == 3); int startPosition = (weekdayIncluded ? weekdayExp.length() : 0); const QString dayMonthExp = "dd MMM "; const QString yearExp = "yy"; QString exp = dayMonthExp + yearExp + yearExp; int length = exp.length(); QLocale locale = QLocale::c(); QDate date = locale.toDate(temp.mid(startPosition, length), exp); if (!date.isValid()) { exp = dayMonthExp + yearExp; length = exp.length(); date = locale.toDate(temp.mid(startPosition, length), exp); if (!date.isValid()) { return dateConstruct; } } //find the time dateTime.setDate(date); temp = temp.mid(startPosition); temp = temp.mid(length + 1);//also remove the space const QString hourExp = "hh"; const QString minuteExp = "mm"; const QString secondExp = "ss"; exp = hourExp + ':' + minuteExp + ':' + secondExp; length = exp.length(); QTime time = QTime::fromString(temp.left(length), exp); if (!time.isValid()) { exp = hourExp + ':' + minuteExp; length = exp.length(); time = QTime::fromString(temp.left(length), exp); if (!time.isValid()) { return dateConstruct; } } dateTime.setTime(time); //find the offset temp = temp.mid(length + 1);//also remove the space bool negativeOffset = false; if (temp.length() == 3) { //e.g. GMT - KTimeZone timeZone = KSystemTimeZones::readZone(temp); + const auto timeZone = QTimeZone(temp.toUtf8()); if (timeZone.isValid()) { - int offset = timeZone.currentOffset(); + QDateTime a = QDateTime::currentDateTime(); + a.setTimeZone(timeZone); + int offset = a.offsetFromUtc(); negativeOffset = (offset < 0); timeZoneOffset = QTime(0, 0, 0); timeZoneOffset = timeZoneOffset.addSecs(qAbs(offset)); } } else if (temp.length() == 5) { //e.g. +1030 negativeOffset = (temp[0] == '-'); timeZoneOffset = QTime::fromString(temp.mid(1,4), "hhmm"); } dateConstruct.setData(dateTime, timeZoneOffset, negativeOffset); return dateConstruct; } QDomDocument KGetMetalink::Metalink_v3::save() const { QDomDocument doc; QDomProcessingInstruction header = doc.createProcessingInstruction("xml", "version=\"1.0\" encoding=\"UTF-8\""); doc.appendChild(header); QDomElement metalink = doc.createElement("metalink"); metalink.setAttribute("xmlns", "http://www.metalinker.org/"); metalink.setAttribute("version", "3.0"); metalink.setAttribute("type", (m_metalink.dynamic ? "dynamic" : "static")); metalink.setAttribute("generator", Metalink::KGET_DESCRIPTION); //the set generator is ignored, instead when saving KGET is always used if (m_metalink.published.isValid()) { metalink.setAttribute("pubdate", dateConstructToString(m_metalink.published)); } if (m_metalink.updated.isValid()) { metalink.setAttribute("refreshdate", dateConstructToString(m_metalink.updated)); } if (!m_metalink.origin.isEmpty()) { metalink.setAttribute("origin", m_metalink.origin.url()); } saveFiles(metalink); doc.appendChild(metalink); return doc; } void KGetMetalink::Metalink_v3::saveFiles(QDomElement &e) const { QDomDocument doc = e.ownerDocument(); QDomElement filesElem = doc.createElement("files"); foreach (const File &file, m_metalink.files.files) { QDomElement elem = doc.createElement("file"); elem.setAttribute("name", file.name); QDomElement size = doc.createElement("size"); QDomText text = doc.createTextNode(QString::number(file.size)); size.appendChild(text); elem.appendChild(size); saveCommonData(file.data, elem); saveResources(file.resources, elem); saveVerification(file.verification, elem); filesElem.appendChild(elem); } e.appendChild(filesElem); } void KGetMetalink::Metalink_v3::saveResources(const Resources &resources, QDomElement &e) const { QDomDocument doc = e.ownerDocument(); QDomElement res = doc.createElement("resources"); foreach (const Url &url, resources.urls) { QDomElement elem = doc.createElement("url"); const uint priority = url.priority; if (priority) { int preference = MAX_PREFERENCE - priority + 1; if (preference <= 0) { preference = 1;//HACK if priority is larger MAX_PREFERENCE makes it 1 } elem.setAttribute("preference", preference); } if (!url.location.isEmpty()) { elem.setAttribute("location", url.location); } QDomText text = doc.createTextNode(url.url.url()); elem.appendChild(text); res.appendChild(elem); } foreach (const Metaurl &metaurl, resources.metaurls) { if (metaurl.type == "torrent") { QDomElement elem = doc.createElement("url"); elem.setAttribute("type", "bittorrent"); const uint priority = metaurl.priority; if (priority) { int preference = MAX_PREFERENCE - priority + 1; if (preference <= 0) { preference = 1;//HACK if priority is larger MAX_PREFERENCE makes it 1 } elem.setAttribute("preference", preference); } QDomText text = doc.createTextNode(metaurl.url.url()); elem.appendChild(text); res.appendChild(elem); } } e.appendChild(res); } void KGetMetalink::Metalink_v3::saveVerification(const KGetMetalink::Verification &verification, QDomElement &e) const { QDomDocument doc = e.ownerDocument(); QDomElement veri = doc.createElement("verification"); QHash::const_iterator it; QHash::const_iterator itEnd = verification.hashes.constEnd(); for (it = verification.hashes.constBegin(); it != itEnd; ++it) { QDomElement elem = doc.createElement("hash"); elem.setAttribute("type", it.key()); QDomText text = doc.createTextNode(it.value()); elem.appendChild(text); veri.appendChild(elem); } foreach (const Pieces &pieces, verification.pieces) { QDomElement elem = doc.createElement("pieces"); elem.setAttribute("type", pieces.type); elem.setAttribute("length", QString::number(pieces.length)); for (int i = 0; i < pieces.hashes.count(); ++i) { QDomElement hash = doc.createElement("hash"); hash.setAttribute("piece", i); QDomText text = doc.createTextNode(pieces.hashes.at(i)); hash.appendChild(text); elem.appendChild(hash); } veri.appendChild(elem); } itEnd = verification.signatures.constEnd(); for (it = verification.signatures.constBegin(); it != itEnd; ++it) { QDomElement elem = doc.createElement("signature"); elem.setAttribute("type", it.key()); QDomText text = doc.createTextNode(it.value()); elem.appendChild(text); veri.appendChild(elem); } e.appendChild(veri); } void KGetMetalink::Metalink_v3::saveCommonData(const KGetMetalink::CommonData &data, QDomElement &e) const { QDomDocument doc = e.ownerDocument(); CommonData commonData = data; if (!commonData.publisher.isEmpty()) { QDomElement elem = doc.createElement("publisher"); QDomElement elemName = doc.createElement("name"); QDomElement elemUrl = doc.createElement("url"); QDomText text = doc.createTextNode(commonData.publisher.name); elemName.appendChild(text); elem.appendChild(elemName); text = doc.createTextNode(commonData.publisher.url.url()); elemUrl.appendChild(text); elem.appendChild(elemUrl); e.appendChild(elem); commonData.publisher.clear(); } if (commonData.oses.count() > 1) {//only one OS can be set in 3.0 commonData.oses.clear(); } commonData.save(e); } QString KGetMetalink::Metalink_v3::dateConstructToString(const KGetMetalink::DateConstruct &date) const { QString dateString; if (!date.isValid()) { return dateString; } QLocale locale = QLocale::c(); //"Fri, 01 Apr 2009 00:00:01 +1030" dateString += locale.toString(date.dateTime, "ddd, dd MMM yyyy hh:mm:ss "); if (date.timeZoneOffset.isValid()) { dateString += (date.negativeOffset ? '-' : '+'); dateString += date.timeZoneOffset.toString("hhmm"); } else { dateString += "+0000"; } return dateString; } bool KGetMetalink::HandleMetalink::load(const QUrl &destination, KGetMetalink::Metalink *metalink) { QFile file(destination.toLocalFile()); if (!file.open(QIODevice::ReadOnly)) { return false; } QDomDocument doc; if (!doc.setContent(&file)) { file.close(); return false; } file.close(); QDomElement root = doc.documentElement(); if (root.attribute("xmlns") == "urn:ietf:params:xml:ns:metalink") { metalink->load(root); return true; } else if ((root.attribute("xmlns") == "http://www.metalinker.org/") || (root.attribute("version") == "3.0")) { Metalink_v3 metalink_v3; metalink_v3.load(root); *metalink = metalink_v3.metalink(); return true; } return false; } bool KGetMetalink::HandleMetalink::load(const QByteArray &data, KGetMetalink::Metalink *metalink) { if (data.isNull()) { return false; } QDomDocument doc; if (!doc.setContent(data)) { return false; } metalink->clear(); QDomElement root = doc.documentElement(); if (root.attribute("xmlns") == "urn:ietf:params:xml:ns:metalink") { metalink->load(root); return true; } else if ((root.attribute("xmlns") == "http://www.metalinker.org/") || (root.attribute("version") == "3.0")) { Metalink_v3 metalink_v3; metalink_v3.load(root); *metalink = metalink_v3.metalink(); return true; } return false; } bool KGetMetalink::HandleMetalink::save(const QUrl &destination, KGetMetalink::Metalink *metalink) { QFile file(destination.toLocalFile()); if (!file.open(QIODevice::WriteOnly)) { return false; } QDomDocument doc; QString fileName = destination.fileName(); if (fileName.endsWith(QLatin1String("meta4"))) { doc = metalink->save(); } else if (fileName.endsWith(QLatin1String("metalink"))) { Metalink_v3 metalink_v3; metalink_v3.setMetalink(*metalink); doc = metalink_v3.save(); } else { file.close(); return false; } QTextStream stream(&file); doc.save(stream, 2); file.close(); return true; } KGetMetalink::MetalinkHttpParser::~MetalinkHttpParser() { } QString* KGetMetalink::MetalinkHttpParser::getEtag() { return &m_EtagValue; } void KGetMetalink::MetalinkHttpParser::checkMetalinkHttp() { if (!m_Url.isValid()) { qDebug() << "Url not valid"; return; } KIO::TransferJob *job; job = KIO::get(m_Url, KIO::NoReload, KIO::HideProgressInfo); job->addMetaData("PropagateHttpHeader", "true"); job->setRedirectionHandlingEnabled(false); connect(job, SIGNAL(result(KJob*)), this, SLOT(slotHeaderResult(KJob*))); // Finished connect(job, SIGNAL(redirection(KIO::Job*,QUrl)), this, SLOT(slotRedirection(KIO::Job*,QUrl))); // Redirection connect(job,SIGNAL(mimetype(KIO::Job*,QString)),this,SLOT(detectMime(KIO::Job*,QString))); // Mime detection. qDebug() << " Verifying Metalink/HTTP Status" ; m_loop.exec(); } void KGetMetalink::MetalinkHttpParser::detectMime(KIO::Job *job, const QString &type) { qDebug() << "Mime Type: " << type ; job->kill(); m_loop.exit(); } void KGetMetalink::MetalinkHttpParser::slotHeaderResult(KJob* kjob) { KIO::Job* job = qobject_cast(kjob); const QString httpHeaders = job ? job->queryMetaData("HTTP-Headers") : QString(); parseHeaders(httpHeaders); setMetalinkHSatus(); // Handle the redirection... (Comment out if not desired) if (m_redirectionUrl.isValid()) { m_Url = m_redirectionUrl; m_redirectionUrl = QUrl(); checkMetalinkHttp(); } if (m_loop.isRunning()) m_loop.exit(); } void KGetMetalink::MetalinkHttpParser::slotRedirection(KIO::Job *job, const QUrl & url) { Q_UNUSED(job) m_redirectionUrl = url; } bool KGetMetalink::MetalinkHttpParser::isMetalinkHttp() { if (m_MetalinkHSatus) { qDebug() << "Metalink Http detected" ; } else { qDebug() << "No Metalink HTTP response found" ; } return m_MetalinkHSatus; } void KGetMetalink::MetalinkHttpParser::parseHeaders(const QString &httpHeader) { QString trimedHeader = httpHeader.mid(httpHeader.indexOf('\n') + 1).trimmed(); foreach(QString line, trimedHeader.split('\n')) { int colon = line.indexOf(':'); QString headerName = line.left(colon).trimmed(); QString headerValue = line.mid(colon + 1).trimmed(); m_headerInfo.insertMulti(headerName, headerValue); } m_EtagValue = m_headerInfo.value("ETag"); } void KGetMetalink::MetalinkHttpParser::setMetalinkHSatus() { bool linkStatus, digestStatus; linkStatus = digestStatus = false; if (m_headerInfo.contains("link")) { QList linkValues = m_headerInfo.values("link"); foreach(QString linkVal, linkValues) { if (linkVal.contains("rel=duplicate")) { linkStatus = true; break; } } } if (m_headerInfo.contains("digest")) { QList digestValues = m_headerInfo.values("digest"); foreach(QString digestVal, digestValues) { if (digestVal.contains("sha-256", Qt::CaseInsensitive)) { digestStatus = true; break; } } } if ((linkStatus) && (digestStatus)) { m_MetalinkHSatus = true; } } QUrl KGetMetalink::MetalinkHttpParser::getUrl() { return m_Url; } QMultiMap* KGetMetalink::MetalinkHttpParser::getHeaderInfo() { return & m_headerInfo; } KGetMetalink::HttpLinkHeader::HttpLinkHeader(const QString &headerLine) : pref(false) { parseHeaderLine(headerLine); } bool KGetMetalink::HttpLinkHeader::operator<(const HttpLinkHeader &other) const { return depth < other.depth; } void KGetMetalink::HttpLinkHeader::parseHeaderLine(const QString &line) { url = line.mid(line.indexOf("<") + 1,line.indexOf(">") -1).trimmed(); const QList attribList = line.split(";"); foreach (const QString str, attribList) { const QString attribId = str.mid(0,str.indexOf("=")).trimmed(); const QString attribValue = str.mid(str.indexOf("=")+1).trimmed(); if (attribId == "rel") { reltype = attribValue; } else if (attribId == "depth") { depth = attribValue.toInt(); } else if (attribId == "geo") { geo = attribValue; } else if (attribId == "pref") { pref = true; } else if (attribId == "pri") { priority = attribValue.toUInt(); } else if (attribId == "type") { type = attribValue; } else if (attribId == "name") { name = attribValue; } } } diff --git a/ui/mirror/mirroradddlg.ui b/ui/mirror/mirroradddlg.ui index c1327f52..76acf0ac 100644 --- a/ui/mirror/mirroradddlg.ui +++ b/ui/mirror/mirroradddlg.ui @@ -1,124 +1,119 @@ MirrorAddDlg 0 0 481 187 Mirror: Number of connections: Enter a URL true 1 20 Priority: Optional: The priority of the mirror, 1 highest 999999 lowest. 0 0 999999 not specified Location: - + Qt::Vertical 20 40 QDialogButtonBox::Cancel|QDialogButtonBox::Yes KLineEdit QLineEdit -
klineedit.h
-
- - KComboBox - QComboBox -
kcombobox.h
+
KLineEdit
diff --git a/ui/mirror/mirrormodel.cpp b/ui/mirror/mirrormodel.cpp index 63fb7a7d..18c87c79 100644 --- a/ui/mirror/mirrormodel.cpp +++ b/ui/mirror/mirrormodel.cpp @@ -1,517 +1,517 @@ /*************************************************************************** * 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. * * * * 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 "mirrormodel.h" +#include #include +#include -#include #include -#include -#include +#include MirrorDelegate::MirrorDelegate(QObject *parent) : QStyledItemDelegate(parent), m_countrySort(nullptr) { } MirrorDelegate::MirrorDelegate(QSortFilterProxyModel *countrySort, QObject *parent) : QStyledItemDelegate(parent), m_countrySort(countrySort) { } QWidget *MirrorDelegate::createEditor(QWidget *parent, const QStyleOptionViewItem &option, const QModelIndex &index) const { Q_UNUSED(option) if (index.isValid()) { if (index.column() == MirrorItem::Url) { KLineEdit *line = new KLineEdit(parent); return line; } else if (index.column() == MirrorItem::Connections) { QSpinBox *numConnections = new QSpinBox(parent); numConnections->setRange(0, 20); return numConnections; } else if (index.column() == MirrorItem::Priority) { QSpinBox *priority = new QSpinBox(parent); priority->setRange(0, 999999); return priority; } else if (index.column() == MirrorItem::Country) { if (m_countrySort) { - KComboBox *countrySort = new KComboBox(parent); + QComboBox *countrySort = new QComboBox(parent); countrySort->setModel(m_countrySort); return countrySort; } } } return nullptr; } void MirrorDelegate::setEditorData(QWidget *editor, const QModelIndex &index) const { if (index.isValid() && editor) { if (index.column() == MirrorItem::Url) { KLineEdit *line = static_cast(editor); const QUrl url = index.data(Qt::EditRole).toUrl(); line->setUrl(url); } else if (index.column() == MirrorItem::Connections) { QSpinBox *numConnections = static_cast(editor); const int num = index.data(Qt::EditRole).toInt(); numConnections->setValue(num); } else if (index.column() == MirrorItem::Priority) { QSpinBox *priority = static_cast(editor); const int num = index.data(Qt::EditRole).toInt(); priority->setValue(num); } else if (index.column() == MirrorItem::Country) { - KComboBox *countrySort = static_cast(editor); + QComboBox *countrySort = static_cast(editor); const QString countryCode = index.data(Qt::EditRole).toString(); const int indexCountrySort = countrySort->findData(countryCode); countrySort->setCurrentIndex(indexCountrySort); } } } void MirrorDelegate::setModelData(QWidget *editor, QAbstractItemModel *model, const QModelIndex &index) const { if (index.isValid() && editor && model) { if (index.column() == MirrorItem::Url) { KLineEdit *line = static_cast(editor); if (!line->text().isEmpty()) { model->setData(index, line->text()); } } else if (index.column() == MirrorItem::Connections) { QSpinBox *numConnections = static_cast(editor); model->setData(index, numConnections->value()); } else if (index.column() == MirrorItem::Priority) { QSpinBox *priority = static_cast(editor); model->setData(index, priority->value()); } else if (index.column() == MirrorItem::Country) { - KComboBox *countrySort = static_cast(editor); + QComboBox *countrySort = static_cast(editor); const QString countryCode = countrySort->itemData(countrySort->currentIndex()).toString(); model->setData(index, countryCode); } } } void MirrorDelegate::updateEditorGeometry(QWidget *editor, const QStyleOptionViewItem &option, const QModelIndex &index) const { Q_UNUSED(index) editor->setGeometry(option.rect); } QSize MirrorDelegate::sizeHint(const QStyleOptionViewItem &option, const QModelIndex &index) const { //make the sizeHint a little bit nicer to have more beautiful editors QSize hint; hint.setWidth(QStyledItemDelegate::sizeHint(option, index).width()); hint.setHeight(option.fontMetrics.height() + 7); return hint; } MirrorProxyModel::MirrorProxyModel(QObject *parent) : QSortFilterProxyModel(parent) { } bool MirrorProxyModel::lessThan(const QModelIndex &left, const QModelIndex &right) const { if (left.column() == MirrorItem::Used) { const int leftData = sourceModel()->data(left, Qt::CheckStateRole).toInt(); const int rightData = sourceModel()->data(right, Qt::CheckStateRole).toInt(); return leftData < rightData; } else if (left.column() == MirrorItem::Priority) { const int leftData = sourceModel()->data(left, Qt::UserRole).toInt(); const int rightData = sourceModel()->data(right, Qt::UserRole).toInt(); return (!leftData ? true : (leftData > rightData) && rightData);//0 is always smallest, otherwise larger is smaller } return QSortFilterProxyModel::lessThan(left, right); } MirrorItem::MirrorItem() : m_checked(Qt::Unchecked), m_numConnections(0), m_priority(0) { } QVariant MirrorItem::data(int column, int role) const { if (column == MirrorItem::Used) { if (role == Qt::CheckStateRole) { return m_checked; } } else if (column == MirrorItem::Url) { if (role == Qt::DisplayRole) { return m_url.toString(); } else if ((role == Qt::UserRole) || (role == Qt::EditRole)) { return QVariant(m_url); } } else if (column == MirrorItem::Connections) { if (role == Qt::DisplayRole) { if (m_numConnections) { return m_numConnections; } else { return i18n("not specified"); } } else if ((role == Qt::EditRole) || (role == Qt::UserRole)) { return m_numConnections; } } else if (column == MirrorItem::Priority) { if (role == Qt::DisplayRole) { if (m_priority) { return m_priority; } else { return i18n("not specified"); } } else if ((role == Qt::EditRole) || (role == Qt::UserRole)) { return m_priority; } } else if (column == MirrorItem::Country) { if (role == Qt::DisplayRole) { return m_countryName; } else if (role == Qt::DecorationRole) { return m_countryFlag; } else if ((role == Qt::UserRole) || (role == Qt::EditRole)) { return m_countryCode; } } return QVariant(); } Qt::ItemFlags MirrorItem::flags(int column) const { Qt::ItemFlags flags = Qt::ItemIsEnabled | Qt::ItemIsSelectable; if (column == MirrorItem::Used) { flags |= Qt::ItemIsUserCheckable; } else if (column == MirrorItem::Url) { flags |= Qt::ItemIsEditable; } else if (column == MirrorItem::Connections) { flags |= Qt::ItemIsEditable; } else if (column == MirrorItem::Priority) { flags |= Qt::ItemIsEditable; } else if (column == MirrorItem::Country) { flags |= Qt::ItemIsEditable; } return flags; } bool MirrorItem::setData(int column, const QVariant &value, int role) { if ((column == MirrorItem::Used) && (role == Qt::CheckStateRole)) { m_checked = static_cast(value.toInt()); return true; } else if ((column == MirrorItem::Url) && (role == Qt::EditRole)) { QUrl url; if (value.type() == QVariant::Url) { url = QUrl(value.toUrl()); } else if (value.type() == QVariant::String) { url = QUrl(value.toString()); } if (!url.isEmpty()) { m_url = url; return true; } } else if ((column == MirrorItem::Connections) && (role == Qt::EditRole)) { m_numConnections = value.toInt(); return true; } else if ((column == MirrorItem::Priority) && (role == Qt::EditRole)) { m_priority = value.toInt(); return true; } else if ((column == MirrorItem::Country) && (role == Qt::EditRole)) { m_countryCode = value.toString(); - m_countryName = KLocale::global()->countryCodeToName(m_countryCode); - +// m_countryName = KLocale::global()->countryCodeToName(m_countryCode); +/* if (!m_countryName.isEmpty()) { QString path = QStandardPaths::locate(QStandardPaths::GenericDataLocation, QLatin1String("locale/") + QString::fromLatin1("l10n/%1/flag.png").arg(m_countryCode)); if (path.isEmpty()) { m_countryFlag = QIcon(); } else { m_countryFlag = QIcon::fromTheme(path); } } else { m_countryFlag = QIcon(); } - return true; + return true;*/ } return false; } MirrorModel::MirrorModel(QObject *parent) : QAbstractTableModel(parent) { } MirrorModel::~MirrorModel() { qDeleteAll(m_data); } int MirrorModel::rowCount(const QModelIndex &index) const { if (!index.isValid()) { return m_data.count(); } else { return 0; } } int MirrorModel::columnCount(const QModelIndex &index) const { if (index.isValid()) { return 0; } return 5; } QVariant MirrorModel::headerData(int section, Qt::Orientation orientation, int role) const { if (orientation == Qt::Vertical) { return QVariant(); } if ((section == MirrorItem::Url) && (role == Qt::DisplayRole)) { return i18nc("Mirror as in server, in url", "Mirror"); } else if (section == MirrorItem::Priority) { if (role == Qt::DisplayRole) { return i18nc("The priority of the mirror", "Priority"); } else if (role == Qt::DecorationRole) { return QIcon::fromTheme("games-highscores"); } } else if ((section == MirrorItem::Connections) && (role == Qt::DisplayRole)) { return i18nc("Number of paralell connections to the mirror", "Connections"); } else if ((section == MirrorItem::Country) && (role == Qt::DisplayRole)) { return i18nc("Location = country", "Location"); } return QVariant(); } QVariant MirrorModel::data(const QModelIndex &index, int role) const { if (!index.isValid()) { return QVariant(); } return m_data.at(index.row())->data(index.column(), role); } Qt::ItemFlags MirrorModel::flags(const QModelIndex &index) const { if (!index.isValid()) { return 0; } return m_data.at(index.row())->flags(index.column()); } bool MirrorModel::setData(const QModelIndex &index, const QVariant &value, int role) { if (!index.isValid()) { return false; } const bool changed = m_data.at(index.row())->setData(index.column(), value, role); if (changed) { emit dataChanged(index, index); } return changed; } bool MirrorModel::removeRows(int row, int count, const QModelIndex &parent) { if (parent.isValid() || (row < 0) || (count < 1) || (row + count > rowCount())) { return false; } beginRemoveRows(parent, row, row + count - 1); while (count) { MirrorItem *item = m_data[row]; m_data.removeAt(row); delete item; --count; } endRemoveRows(); return true; } void MirrorModel::addMirror(const QUrl &url, int numConnections, int priority, const QString &countryCode) { if (!url.isValid()) { return; } for (int i = 0; i < rowCount(); ++i) { //exists already, so remove the row if (QUrl(m_data.at(i)->data(MirrorItem::Url).toString()) == url) { removeRow(i); break; } } int index = rowCount(); emit beginInsertRows(QModelIndex(), index, index); MirrorItem *item = new MirrorItem; m_data.append(item); item->setData(MirrorItem::Used, Qt::Checked, Qt::CheckStateRole);//every newly added mirror is set to checked automatically item->setData(MirrorItem::Url, QVariant(url)); item->setData(MirrorItem::Connections, numConnections); item->setData(MirrorItem::Priority, priority); item->setData(MirrorItem::Country, countryCode); emit endInsertRows(); } void MirrorModel::setMirrors(const QHash > &mirrors) { removeRows(0, rowCount()); QHash >::const_iterator it; QHash >::const_iterator itEnd = mirrors.constEnd(); for (it = mirrors.constBegin(); it != itEnd; ++it) { MirrorItem *item = new MirrorItem; item->setData(MirrorItem::Url, QVariant(it.key())); Qt::CheckState state = (*it).first ? Qt::Checked : Qt::Unchecked; item->setData(MirrorItem::Used, state, Qt::CheckStateRole); item->setData(MirrorItem::Connections, (*it).second); m_data.append(item); } emit reset(); } QHash > MirrorModel::availableMirrors() const { QHash > mirrors; foreach (MirrorItem *item, m_data) { bool used = (item->data(MirrorItem::Used, Qt::CheckStateRole).toInt() == Qt::Checked) ? true : false; const QUrl url = QUrl(item->data(MirrorItem::Url).toString()); mirrors[url] = QPair(used, item->data(MirrorItem::Connections, Qt::UserRole).toInt()); } return mirrors; } diff --git a/ui/newtransferdialog.cpp b/ui/newtransferdialog.cpp index b9f42d05..c208627a 100644 --- a/ui/newtransferdialog.cpp +++ b/ui/newtransferdialog.cpp @@ -1,796 +1,796 @@ /* This file is part of the KDE project Copyright (C) 2005 Dario Massarin Copyright (C) 2007 by Javier Goday Copyright (C) 2008 - 2009 by Lukas Appelhans Copyright (C) 2010 by 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 "newtransferdialog.h" #include "core/filedeleter.h" #include "core/kget.h" #include "mainwindow.h" #include "core/mostlocalurl.h" #include "core/transfertreemodel.h" #include "core/transfergrouphandler.h" #include "core/plugin/transferfactory.h" #include "core/urlchecker.h" #include "settings.h" #include "kget_debug.h" -#include +#include #include #include #include #include #include #include #include #include #include #include #include Q_GLOBAL_STATIC(NewTransferDialogHandler, newTransferDialogHandler) NewTransferDialog::NewTransferDialog(QWidget *parent) : QDialog(parent), m_window(nullptr), m_existingTransfer(nullptr), m_multiple(false), m_overWriteSingle(false) { setModal(true); setWindowTitle(i18n("New Download")); ui.setupUi(this); ui.buttonBox->button(QDialogButtonBox::Ok)->setEnabled(false); //timer to avoid constant checking of the input m_timer = new QTimer(this); m_timer->setInterval(350); m_timer->setSingleShot(true); connect(m_timer, SIGNAL(timeout()), this, SLOT(checkInput())); const KColorScheme scheme = KColorScheme(QPalette::Active, KColorScheme::View); m_existingFileBackground = scheme.background(KColorScheme::NeutralBackground); m_normalBackground = scheme.background(); // properties of the m_destRequester combobox LineEditUrlDropEventFilter *dropUrlEventFilter = new LineEditUrlDropEventFilter(this); dropUrlEventFilter->installEventFilter(ui.destRequester->comboBox()); ui.destRequester->comboBox()->setDuplicatesEnabled(false); ui.destRequester->comboBox()->setEditable(true); ui.destRequester->setAcceptMode(QFileDialog::AcceptSave); ui.errorWidget->setCloseButtonVisible(false); connect(ui.groupComboBox, SIGNAL(currentIndexChanged(int)), this, SLOT(setDefaultDestination())); connect(ui.urlRequester, SIGNAL(textChanged(QString)), this, SLOT(setDefaultDestination())); connect(ui.destRequester, SIGNAL(textChanged(QString)), this, SLOT(inputTimer())); connect(ui.urlRequester, SIGNAL(textChanged(QString)), this, SLOT(inputTimer())); connect(ui.listWidget, SIGNAL(itemChanged(QListWidgetItem*)), this, SLOT(inputTimer())); connect(this, SIGNAL(finished(int)), this, SLOT(slotFinished(int))); connect(ui.buttonBox, &QDialogButtonBox::accepted, this, &QDialog::accept); connect(ui.buttonBox, &QDialogButtonBox::rejected, this, &QDialog::reject); } NewTransferDialog::~NewTransferDialog() { } void NewTransferDialog::setMultiple(bool useMultiple) { m_multiple = useMultiple; const Qt::Alignment alignment = Qt::AlignLeft | (m_multiple ? Qt::AlignTop : Qt::AlignVCenter); ui.urlLabel->setAlignment(alignment); ui.urlRequester->setVisible(!m_multiple); ui.listWidget->setVisible(m_multiple); ui.destRequester->setMode(m_multiple ? KFile::Directory : KFile::File); } void NewTransferDialog::clear() { ui.urlRequester->clear(); ui.urlRequester->setFocus(); ui.listWidget->clear(); ui.destRequester->comboBox()->clear(); ui.destRequester->clear(); m_destination.clear(); m_sources.clear(); m_existingTransfer = nullptr; m_overWriteSingle = false; //add all destinations QStringList list; QString downloadPath = KGet::generalDestDir(); if (!downloadPath.isEmpty()) { if (!downloadPath.endsWith('/')) { downloadPath.append('/'); } list << downloadPath; } foreach (TransferGroupHandler *handler, KGet::allTransferGroups()) { const QString folder = handler->defaultFolder(); if (!folder.isEmpty()) { list << (folder.endsWith('/') ? folder : folder + '/'); } } list.removeDuplicates(); ui.destRequester->comboBox()->insertItems(0, list); //add all transfer groups ui.groupComboBox->clear(); foreach (TransferGroupHandler *group, KGet::allTransferGroups()) { ui.groupComboBox->addItem(QIcon::fromTheme(group->iconName()), group->name()); } ui.groupComboBox->setCurrentItem(Settings::lastGroup()); if (ui.groupComboBox->currentIndex() == -1) { ui.groupComboBox->setCurrentIndex(0); } const bool multipleGroups = KGet::transferGroupNames().count(); ui.groupComboBox->setVisible(multipleGroups); ui.groupLabel->setVisible(multipleGroups); } void NewTransferDialog::setSource(const QList &sources) { if (sources.isEmpty()) { return; } if (sources.count() == 1) { QUrl m_srcUrl = sources.first().url(); ui.urlRequester->clear(); if (m_srcUrl.isEmpty()) { m_srcUrl = QUrl(QApplication::clipboard()->text(QClipboard::Clipboard).trimmed()); } if (UrlChecker::checkSource(m_srcUrl) == UrlChecker::NoError) { ui.urlRequester->insert(m_srcUrl.toString()); } } else { foreach (const QUrl &sourceUrl, sources) { if (sourceUrl.url() != QUrl(sourceUrl.url()).fileName()) {//TODO simplify, whatfor is this check anyway, shouldn't the sources be checked already and if not add this to UrlChecker qCDebug(KGET_DEBUG) << "Insert" << sourceUrl; QListWidgetItem *newItem = new QListWidgetItem(sourceUrl.toString(), ui.listWidget); newItem->setCheckState(Qt::Checked); } } } const QList groups = KGet::groupsFromExceptions(sources.first()); if (!groups.isEmpty()) { ui.groupComboBox->setCurrentIndex(ui.groupComboBox->findText(groups.first()->name())); } } void NewTransferDialog::setDestinationFileName(const QString &filename) { ui.destRequester->setUrl(QString(ui.destRequester->url().adjusted(QUrl::RemoveFilename).toString() + filename)); } void NewTransferDialog::setDestination() { //sets destRequester to either display the defaultFolder of group or the generalDestDir QString group = ui.groupComboBox->currentText(); TransferGroupHandler * current = nullptr; foreach (TransferGroupHandler * handler, KGet::allTransferGroups()) { if (handler->name() == group) { current = handler; break; } } if (current) { QString groupFolder = current->defaultFolder(); if (groupFolder.isEmpty()) { groupFolder = KGet::generalDestDir(); } if (!groupFolder.endsWith('/')) { groupFolder.append('/'); } ui.destRequester->comboBox()->setCurrentItem(groupFolder, true); } } void NewTransferDialog::showDialog(QList list, const QString &suggestedFileName) { //TODO handle the case where for some there are suggested file names --> own file name column in multiple setting //the dialog is already in use, adapt it if (isVisible()) { list << m_sources; } clear();//Let's clear the old stuff m_sources << list; UrlChecker::removeDuplicates(m_sources); const int size = m_sources.size(); qCDebug(KGET_DEBUG) << "SET SOURCES " << m_sources << " MULTIPLE " << (size > 1); setMultiple(size > 1); if (size) { if (size == 1 && !suggestedFileName.isEmpty()) { setDestinationFileName(suggestedFileName); } setSource(m_sources); } prepareDialog(); } void NewTransferDialog::setDefaultDestination() { //NOTE if the user enters a file name manually and the changes the group the manually entered file name will be overwritten setDestination(); //set a file name if (!m_multiple) { const QUrl url(ui.urlRequester->text().trimmed()); if ((UrlChecker::checkSource(url) == UrlChecker::NoError) && QFileInfo(ui.destRequester->url().toLocalFile()).isDir()) { setDestinationFileName(url.fileName()); } } } void NewTransferDialog::prepareDialog() { if (m_window) { KWindowInfo info(m_window->winId(), NET::WMDesktop); KWindowSystem::setCurrentDesktop(info.desktop()); KWindowSystem::forceActiveWindow(m_window->winId()); } qCDebug(KGET_DEBUG) << "Show the dialog!"; show(); } bool NewTransferDialog::isEmpty() { return (m_multiple ? !ui.listWidget->count() : ui.urlRequester->text().trimmed().isEmpty()); } void NewTransferDialog::inputTimer() { ui.buttonBox->button(QDialogButtonBox::Ok)->setEnabled(false); m_timer->start(); } void NewTransferDialog::checkInput() { qDebug() << "Check input"; QUrl source = QUrl(ui.urlRequester->text().trimmed()); const QUrl dest = ui.destRequester->url(); //check the destination folder UrlChecker::UrlError error = UrlChecker::checkFolder(dest); const bool folderValid = (error == UrlChecker::NoError); bool destinationValid = false; QString infoText; QString warningText; if (!folderValid) { if (m_multiple) { infoText = UrlChecker::message(QUrl(), UrlChecker::Folder, error); } else { //might be a destination instead of a folder destinationValid = (UrlChecker::checkDestination(dest) == UrlChecker::NoError); } } else { m_destination = dest; } //check the source if (!m_multiple) { source = mostLocalUrl(source); } error = UrlChecker::checkSource(source); const bool sourceValid = (error == UrlChecker::NoError); if (!m_multiple && !sourceValid) { infoText = UrlChecker::message(QUrl(), UrlChecker::Source, error); } //check if any sources are checked and for existing transfers or destinations bool filesChecked = false; if (m_multiple && folderValid) { QListWidget *list = ui.listWidget; //check if some sources have been checked for (int i = 0; i < list->count(); ++i) { QListWidgetItem *item = list->item(i); if (item->checkState() == Qt::Checked) { filesChecked = true; break; } } if (!filesChecked) { infoText = i18n("Select at least one source url."); } //check if there are existing files if (filesChecked) { bool existingFile = false; for (int i = 0; i < list->count(); ++i) { QListWidgetItem *item = list->item(i); const QUrl source = QUrl(item->text()); const QUrl destUrl = UrlChecker::destUrl(dest, source); if (UrlChecker::wouldOverwrite(source, destUrl)) { item->setBackground(m_existingFileBackground); existingFile = true; } else { item->setBackground(m_normalBackground); } } if (existingFile) { warningText = i18n("Files that exist already in the current folder have been marked.");//TODO better message } } } //single file UrlChecker::UrlWarning warning = UrlChecker::NoWarning; if (!m_multiple && sourceValid && (folderValid || destinationValid)) { m_destination = UrlChecker::destUrl(dest, source); //show only one message for existing transfers m_existingTransfer = UrlChecker::existingTransfer(source, UrlChecker::Source, &warning); if (m_existingTransfer) { warningText = UrlChecker::message(QUrl(), UrlChecker::Source, warning); } else { m_existingTransfer = UrlChecker::existingTransfer(m_destination, UrlChecker::Destination, &warning); if (m_existingTransfer) { warningText = UrlChecker::message(QUrl(), UrlChecker::Destination, warning); } } if (UrlChecker::wouldOverwrite(QUrl(ui.urlRequester->text().trimmed()), m_destination)) { m_overWriteSingle = true; if (!warningText.isEmpty()) { warningText += '\n'; } warningText += UrlChecker::message(QUrl(), UrlChecker::Destination, UrlChecker::ExistingFile); } else { m_overWriteSingle = false; } } if (!infoText.isEmpty()) { setInformation(infoText); } else if (!warningText.isEmpty()) { setWarning(warningText); } else { ui.errorWidget->hide(); } //activate the ok button if (m_multiple) { ui.buttonBox->button(QDialogButtonBox::Ok)->setEnabled(folderValid && filesChecked); } else { ui.buttonBox->button(QDialogButtonBox::Ok)->setEnabled((folderValid || destinationValid) && sourceValid); } qCDebug(KGET_DEBUG) << source << source.fileName() << dest << dest.fileName() << "Folder valid:" << folderValid << "Destination valid:" << destinationValid << "Source valid:" << sourceValid; } void NewTransferDialog::slotFinished(int resultCode) { if (resultCode == QDialog::Accepted) { dialogAccepted(); } clear(); } void NewTransferDialog::dialogAccepted() { qCDebug(KGET_DEBUG) << "Dialog accepted."; //an existing transfer has been specified and since ok was clicked, it was chosen to be overwritten if (m_existingTransfer) { qCDebug(KGET_DEBUG) << "Removing existing transfer:" << m_existingTransfer; KGet::delTransfer(m_existingTransfer); } //set the last directory QString dir = m_destination.toLocalFile(); if (!QFileInfo(dir).isDir()) { dir = m_destination.adjusted(QUrl::RemoveFilename).toLocalFile(); } Settings::setLastDirectory(dir); Settings::self()->save(); const QString group = ui.groupComboBox->currentText(); ///add data to create transfers QList data; if (!m_multiple) { if (m_overWriteSingle) { qCDebug(KGET_DEBUG) << "Removing existing file:" << m_destination; //removes m_destination if it exists, do that here so that it is removed no matter if a transfer could be created or not //as the user decided to throw the file away FileDeleter::deleteFile(m_destination); } //sourceUrl is valid, has been checked before const QUrl sourceUrl = QUrl(ui.urlRequester->text().trimmed()); qCDebug(KGET_DEBUG) << "Downloading" << sourceUrl << "to" << m_destination; data << KGet::TransferData(sourceUrl, m_destination, group); } else { QList list; for (int i = 0; i != ui.listWidget->count(); ++i) { QListWidgetItem *item = ui.listWidget->item(i); //find selected sources if (item->checkState() == Qt::Checked) { //both sourceUrl and destUrl are valid, they have been tested in checkInput const QUrl sourceUrl = QUrl(item->text().trimmed()); const QUrl destUrl = UrlChecker::destUrl(m_destination, sourceUrl); qCDebug(KGET_DEBUG) << "Downloading" << sourceUrl << "to" << destUrl; //file exists already, remove it if (item->background() == m_existingFileBackground) { qCDebug(KGET_DEBUG) << "Removing existing file:" << destUrl; //removes destUrl if it exists, do that here so that it is removed no matter if a transfer could be created or not //as the user decided to throw the file away FileDeleter::deleteFile(destUrl); } data << KGet::TransferData(sourceUrl, destUrl, group); } } } if (!data.isEmpty()) { Settings::setLastGroup(ui.groupComboBox->currentText()); KGet::createTransfers(data); } } void NewTransferDialog::setInformation(const QString &information) { ui.errorWidget->setMessageType(KMessageWidget::Information); ui.errorWidget->setText(information); ui.errorWidget->setVisible(!information.isEmpty()); } void NewTransferDialog::setWarning(const QString &warning) { ui.errorWidget->setMessageType(KMessageWidget::Warning); ui.errorWidget->setText(warning); ui.errorWidget->setVisible(!warning.isEmpty()); } /** * NOTE some checks in this class might seem redundant, though target is to display as few dialogs, and then preferable * the NewTransferDialog, to the user as possible i.e. if not enough information -- e.g. no destination folder * determinable, ...-- is present for a url or a group of urls they won't be added as transfer, * instead the NewTransferDialog will be shown * * This also tries to add as many transfers as possible with one run, to ensure a high speed */ NewTransferDialogHandler::NewTransferDialogHandler(QObject *parent) : QObject(parent), m_nextJobId(0) { } NewTransferDialogHandler::~NewTransferDialogHandler() { } void NewTransferDialogHandler::showNewTransferDialog(const QUrl &url) { showNewTransferDialog(url.isEmpty() ? QList() : QList() << url); } void NewTransferDialogHandler::showNewTransferDialog(QList urls) { if (urls.isEmpty()) { newTransferDialogHandler->createDialog(urls, QString()); return; } QHash::iterator itUrls = newTransferDialogHandler->m_urls.insert(newTransferDialogHandler->m_nextJobId, UrlData()); QString folder; QString suggestedFileName; ///Only two urls defined, check if second one is a path or a file name if (urls.count() == 2) { const QUrl lastUrl = urls.last(); QDir dir(lastUrl.toLocalFile()); //check if last url is a file path, either absolute or relative if (lastUrl.isLocalFile()) { if (QDir::isAbsolutePath(lastUrl.toLocalFile())) { if (dir.exists()) { //second url is a folder path folder = lastUrl.adjusted(QUrl::RemoveFilename).toString(); } else { //second url is a file path, use this one folder = lastUrl.adjusted(QUrl::RemoveFilename).toString(); suggestedFileName = lastUrl.fileName(); } urls.removeLast(); } else { //second url is just a file name suggestedFileName = lastUrl.fileName(); urls.removeLast(); } } else if (!lastUrl.isValid() || (lastUrl.scheme().isEmpty() && lastUrl.adjusted(QUrl::RemoveFilename).isEmpty())) { // Sometimes valid filenames are not recognised by KURL::isLocalFile(), they are marked as invalid then suggestedFileName = lastUrl.url(); urls.removeLast(); } } ///More than two urls defined, and last is local and will be used as destination directory if (urls.count() > 2 && urls.last().isLocalFile()) { /** * FIXME should the code be uncommented again, though then inputing a wrong destination like * ~/Downloads/folderNotExisting would result in ~/Downloads/ instead of informing the user * and giving them the possibility to improve their mistake */ // if (!QFileInfo(urls.last().toLocalFile()).isDir()) { // folder = urls.last().directory(QUrl::AppendTrailingSlash); // } else { folder = urls.last().adjusted(QUrl::RemoveFilename).toString();//checks if that folder is correct happen later // } urls.removeLast(); } //add a folder or suggestedFileName if they are valid if (!folder.isEmpty() && KGet::isValidDestDirectory(folder)) { (*itUrls).folder = folder; } if (!suggestedFileName.isEmpty()) { (*itUrls).suggestedFileName = QUrl(suggestedFileName).toString();//pathOrUrl to get a non percent encoded url } newTransferDialogHandler->m_numJobs[newTransferDialogHandler->m_nextJobId] = urls.count(); foreach (const QUrl &url, urls) { //needed to avoid when protocols like the desktop protocol is used, see bko:185283 KIO::Job *job = mostLocalUrlJob(url); job->setProperty("jobId", (newTransferDialogHandler->m_nextJobId)); connect(job, SIGNAL(result(KJob*)), newTransferDialogHandler, SLOT(slotMostLocalUrlResult(KJob*))); job->start(); } ++(newTransferDialogHandler->m_nextJobId); } void NewTransferDialogHandler::slotMostLocalUrlResult(KJob *j) { MostLocalUrlJob *job = static_cast(j); const int jobId = job->property("jobId").toInt(); if (job->error()) { qCWarning(KGET_DEBUG) << "An error happened for" << job->url(); } else { m_urls[jobId].urls << job->mostLocalUrl(); } --m_numJobs[jobId]; if (m_numJobs[jobId] <= 0) { handleUrls(jobId); } } void NewTransferDialogHandler::handleUrls(const int jobId) { QHash::iterator itUrls = m_urls.find(jobId); if (itUrls == m_urls.end()) { qCWarning(KGET_DEBUG) << "JobId" << jobId << "was not defined, could not handle urls for it."; return; } QList urls = (*itUrls).urls; UrlChecker::removeDuplicates(urls); QString folder = (*itUrls).folder; if (!folder.isEmpty() && (UrlChecker::checkFolder(QUrl::fromLocalFile(folder), true) != UrlChecker::NoError)) { folder.clear(); } const QString suggestedFileName = (*itUrls).suggestedFileName; QUrl newDest; const QUrl folderUrl = QUrl::fromLocalFile(folder); //check if the sources are correct UrlChecker check(UrlChecker::Source); check.addUrls(urls); check.displayErrorMessages(); check.existingTransfers(); urls = check.correctUrls(); QList data; ///Just one file to download, with a specified suggestedFileName, handle if possible if (!suggestedFileName.isEmpty() && (urls.count() == 1)) { const QUrl sourceUrl = urls.first(); const QList groups = KGet::groupsFromExceptions(sourceUrl); const QString groupName = (groups.isEmpty() ? QString() : groups.first()->name()); QString defaultFolder; if (groups.isEmpty()) { defaultFolder = (Settings::askForDestination() ? QString() : QStandardPaths::writableLocation(QStandardPaths::DownloadLocation)); } else { defaultFolder = groups.first()->defaultFolder(); } if (!folder.isEmpty()) { const QUrl destUrl = UrlChecker::destUrl(QUrl::fromLocalFile(folder), sourceUrl, suggestedFileName); newDest = check.checkExistingFile(sourceUrl, destUrl); if (!newDest.isEmpty()) { data << KGet::TransferData(sourceUrl, newDest, groupName); } urls.removeFirst(); } else if (((!groups.isEmpty() && !Settings::directoriesAsSuggestion()) || !Settings::askForDestination()) && (UrlChecker::checkFolder(QUrl::fromLocalFile(defaultFolder)) == UrlChecker::NoError)) { const QUrl destUrl = UrlChecker::destUrl(QUrl::fromLocalFile(defaultFolder), sourceUrl, suggestedFileName); newDest = check.checkExistingFile(sourceUrl, destUrl); if (!newDest.isEmpty()) { data << KGet::TransferData(sourceUrl, newDest, groupName); } urls.removeFirst(); } } ///A valid folder has been defined, use that for downloading if (!folder.isEmpty()) { //find the associated groups first, we just need the first matching group though const QList groups = KGet::allTransferGroups(); foreach (TransferGroupHandler *group, groups) { if (urls.isEmpty()) { break; } const QString groupName = group->name(); const QStringList patterns = group->regExp().pattern().split(','); //find all urls where a group can be identified QList::iterator it = urls.begin(); while (it != urls.end()) { const QUrl sourceUrl = *it; if (KGet::matchesExceptions(sourceUrl, patterns)) { const QUrl destUrl = UrlChecker::destUrl(folderUrl, sourceUrl); newDest = check.checkExistingFile(sourceUrl, destUrl); if (!newDest.isEmpty()) { data << KGet::TransferData(sourceUrl, newDest, groupName); } it = urls.erase(it); } else { ++it; } } } //there are still some unhandled urls, i.e. for those no group could be found, add them with an empty group foreach (const QUrl &sourceUrl, urls) { const QUrl destUrl = UrlChecker::destUrl(folderUrl, sourceUrl); newDest = check.checkExistingFile(sourceUrl, destUrl); if (!newDest.isEmpty()) { data << KGet::TransferData(sourceUrl, newDest); } } //all urls have been handled urls.clear(); } ///Now handle default folders/groups qCDebug(KGET_DEBUG) << "DIRECTORIES AS SUGGESTION" << Settings::directoriesAsSuggestion(); if (!Settings::directoriesAsSuggestion() && !urls.isEmpty()) { qCDebug(KGET_DEBUG) << "No, Directories not as suggestion"; //find the associated groups first, we just need the first matching group though const QList groups = KGet::allTransferGroups(); foreach (TransferGroupHandler *group, groups) { if (urls.isEmpty()) { break; } const QUrl folderUrl = QUrl::fromLocalFile(group->defaultFolder()); if (UrlChecker::checkFolder(folderUrl) != UrlChecker::NoError) { continue; } const QString groupName = group->name(); const QStringList patterns = group->regExp().pattern().split(','); QList::iterator it = urls.begin(); while (it != urls.end()) { const QUrl sourceUrl = *it; if (KGet::matchesExceptions(sourceUrl, patterns)) { const QUrl destUrl = UrlChecker::destUrl(folderUrl, sourceUrl); newDest = check.checkExistingFile(sourceUrl, destUrl); if (!newDest.isEmpty()) { data << KGet::TransferData(sourceUrl, newDest, groupName); } it = urls.erase(it); } else { ++it; } } } } ///Download the rest of the urls to QStandardPaths::writableLocation(QStandardPaths::DownloadLocation) if the user is not aksed for a destination if (!Settings::askForDestination()) { //the download path will be always used const QString dir = QStandardPaths::writableLocation(QStandardPaths::DownloadLocation); if (!dir.isEmpty()) { QList::iterator it = urls.begin(); while (it != urls.end()) { const QUrl sourceUrl = *it; const QUrl destUrl = UrlChecker::destUrl(QUrl::fromLocalFile(dir), sourceUrl); newDest = check.checkExistingFile(sourceUrl, destUrl); if (!newDest.isEmpty()) { data << KGet::TransferData(sourceUrl, newDest); } it = urls.erase(it); } } } ///Now create transfers for the urls that provided enough data if (!data.isEmpty()) { KGet::createTransfers(data); } ///Handle custom newtransferdialogs... if ((!m_dialog || m_dialog->isEmpty()) && urls.count() == 1) {//FIXME why the m_dialog check? whenever a dialog has been created this would not be shown? QUrl url = urls.first(); QPointer dialog; foreach (TransferFactory * factory, KGet::factories()) { const QList groups = KGet::groupsFromExceptions(url); dialog = factory->createNewTransferDialog(url, suggestedFileName, !groups.isEmpty() ? groups.first() : nullptr); if (dialog) { KWindowInfo info(KGet::m_mainWindow->winId(), NET::WMDesktop); KWindowSystem::setCurrentDesktop(info.desktop()); KWindowSystem::forceActiveWindow(KGet::m_mainWindow->winId()); dialog->exec(); delete dialog; } } } m_numJobs.remove(jobId); m_urls.erase(itUrls); ///Display default NewTransferDialog if (!urls.isEmpty()) { createDialog(urls, suggestedFileName); } } void NewTransferDialogHandler::createDialog(const QList &urls, const QString &suggestedFileName) { if (!m_dialog) { m_dialog = new NewTransferDialog(KGet::m_mainWindow); } m_dialog->m_window = KGet::m_mainWindow; m_dialog->showDialog(urls, suggestedFileName); } diff --git a/ui/signaturedlg.cpp b/ui/signaturedlg.cpp index 0b4883ce..83898f6b 100644 --- a/ui/signaturedlg.cpp +++ b/ui/signaturedlg.cpp @@ -1,374 +1,374 @@ /*************************************************************************** * 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. * * * * 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 "signaturedlg.h" #include "core/kget.h" #include "core/filemodel.h" #include "core/transferhandler.h" #include "core/signature.h" #include "kget_debug.h" -#include +#include #ifdef HAVE_QGPGME #include #include #endif #include #include #include #include const QStringList SignatureDlg::OWNERTRUST = QStringList() << i18nc("trust level", "Unknown") << i18nc("trust level", "Undefined") << i18nc("trust level", "Never") << i18nc("trust level", "Marginal") << i18nc("trust level", "Full") << i18nc("trust level", "Ultimate"); SignatureDlg::SignatureDlg(TransferHandler *transfer, const QUrl &dest, QWidget *parent, Qt::WFlags flags) : KGetSaveSizeDialog("SignatureDlg", parent, flags), m_signature(transfer->signature(dest)), m_fileModel(transfer->fileModel()) { setWindowTitle(i18nc("Signature here is meant in cryptographic terms, so the signature of a file.", "Signature of %1.", dest.fileName())); ui.setupUi(this); ui.loadSignature->setIcon(QIcon::fromTheme("document-open")); ui.verify->setIcon(QIcon::fromTheme("document-encrypt")); ui.information->setCloseButtonVisible(false); ui.information->setWordWrap(true); if (m_signature) { connect(ui.loadSignature, &QPushButton::clicked, this, &SignatureDlg::loadSignatureClicked); connect(ui.verify, &QPushButton::clicked, this, &SignatureDlg::verifyClicked); - connect(ui.signature, &KTextEdit::textChanged, this, &SignatureDlg::textChanged); + connect(ui.signature, &QTextEdit::textChanged, this, &SignatureDlg::textChanged); connect(m_signature, &Signature::verified, this, &SignatureDlg::updateData); if (m_fileModel) { m_file = m_fileModel->index(dest, FileItem::File); connect(m_fileModel, &FileModel::fileFinished, this, &SignatureDlg::fileFinished); } updateData(); updateButtons(); } else { ui.information->setMessageType(KMessageWidget::Warning); ui.information->setText(i18n("This option is not supported for the current transfer.")); ui.sigGroup->hide(); ui.keyGroup->hide(); } connect(ui.buttonBox, &QDialogButtonBox::accepted, this, &QDialog::accept); connect(ui.buttonBox, &QDialogButtonBox::rejected, this, &QDialog::reject); } void SignatureDlg::fileFinished(const QUrl &file) { if (m_fileModel && (m_fileModel->getUrl(m_file) == file)) { updateButtons(); } } void SignatureDlg::textChanged() { if (m_signature) { m_signature->setAsciiDetatchedSignature(ui.signature->toPlainText()); clearData(); updateButtons(); } } void SignatureDlg::loadSignatureClicked() { const QUrl url = QFileDialog::getOpenFileUrl(this, i18n("Load Signature File"), KGet::generalDestDir(), "*.asc|" + i18n("Detached OpenPGP ASCII signature (*.asc)") + '\n' + "*.sig|" + i18n("Detached OpenPGP binary signature (*.sig)")); if (url.isEmpty()) { return; } const bool isAsciiSig = url.fileName().endsWith(QLatin1String(".asc")); clearData(); handleWidgets(isAsciiSig); ui.signature->clear(); QFile file(url.path()); if (!file.open(QIODevice::ReadOnly)) { qCWarning(KGET_DEBUG) << "Could not open file" << url; return; } if (file.size() > 1 * 1024) { qCWarning(KGET_DEBUG) << "File is larger than 1 KiB, which is not supported."; return; } const QByteArray data = file.readAll(); if (isAsciiSig) { ui.signature->setText(data); } else if (m_signature) { m_signature->setSignature(data, Signature::BinaryDetached); clearData(); updateButtons(); } } void SignatureDlg::updateButtons() { bool enableVerify = m_signature && m_signature->isVerifyable(); if (!m_fileModel || !m_fileModel->downloadFinished(m_fileModel->getUrl(m_file))) { enableVerify = false; } ui.verify->setEnabled(enableVerify); } void SignatureDlg::updateData() { if (!m_signature) { return; } const QString fingerprintString = m_signature->fingerprint(); const QByteArray signature = m_signature->signature(); QStringList information; bool problem = false; bool error = false; if (signature.isEmpty()) { information << i18n("You need to define a signature."); problem = true; } if (fingerprintString.isEmpty()) { information << i18n("No fingerprint could be found, check if the signature is correct or verify the download.");//TODO get fingerprint from signature! problem = true; } ui.fingerprint->setText(fingerprintString); const bool isAsciiSig = (m_signature->type() != Signature::BinaryDetached); handleWidgets(isAsciiSig); if (isAsciiSig) { ui.signature->blockSignals(true); ui.signature->setText(signature); ui.signature->blockSignals(false); } ui.keyGroup->setVisible(!signature.isEmpty()); const int iconSize = KIconLoader::global()->currentSize(KIconLoader::Small); #ifdef HAVE_QGPGME if (!fingerprintString.isEmpty()) { GpgME::initializeLibrary(); GpgME::Error err = GpgME::checkEngine(GpgME::OpenPGP); QScopedPointer context(GpgME::Context::createForProtocol(GpgME::OpenPGP)); if (err) { qCDebug(KGET_DEBUG) << "OpenPGP not supported!"; } else if (!context.data()) { qCDebug(KGET_DEBUG) << "Could not create context."; } else { QByteArray fingerprint = fingerprintString.toAscii(); const GpgME::Key key = context->key(fingerprint.constData(), err); if (err || key.isNull() || !key.numUserIDs() || !key.numSubkeys()) { qCDebug(KGET_DEBUG) << "There was an error while loading the key:" << err; } else { if (key.isRevoked()) { information << i18n("The key has been revoked."); problem = true; } if (key.isDisabled()) { information << i18n("The key is disabled."); problem = true; } if (key.isInvalid()) { information << i18n("The key is invalid."); error = true; } ui.expirationIcon->clear(); if (key.isExpired()) { information << i18n("The key is expired."); ui.expirationIcon->setPixmap(QIcon::fromTheme("dialog-warning").pixmap(iconSize)); ui.expirationIcon->show(); problem = true; } else { ui.expirationIcon->hide(); } //handle the trust of the key const GpgME::Key::OwnerTrust ownerTrust = key.ownerTrust(); ui.trust->setText(OWNERTRUST.value(ownerTrust)); switch (ownerTrust) { case GpgME::Key::Never: information.prepend(i18n("The key is not to be trusted.")); ui.trustIcon->setPixmap(QIcon::fromTheme("dialog-error").pixmap(iconSize)); error = true; break; case GpgME::Key::Marginal: information.prepend(i18n("The key is to be trusted marginally.")); ui.trustIcon->setPixmap(QIcon::fromTheme("dialog-warning").pixmap(iconSize)); problem = true; break; case GpgME::Key::Full: ui.trustIcon->setPixmap(QIcon::fromTheme("dialog-ok").pixmap(iconSize)); break; case GpgME::Key::Ultimate: ui.trustIcon->setPixmap(QIcon::fromTheme("dialog-ok").pixmap(iconSize)); break; case GpgME::Key::Unknown: case GpgME::Key::Undefined: default: information.prepend(i18n("Trust level of the key is unclear.")); ui.trustIcon->setPixmap(QIcon::fromTheme("dialog-warning").pixmap(iconSize)); problem = true; break; } //issuer, issuer mail and comment if (key.numUserIDs()) { const GpgME::UserID userID = key.userID(0); ui.issuer->setText(userID.name()); ui.comment->setText(userID.comment()); ui.email->setText(userID.email()); } //key creation/expiration-time if (key.numSubkeys()) { const GpgME::Subkey subKey = key.subkey(0); time_t creation = subKey.creationTime(); QDateTime creationTime; creationTime.setTime_t(creation); ui.creation->setText(creationTime.toString()); time_t expiration = subKey.expirationTime(); if (expiration) { QDateTime expirationTime; expirationTime.setTime_t(expiration); ui.expiration->setText(expirationTime.toString()); } else { ui.expiration->setText(i18n("Unlimited")); } } } } } #endif //HAVE_QGPGME const Signature::VerificationStatus verificationStatus = m_signature->status(); //display the verification status ui.verificationIcon->hide(); switch (verificationStatus) { case Signature::Verified: case Signature::VerifiedInformation: case Signature::VerifiedWarning: ui.verificationIcon->setPixmap(QIcon::fromTheme("dialog-ok").pixmap(iconSize)); ui.verificationIcon->show(); ui.verified->setText(i18nc("pgp signature is verified", "Verified")); break; case Signature::NotVerified: ui.verified->setText(i18nc("pgp signature is not verified", "Failed")); ui.verificationIcon->setPixmap(QIcon::fromTheme("dialog-error").pixmap(iconSize)); ui.verificationIcon->show(); information.prepend(i18n("Caution: Verification failed. Either you entered the wrong signature, or the data has been modified.")); error = true; break; case Signature::NotWorked://TODO downloading state? --> currently downloading ui.verified->clear();//TODO information.prepend(i18n("Verification not possible. Check the entered data, whether gpg-agent is running, or whether you have an Internet connection (for retrieving keys.)")); problem = true; break; case Signature::NoResult: ui.verified->clear();//TODO break; } if (verificationStatus == Signature::VerifiedWarning) { problem = true; } #ifndef HAVE_QGPGME ui.sigGroup->hide(); ui.keyGroup->hide(); ui.verify->hide(); information.clear(); information << i18n("Feature is not supported, as KGet is not compiled with QPGME support."); resize(350, 200); #endif //HAVE_QPGME //TODO more messages, e.g. from result etc. //TODO enter more error cases //change the icon of the titlewidget if (error) { ui.information->setMessageType(KMessageWidget::Error); } else if (problem) { ui.information->setMessageType(KMessageWidget::Warning); } else { if (verificationStatus != Signature::Verified) { ui.information->setMessageType(KMessageWidget::Information); } } const QString text = information.join(" "); ui.information->setVisible(!text.isEmpty()); ui.information->setText(text); } void SignatureDlg::verifyClicked() { clearData(); m_signature->verify(); } void SignatureDlg::clearData() { ui.verified->clear(); ui.verificationIcon->clear(); ui.issuer->clear(); ui.email->clear(); ui.comment->clear(); ui.creation->clear(); ui.expiration->clear(); ui.expirationIcon->hide(); ui.trust->clear(); ui.trustIcon->clear(); ui.fingerprint->clear(); } void SignatureDlg::handleWidgets(bool isAsciiSig) { ui.asciiLabel->setVisible(isAsciiSig); ui.signature->setVisible(isAsciiSig); ui.binaryLabel->setVisible(!isAsciiSig); QLayoutItem *item = ui.verticalLayout_2->itemAt(ui.verticalLayout_2->count() - 1); QSpacerItem *spacer = item->spacerItem(); if (isAsciiSig) { if (spacer) { ui.verticalLayout_2->removeItem(item); delete item; } } else if (!spacer) { ui.verticalLayout_2->addStretch(1); } } diff --git a/ui/signaturedlg.ui b/ui/signaturedlg.ui index 01f70461..28905bd3 100644 --- a/ui/signaturedlg.ui +++ b/ui/signaturedlg.ui @@ -1,337 +1,332 @@ SignatureDlg 0 0 400 492 Signature 75 true Verification: 75 true Qt::Horizontal 40 20 Has binary PGP signature. Ascii PGP signature: - + Qt::Horizontal 40 20 Load Signature Verify Key 75 true Issuer: 75 true Qt::LinksAccessibleByMouse|Qt::TextSelectableByMouse E-Mail: Qt::LinksAccessibleByMouse|Qt::TextSelectableByMouse Comment: Creation: Qt::LinksAccessibleByMouse|Qt::TextSelectableByMouse Expiration: Qt::LinksAccessibleByMouse|Qt::TextSelectableByMouse Qt::Horizontal 40 20 Trust: Qt::LinksAccessibleByMouse|Qt::TextSelectableByMouse Qt::Horizontal 40 20 Fingerprint: Qt::LinksAccessibleByMouse|Qt::TextSelectableByMouse QDialogButtonBox::Cancel|QDialogButtonBox::Ok - - KTextEdit - QTextEdit -
ktextedit.h
-
KMessageWidget QWidget
KMessageWidget
1
diff --git a/ui/transferdetails.cpp b/ui/transferdetails.cpp index fa12e4d6..00fcfebc 100644 --- a/ui/transferdetails.cpp +++ b/ui/transferdetails.cpp @@ -1,107 +1,107 @@ /* This file is part of the KDE project Copyright (C) 2005 Dario Massarin 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 "transferdetails.h" #include "core/kget.h" -#include -#include +#include +#include #include "kget_debug.h" -#include +#include #include TransferDetails::TransferDetails(TransferHandler * transfer) : QWidget(nullptr), m_transfer(transfer) { m_genericWidget = new QWidget(this); frm.setupUi(m_genericWidget); m_layout = new QVBoxLayout(this); m_layout->addWidget(m_genericWidget); setLayout(m_layout); frm.sourceContentEdit->setText(m_transfer->source().toString()); frm.destContentEdit->setText(m_transfer->dest().toLocalFile()); //This updates the widget with the right values slotTransferChanged(transfer, 0xFFFFFFFF); connect(transfer, SIGNAL(transferChangedEvent(TransferHandler*,TransferHandler::ChangesFlags)), this, SLOT(slotTransferChanged(TransferHandler*,TransferHandler::ChangesFlags))); } TransferDetails::~TransferDetails() { } QWidget *TransferDetails::detailsWidget(TransferHandler *handler) { QWidget *details = KGet::factory(handler)->createDetailsWidget(handler); if (!details) { // the transfer factory doesn't override the details widget so use the generic one details = new TransferDetails(handler); } return details; } void TransferDetails::slotTransferChanged(TransferHandler * transfer, TransferHandler::ChangesFlags flags) { qCDebug(KGET_DEBUG) << "TransferDetails::slotTransferChanged"; Q_UNUSED(transfer) if(flags & Transfer::Tc_Status) { frm.statusPixmapContentLabel->setPixmap(m_transfer->statusPixmap()); frm.statusTextContentLabel->setText(m_transfer->statusText()); if (m_transfer->status() == Job::Finished) { frm.speedContentLabel->setText(i18n("Average speed: %1/s", KIO::convertSize(m_transfer->averageDownloadSped()))); } } if ((flags & Transfer::Tc_TotalSize) || (flags & Transfer::Tc_DownloadedSize)) { frm.completedContentLabel->setText(i18n("%1 of %2", KIO::convertSize(m_transfer->downloadedSize()), KIO::convertSize(m_transfer->totalSize()))); } if (flags & Transfer::Tc_Percent) { frm.progressBar->setValue(m_transfer->percent()); } if ((flags & Transfer::Tc_DownloadSpeed) && (m_transfer->status() != Job::Finished)) { int speed = m_transfer->downloadSpeed(); if (speed == 0) { if(m_transfer->status() == Job::Running) frm.speedContentLabel->setText(i18n("Stalled") ); else frm.speedContentLabel->setText(QString()); } else { frm.speedContentLabel->setText(i18n("%1/s", KIO::convertSize(speed))); } } if(flags & Transfer::Tc_FileName) { frm.destContentEdit->setText(m_transfer->dest().toLocalFile()); } if (flags & Transfer::Tc_Source) { frm.sourceContentEdit->setText(m_transfer->source().toString()); } frm.remainingTimeLabel->setText(KIO::convertSeconds(m_transfer->remainingTime())); } diff --git a/ui/transfersettingsdialog.cpp b/ui/transfersettingsdialog.cpp index 407739e2..0faa107c 100644 --- a/ui/transfersettingsdialog.cpp +++ b/ui/transfersettingsdialog.cpp @@ -1,183 +1,184 @@ /* This file is part of the KDE project Copyright (C) 2008 Lukas Appelhans 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 "transfersettingsdialog.h" #include "mirror/mirrorsettings.h" #include "renamefile.h" #include "signaturedlg.h" #include "verificationdialog.h" #include "settings.h" #include "core/transferhandler.h" #include "core/filemodel.h" #include "core/verifier.h" #include -#include #include + +#include #include TransferSettingsDialog::TransferSettingsDialog(QWidget *parent, TransferHandler *transfer) : KGetSaveSizeDialog("TransferSettingsDialog", parent), m_transfer(transfer), m_model(m_transfer->fileModel()), m_proxy(nullptr) { setWindowTitle(i18n("Transfer Settings for %1", m_transfer->source().fileName())); ui.setupUi(this); ui.ktitlewidget->setPixmap(QIcon::fromTheme("preferences-other").pixmap(16)); ui.downloadSpin->setValue(m_transfer->downloadLimit(Transfer::VisibleSpeedLimit)); ui.uploadSpin->setValue(m_transfer->uploadLimit(Transfer::VisibleSpeedLimit)); ui.ratioSpin->setValue(m_transfer->maximumShareRatio()); ui.destination->setUrl(m_transfer->directory().toString()); ui.destination->lineEdit()->setReadOnly(true); ui.rename->setIcon(QIcon::fromTheme("edit-rename")); ui.mirrors->setIcon(QIcon::fromTheme("download")); ui.signature->setIcon(QIcon::fromTheme("application-pgp-signature")); ui.verification->setIcon(QIcon::fromTheme("document-decrypt")); if (m_model) { m_model->watchCheckState(); m_proxy = new QSortFilterProxyModel(this); m_proxy->setSourceModel(m_model); ui.treeView->setModel(m_proxy); ui.treeView->sortByColumn(0, Qt::AscendingOrder); QByteArray loadedState = QByteArray::fromBase64(Settings::transferSettingsHeaderState().toAscii()); if (!loadedState.isEmpty()) { ui.treeView->header()->restoreState(loadedState); } else { ui.treeView->header()->resizeSection(0, ui.treeView->header()->defaultSectionSize() * 3); } } updateCapabilities(); connect(m_transfer, &TransferHandler::capabilitiesChanged, this, &TransferSettingsDialog::updateCapabilities); connect(this, &TransferSettingsDialog::accepted, this, &TransferSettingsDialog::save); connect(this, &TransferSettingsDialog::finished, this, &TransferSettingsDialog::slotFinished); connect(ui.treeView->selectionModel(), SIGNAL(selectionChanged(QItemSelection,QItemSelection)), this, SLOT(slotSelectionChanged())); connect(ui.rename, &QPushButton::clicked, this, &TransferSettingsDialog::slotRename); connect(ui.mirrors, &QPushButton::clicked, this, &TransferSettingsDialog::slotMirrors); connect(ui.verification, &QPushButton::clicked, this, &TransferSettingsDialog::slotVerification); connect(ui.signature, &QPushButton::clicked, this, &TransferSettingsDialog::slotSignature); connect(ui.buttonBox, &QDialogButtonBox::accepted, this, &QDialog::accept); connect(ui.buttonBox, &QDialogButtonBox::rejected, this, &QDialog::reject); } TransferSettingsDialog::~TransferSettingsDialog() { if (m_model) { Settings::setTransferSettingsHeaderState(ui.treeView->header()->saveState().toBase64()); } } QSize TransferSettingsDialog::sizeHint() const { QSize sh = QDialog::sizeHint(); sh.setWidth(sh.width() * 1.7); return sh; } void TransferSettingsDialog::updateCapabilities() { const int capabilities = m_transfer->capabilities(); const bool supportsSpeedLimit = capabilities & Transfer::Cap_SpeedLimit; ui.labelDownload->setVisible(supportsSpeedLimit); ui.downloadSpin->setVisible(supportsSpeedLimit); ui.labelUpload->setVisible(supportsSpeedLimit); ui.uploadSpin->setVisible(supportsSpeedLimit); ui.labelShareRatio->setVisible(supportsSpeedLimit); ui.ratioSpin->setVisible(supportsSpeedLimit); ui.destination->setEnabled(capabilities & Transfer::Cap_Moving); ui.mirrors->setVisible(capabilities & Transfer::Cap_MultipleMirrors); ui.rename->setVisible(capabilities & Transfer::Cap_Renaming); } void TransferSettingsDialog::slotMirrors() { const QModelIndex index = m_proxy->mapToSource(ui.treeView->selectionModel()->selectedIndexes().first()); QDialog *mirrors = new MirrorSettings(this, m_transfer, m_model->getUrl(index)); mirrors->setAttribute(Qt::WA_DeleteOnClose); mirrors->show(); } void TransferSettingsDialog::slotRename() { const QModelIndex index = m_proxy->mapToSource(ui.treeView->selectionModel()->selectedIndexes().first()); RenameFile *renameDlg = new RenameFile(m_model, index, this); renameDlg->setAttribute(Qt::WA_DeleteOnClose); renameDlg->show(); } void TransferSettingsDialog::slotVerification() { const QModelIndex index = m_proxy->mapToSource(ui.treeView->selectionModel()->selectedIndexes().first()); QDialog *verification = new VerificationDialog(this, m_transfer, m_model->getUrl(index)); verification->setAttribute(Qt::WA_DeleteOnClose); verification->show(); } void TransferSettingsDialog::slotSignature() { const QModelIndex index = m_proxy->mapToSource(ui.treeView->selectionModel()->selectedIndexes().first()); SignatureDlg *signature = new SignatureDlg(m_transfer, m_model->getUrl(index), this); signature->setAttribute(Qt::WA_DeleteOnClose); signature->show(); } void TransferSettingsDialog::slotSelectionChanged() { bool enabled = false; //only enable rename when one item is selected and when this item is a file if (ui.treeView->selectionModel()->selectedRows().count() == 1) { const QModelIndex index = m_proxy->mapToSource(ui.treeView->selectionModel()->selectedIndexes().first()); if (index.isValid() && !(static_cast(index.internalPointer()))->childCount()) { enabled = true; } } ui.mirrors->setEnabled(enabled); ui.rename->setEnabled(enabled); ui.verification->setEnabled(enabled); ui.signature->setEnabled(enabled); } void TransferSettingsDialog::save() {//TODO: Set to -1 when no limit QUrl oldDirectory = m_transfer->directory(); QUrl newDirectory = ui.destination->url(); if ((oldDirectory != newDirectory) && !m_transfer->setDirectory(newDirectory)) { KMessageBox::error(this, i18n("Changing the destination did not work, the destination stays unmodified."), i18n("Destination unmodified")); } m_transfer->setDownloadLimit(ui.downloadSpin->value(), Transfer::VisibleSpeedLimit); m_transfer->setUploadLimit(ui.uploadSpin->value(), Transfer::VisibleSpeedLimit); m_transfer->setMaximumShareRatio(ui.ratioSpin->value()); } void TransferSettingsDialog::slotFinished() { if (m_model) { m_model->stopWatchCheckState(); } } diff --git a/ui/transfersview.cpp b/ui/transfersview.cpp index 4e5552a8..6a4445f7 100644 --- a/ui/transfersview.cpp +++ b/ui/transfersview.cpp @@ -1,322 +1,323 @@ /* This file is part of the KDE project Copyright (C) 2006 Dario Massarin Copyright (C) 2009 Lukas Appelhans 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 "transfersview.h" #include "settings.h" #include "transfersviewdelegate.h" #include "transferdetails.h" #include "core/transfertreemodel.h" #include "core/kget.h" #include "kget_debug.h" -#include -#include + #include -#include #include +#include +#include #include #include +#include #include #include TransfersView::TransfersView(QWidget * parent) : QTreeView(parent) { // setItemsExpandable(false); setRootIsDecorated(false); setAnimated(true); setAllColumnsShowFocus(true); header()->setDefaultAlignment(Qt::AlignCenter); header()->setMinimumSectionSize(80); header()->setContextMenuPolicy(Qt::CustomContextMenu); header()->setClickable(true); m_headerMenu = new QMenu(header()); setSelectionMode(QAbstractItemView::ExtendedSelection); setDragEnabled(true); setAcceptDrops(true); setDropIndicatorShown(true); setEditTriggers(QAbstractItemView::NoEditTriggers); setVerticalScrollMode(QAbstractItemView::ScrollPerPixel); setHorizontalScrollMode(QAbstractItemView::ScrollPerPixel); connect(header(), SIGNAL(customContextMenuRequested(QPoint)), SLOT(slotShowHeaderMenu(QPoint))); connect(header(), SIGNAL(sectionCountChanged(int,int)), this, SLOT(populateHeaderActions())); connect(header(), SIGNAL(sectionMoved(int,int,int)), this, SLOT(slotSectionMoved(int,int,int))); connect(header(), SIGNAL(sectionResized(int,int,int)), this, SLOT(slotSaveHeader())); connect(this, &TransfersView::doubleClicked, this, &TransfersView::slotItemActivated); connect(this, &TransfersView::collapsed, this, &TransfersView::slotItemCollapsed); connect(KGet::model(), SIGNAL(rowsAboutToBeRemoved(QModelIndex,int,int)), this, SLOT(closeExpandableDetails(QModelIndex,int,int))); } TransfersView::~TransfersView() { } void TransfersView::setModel(QAbstractItemModel * model) { QTreeView::setModel(model); int nGroups = model->rowCount(QModelIndex()); for(int i = 0; i < nGroups; i++) { qCDebug(KGET_DEBUG) << "openEditor for row " << i; openPersistentEditor(model->index(i, TransferTreeModel::Status, QModelIndex())); } QByteArray loadedState = QByteArray::fromBase64(Settings::headerState().toAscii()); if (loadedState.isEmpty()) { setColumnWidth(0 , 230); } else { header()->restoreState(loadedState); } //Workaround if the saved headerState is corrupted header()->setRootIndex(QModelIndex()); populateHeaderActions(); toggleMainGroup(); connect(model, SIGNAL(rowsRemoved(QModelIndex,int,int)), SLOT (toggleMainGroup())); } void TransfersView::dropEvent(QDropEvent * event) { QModelIndex dropIndex = indexAt(event->pos()); QTreeView::dropEvent(event); setExpanded(dropIndex, true); } void TransfersView::rowsInserted(const QModelIndex & parent, int start, int end) { qCDebug(KGET_DEBUG) << "TransfersView::rowsInserted"; if(!parent.isValid()) { qCDebug(KGET_DEBUG) << "parent is not valid " << start << " " << end; for(int i = start; i <= end; i++) { qCDebug(KGET_DEBUG) << "openEditor for row " << i; openPersistentEditor(model()->index(i, TransferTreeModel::Status, parent)); } } QTreeView::rowsInserted(parent, start, end); setExpanded(parent, true); toggleMainGroup(); } void TransfersView::populateHeaderActions() { m_headerMenu->clear(); m_headerMenu->addSection(i18n("Select columns")); QSignalMapper *columnMapper = new QSignalMapper(this); connect(columnMapper, static_cast(&QSignalMapper::mapped), this, &TransfersView::slotHideSection); //Create for each column an action with the column-header as name QVector orderedMenuItems(header()->count()); for (int i = 0; i < header()->count(); ++i) { QAction *action = new QAction(this); action->setText(model()->headerData(i, Qt::Horizontal).toString()); action->setCheckable(true); action->setChecked(!header()->isSectionHidden(i)); orderedMenuItems[header()->visualIndex(i)] = action; connect(action, SIGNAL(toggled(bool)), columnMapper, SLOT(map())); columnMapper->setMapping(action, i); } //append the sorted actions for (int i = 0; i < orderedMenuItems.count(); ++i) { m_headerMenu->addAction(orderedMenuItems[i]); } } void TransfersView::slotHideSection(int logicalIndex) { const bool hide = !header()->isSectionHidden(logicalIndex); header()->setSectionHidden(logicalIndex, hide); slotSaveHeader(); } void TransfersView::slotSectionMoved(int logicalIndex, int oldVisualIndex, int newVisualIndex) { Q_UNUSED(logicalIndex) //first item is the title, so increase the indexes by one ++oldVisualIndex; ++newVisualIndex; QList actions = m_headerMenu->actions(); QAction *before = actions.last(); if (newVisualIndex + 1 < actions.count()) { if (newVisualIndex > oldVisualIndex) { before = actions[newVisualIndex + 1]; } else { before = actions[newVisualIndex]; } } QAction *action = actions[oldVisualIndex]; m_headerMenu->removeAction(action); m_headerMenu->insertAction(before, action); slotSaveHeader(); } void TransfersView::slotSaveHeader() { Settings::setHeaderState(header()->saveState().toBase64()); Settings::self()->save(); } void TransfersView::dragMoveEvent ( QDragMoveEvent * event ) { Q_UNUSED(event) closeExpandableDetails(); QTreeView::dragMoveEvent(event); } void TransfersView::slotItemActivated(const QModelIndex & index) { if (!index.isValid()) return; TransferTreeModel * transferTreeModel = KGet::model(); ModelItem * item = transferTreeModel->itemFromIndex(index); TransfersViewDelegate *view_delegate = static_cast (itemDelegate()); if(!item) return; if(!item->isGroup() && index.column() == 0) { if(!view_delegate->isExtended(index)) { TransferHandler *handler = item->asTransfer()->transferHandler(); QWidget *widget = getDetailsWidgetForTransfer(handler); m_editingIndexes.append(index); view_delegate->extendItem(widget, index); } else { m_editingIndexes.removeAll(index); view_delegate->contractItem(index); } KGet::actionCollection()->action("transfer_show_details")->setChecked(view_delegate->isExtended(index)); } else if (!item->isGroup() && static_cast(item)->transferHandler()->status() == Job::Finished) { new KRun(static_cast(item)->transferHandler()->dest(), this); } } void TransfersView::slotItemCollapsed(const QModelIndex & index) { if (!index.isValid()) return; TransferTreeModel * transferTreeModel = KGet::model(); ModelItem * item = transferTreeModel->itemFromIndex(index); TransfersViewDelegate *view_delegate = static_cast (itemDelegate()); if(!item) return; if(item->isGroup()) { TransferGroupHandler * groupHandler = item->asGroup()->groupHandler(); QList transfers = groupHandler->transfers(); foreach(TransferHandler * transfer, transfers) { qCDebug(KGET_DEBUG) << "Transfer = " << transfer->source().toString(); view_delegate->contractItem(KGet::model()->itemFromTransferHandler(transfer)->index()); } } } void TransfersView::toggleMainGroup() { // show or hide the first group header if there's only one download group int nGroups = model()->rowCount(QModelIndex()); if(nGroups <= 1) { setRootIndex(model()->index(0, 0, QModelIndex())); } else { setRootIndex(QModelIndex()); } header()->setRootIndex(QModelIndex());//HACK: else the header isn't visible with no visible items in the view } void TransfersView::rowsAboutToBeRemoved(const QModelIndex & parent, int start, int end) { Q_UNUSED(parent) Q_UNUSED(start) Q_UNUSED(end) closeExpandableDetails(currentIndex()); } void TransfersView::slotShowHeaderMenu(const QPoint &point) { m_headerMenu->popup(header()->mapToGlobal(point)); } void TransfersView::closeExpandableDetails(const QModelIndex &transferIndex) { TransfersViewDelegate *view_delegate = static_cast (itemDelegate()); if(transferIndex.isValid()) { view_delegate->contractItem(transferIndex); m_editingIndexes.removeAll(transferIndex); } else { view_delegate->contractAll(); m_editingIndexes.clear(); } } void TransfersView::selectionChanged(const QItemSelection & selected, const QItemSelection & deselected) { Q_UNUSED(deselected) if (!selected.indexes().isEmpty()) { TransfersViewDelegate *view_delegate = static_cast(itemDelegate()); KGet::actionCollection()->action("transfer_show_details")->setChecked(view_delegate->isExtended(selected.indexes().first())); } QTreeView::selectionChanged(selected, deselected); } void TransfersView::closeExpandableDetails(const QModelIndex &parent, int rowStart, int rowEnd) { Q_UNUSED(parent) Q_UNUSED(rowStart) Q_UNUSED(rowEnd) TransfersViewDelegate *view_delegate = static_cast (itemDelegate()); view_delegate->contractAll(); m_editingIndexes.clear(); } QWidget *TransfersView::getDetailsWidgetForTransfer(TransferHandler *handler) { QGroupBox *groupBox = new QGroupBox(i18n("Transfer Details")); QVBoxLayout *layout = new QVBoxLayout(groupBox); QWidget *detailsWidget = TransferDetails::detailsWidget(handler); layout->addWidget(detailsWidget); return groupBox; } diff --git a/ui/transfersviewdelegate.cpp b/ui/transfersviewdelegate.cpp index aa70cf76..20ed38a0 100644 --- a/ui/transfersviewdelegate.cpp +++ b/ui/transfersviewdelegate.cpp @@ -1,489 +1,489 @@ /* This file is part of the KDE project Copyright (C) 2006 Dario Massarin Copyright (C) 2007 by Javier Goday Copyright (C) 2008 Lukas Appelhans Copyright (C) 2010 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 "ui/transfersviewdelegate.h" #include "transferdetails.h" #include "ui/contextmenu.h" #include "core/kget.h" #include "core/transferhandler.h" #include "core/transfergrouphandler.h" #include "core/transfertreemodel.h" #include "core/transfertreeselectionmodel.h" #include "settings.h" #include "kget_debug.h" -#include -#include -#include -#include +#include #include -#include -#include -#include +#include #include +#include #include -#include +#include +#include +#include +#include +#include GroupStatusButton::GroupStatusButton(const QModelIndex & index, QWidget * parent) : QToolButton(parent), m_status(None), m_index(index), m_timerId(-1), m_iconSize(22), m_gradientId(0) { setAttribute(Qt::WA_NoSystemBackground); } void GroupStatusButton::checkStateSet() { // qCDebug(KGET_DEBUG) << "GroupStatusButton::checkStateSet"; QToolButton::checkStateSet(); if(isChecked()) { if(m_status == None) m_gradientId = 0.9; m_status = Selecting; } else { if(m_status == None) m_gradientId = 1; m_status = Deselecting; } setMouseTracking(!isChecked()); if(m_timerId == -1) m_timerId = startTimer(100); } void GroupStatusButton::enterEvent(QEvent * event) { Q_UNUSED(event) if(!isChecked()) { m_status = Blinking; if(m_timerId == -1) { m_timerId = startTimer(100); if(m_status == !BlinkingExiting) m_gradientId = 1; } } } void GroupStatusButton::leaveEvent(QEvent * event) { Q_UNUSED(event) if(m_status == Blinking) m_status = BlinkingExiting; } void GroupStatusButton::paintEvent(QPaintEvent * event) { Q_UNUSED(event) QPainter p(this); const int offset = (rect().width() - m_iconSize) / 2; if(m_gradientId == 0) m_gradientId = isChecked() ? 1 : 0.7; QRadialGradient gradient(height() / 2.0, height() / 2.0, height() / 2); QPen pen; if(KGet::selectionModel()->isSelected(m_index)) { gradient.setColorAt(0, palette().color(QPalette::AlternateBase)); gradient.setColorAt(m_gradientId, Qt::transparent); gradient.setColorAt(1, Qt::transparent); pen.setColor(palette().color(QPalette::AlternateBase)); } else { gradient.setColorAt(0, palette().color(QPalette::Highlight)); gradient.setColorAt(m_gradientId, Qt::transparent); gradient.setColorAt(1, Qt::transparent); pen.setColor(palette().color(QPalette::Highlight)); } QRect r = rect().adjusted(0, 0, 0, 1); p.fillRect(r, gradient); p.setRenderHint(QPainter::Antialiasing); if(isChecked()) { pen.setWidth(1); p.setPen(pen); p.drawEllipse(rect().x()+5, rect().y()+4, rect().width()-10, rect().width()-10); } p.drawPixmap(rect().topLeft() + QPoint(offset, offset - 1), icon().pixmap(m_iconSize, isChecked() || m_status == Blinking ? QIcon::Normal : QIcon::Disabled)); } void GroupStatusButton::timerEvent(QTimerEvent *event) { Q_UNUSED(event) if(m_status == Selecting) { m_gradientId+=0.04; if(m_gradientId >= 1) { m_status = None; m_gradientId = 1; killTimer(m_timerId); m_timerId = -1; } } else if(m_status == Deselecting) { m_gradientId-=0.04; if(m_gradientId <= 0.7) { m_status = None; m_gradientId = 0.7; killTimer(m_timerId); m_timerId = -1; } } else if(m_status == Blinking) { if(isChecked()) { m_status = Selecting; m_gradientId = 0.9; return; } m_gradientId-=0.04; if(m_gradientId <= 0.7) { m_gradientId = 1; } } else if(m_status == BlinkingExiting) { m_gradientId-=0.04; if(m_gradientId <= 0.7) { m_status = None; m_gradientId = 0.7; killTimer(m_timerId); m_timerId = -1; } } update(); } GroupStatusEditor::GroupStatusEditor(const QModelIndex &index, QWidget *parent) : QWidget(parent), m_index(index) { setMinimumWidth(80); m_layout = new QHBoxLayout(); m_layout->addStretch(); setLayout(m_layout); m_btGroup = new QButtonGroup(this); m_btGroup->setExclusive(true); m_startBt = new GroupStatusButton(m_index, this); m_startBt->setCheckable(true); m_startBt->setAutoRaise(true); m_startBt->setIcon(QIcon::fromTheme("media-playback-start")); m_startBt->setFixedSize(36, 36); m_startBt->setIconSize(QSize(22, 22)); m_layout->addWidget(m_startBt); m_btGroup->addButton(m_startBt); m_stopBt = new GroupStatusButton(m_index, this); m_stopBt->setCheckable(true); m_stopBt->setAutoRaise(true); m_stopBt->setIcon(QIcon::fromTheme("media-playback-pause")); m_stopBt->setFixedSize(36, 36); m_stopBt->setIconSize(QSize(22, 22)); m_layout->addWidget(m_stopBt); m_btGroup->addButton(m_stopBt); m_stopBt->setChecked(true); m_layout->addStretch(); m_layout->setMargin(1); connect(m_startBt, &GroupStatusButton::toggled, this, &GroupStatusEditor::slotStatusChanged); } void GroupStatusEditor::setRunning(bool running) { if(running == m_startBt->isChecked()) return; if(running) m_startBt->setChecked(true); else m_stopBt->setChecked(true); } bool GroupStatusEditor::isRunning() { return m_startBt->isChecked(); } void GroupStatusEditor::slotStatusChanged() { emit changedStatus(this); } BasicTransfersViewDelegate::BasicTransfersViewDelegate(QAbstractItemView *parent) : KExtendableItemDelegate(parent) { } QWidget *BasicTransfersViewDelegate::createEditor(QWidget *parent, const QStyleOptionViewItem &option, const QModelIndex &index) const { if (index.column() == TransferTreeModel::Status) { GroupStatusEditor *qroupStatusEditor = new GroupStatusEditor(index, parent); connect(qroupStatusEditor, &GroupStatusEditor::changedStatus, this, &BasicTransfersViewDelegate::slotGroupStatusChanged); return qroupStatusEditor; } else { return KExtendableItemDelegate::createEditor(parent, option, index); } } void BasicTransfersViewDelegate::slotGroupStatusChanged(GroupStatusEditor *editor) { commitData(editor); } void BasicTransfersViewDelegate::setEditorData(QWidget *editor, const QModelIndex &index) const { if (index.column() == TransferTreeModel::Status) { GroupStatusEditor *groupEditor = static_cast(editor); groupEditor->setRunning(KGet::model()->itemFromIndex(index)->asGroup()->groupHandler()->status() == JobQueue::Running); } else { KExtendableItemDelegate::setEditorData(editor, index); } } void BasicTransfersViewDelegate::setModelData(QWidget *editor, QAbstractItemModel *model, const QModelIndex &index) const { if (index.column() == TransferTreeModel::Status) { GroupStatusEditor *groupEditor = static_cast(editor); TransferGroupHandler *groupHandler = KGet::model()->itemFromIndex(index)->asGroup()->groupHandler(); if (groupEditor->isRunning()) { groupHandler->start(); } else { groupHandler->stop(); } } else { KExtendableItemDelegate::setModelData(editor, model, index); } } TransfersViewDelegate::TransfersViewDelegate(QAbstractItemView *parent) : BasicTransfersViewDelegate(parent) { Q_ASSERT(qobject_cast(parent)); setExtendPixmap(QIcon::fromTheme("arrow-right").pixmap(16)); setContractPixmap(QIcon::fromTheme("arrow-down").pixmap(16)); } void TransfersViewDelegate::paint(QPainter * painter, const QStyleOptionViewItem & option, const QModelIndex & index) const { TransferTreeModel * transferTreeModel = KGet::model(); ModelItem * item = transferTreeModel->itemFromIndex(index); if (item->isGroup()) { painter->save(); if (option.state & QStyle::State_Selected) { } else { static bool backgroundInitialized = false; static QPixmap groupBackground(64, 35); static QPalette palette(QApplication::palette()); if(!backgroundInitialized || palette!= QApplication::palette()) { const QRect rect = groupBackground.rect(); QPainter p(&groupBackground); QLinearGradient gradient(rect.x(), rect.y(), rect.x(), (rect.y() + rect.height())); gradient.setColorAt(0, QApplication::palette().color(QPalette::Base)); gradient.setColorAt(0.5, QApplication::palette().color(QPalette::AlternateBase).darker(110)); gradient.setColorAt(1, QApplication::palette().color(QPalette::Base)); p.fillRect(rect, gradient); backgroundInitialized = true; } painter->drawTiledPixmap(option.rect, groupBackground); } KExtendableItemDelegate::paint(painter, option, index); painter->restore(); } else { if (KGet::selectionModel()->isSelected(index)) painter->fillRect(option.rect, QApplication::palette().color(option.state & QStyle::State_Active ? QPalette::Active : QPalette::Inactive, QPalette::Highlight)); KExtendableItemDelegate::paint(painter, option, index); if (index.column() == 3 && !isExtended(transferTreeModel->index(index.row(), 0, index.parent()))) { // the percent column TransferHandler *transferHandler = item->asTransfer()->transferHandler(); // following progressbar code has mostly been taken from Qt4 examples/network/torrent/mainview.cpp // Set up a QStyleOptionProgressBar to precisely mimic the // environment of a progress bar. QStyleOptionProgressBar progressBarOption; progressBarOption.state = QStyle::State_Enabled; progressBarOption.direction = QApplication::layoutDirection(); progressBarOption.rect = option.rect; progressBarOption.fontMetrics = QApplication::fontMetrics(); progressBarOption.minimum = 0; progressBarOption.maximum = 100; progressBarOption.textAlignment = Qt::AlignCenter; progressBarOption.textVisible = true; // Set the progress and text values of the style option. int progress = transferHandler->percent(); if (progress >= 0 && progress <= 100) { progressBarOption.progress = progress; progressBarOption.text = QString().sprintf("%d%%", progressBarOption.progress); } else { progressBarOption.text = i18nc("not available", "n/a"); } progressBarOption.rect.setY(progressBarOption.rect.y() + (option.rect.height() - QApplication::fontMetrics().height()) / 2); progressBarOption.rect.setHeight(QApplication::fontMetrics().height()); // Draw the progress bar onto the view. QApplication::style()->drawControl(QStyle::CE_ProgressBar, &progressBarOption, painter); } } /// These lines are just for testing purposes. Uncomment them to show on the view the repaint events. // static int i=0; // qCDebug(KGET_DEBUG) << "paint!!! " << i++ << " " << index.internalPointer() << " " << index.column(); // // painter->drawRect(option.rect); // painter->drawText(option.rect.topLeft(), QString::number(i)); } void TransfersViewDelegate::drawFocus(QPainter * painter, const QStyleOptionViewItem & option, const QRect & rect) const { Q_UNUSED(painter) Q_UNUSED(option) Q_UNUSED(rect) } QSize TransfersViewDelegate::sizeHint(const QStyleOptionViewItem & option, const QModelIndex & index) const { Q_UNUSED(option) TransferTreeModel *transferTreeModel = KGet::model(); ModelItem *item = transferTreeModel->itemFromIndex(index); if (!item) { qCWarning(KGET_DEBUG) << "Sizehint for non-existing item."; return QSize(); } if (transferTreeModel->itemFromIndex(index)->isGroup()) { return QSize(0, 35); } else { QSize ret(KExtendableItemDelegate::sizeHint(option, index)); ret.rheight() += 8; return ret; } } bool TransfersViewDelegate::editorEvent(QEvent * event, QAbstractItemModel * model, const QStyleOptionViewItem & option, const QModelIndex & index) { Q_UNUSED(model) Q_UNUSED(option) if (event->type() == QEvent::MouseButtonRelease) { QMouseEvent * mouseEvent = static_cast(event); if (mouseEvent->button() == Qt::RightButton) { // qCDebug(KGET_DEBUG) << "TransfersViewDelegate::editorEvent() -> rightClick"; QMenu *popup = nullptr; TransferTreeModel * transferTreeModel = KGet::model(); ModelItem * item = transferTreeModel->itemFromIndex(index); if (item->isGroup()) { // qCDebug(KGET_DEBUG) << "isTransferGroup = true"; TransferGroupHandler * transferGroupHandler = item->asGroup()->groupHandler(); popup = ContextMenu::createTransferGroupContextMenu(transferGroupHandler, qobject_cast(this)); } else { // qCDebug(KGET_DEBUG) << "isTransferGroup = false"; TransferHandler * transferHandler = item->asTransfer()->transferHandler(); popup = ContextMenu::createTransferContextMenu(transferHandler, qobject_cast(this)); } if (popup) { popup->exec(QCursor::pos()); popup->deleteLater(); } } } return false; } diff --git a/ui/tray.cpp b/ui/tray.cpp index 4818c0db..d21817cf 100644 --- a/ui/tray.cpp +++ b/ui/tray.cpp @@ -1,99 +1,98 @@ /* This file is part of the KDE project Copyright (C) 2009 by Fabian Henze 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 "ui/tray.h" #include "mainwindow.h" #include "ui/newtransferdialog.h" #include "kget_debug.h" -#include +#include -#include -#include -#include -#include -#include +#include +#include +#include +#include #include /** class Tray * Reimplementation of the KStatusNotifierItem class */ Tray::Tray(MainWindow * parent) : KStatusNotifierItem(parent) { // set up the context menu QMenu * cm = contextMenu(); cm->addAction( parent->actionCollection()->action("new_download") ); cm->addAction( parent->actionCollection()->action("import_links") ); cm->addSeparator(); cm->addAction( parent->actionCollection()->action("start_all_download") ); cm->addAction( parent->actionCollection()->action("stop_all_download") ); cm->addSeparator(); cm->addAction( parent->actionCollection()->action("konqueror_integration") ); cm->addAction( parent->actionCollection()->action("options_configure") ); // Set up basic tray parameters setCategory(ApplicationStatus); setIconByName("kget"); setTitle(i18n("KGet")); setContextMenu(cm); setAssociatedWidget(parent); setToolTipIconByName("kget"); setToolTipTitle(i18n("Download Manager")); // Not of much use atm, but maybe we want to set this later? // setToolTipSubTitle("[..]"); // filter middle mouse clicks to ask scheduler to paste URL connect(this, &Tray::secondaryActivateRequested, this, &Tray::slotActivated); } // filter middle mouse clicks to ask scheduler to paste URL void Tray::slotActivated() { // Here we paste the transfer QString newtransfer = QApplication::clipboard()->text(); newtransfer = newtransfer.trimmed(); if(!newtransfer.isEmpty()) NewTransferDialogHandler::showNewTransferDialog(QUrl(newtransfer)); } // display a play icon when downloading and // switch between Active or Passive state void Tray::setDownloading( bool downloading ) { qCDebug(KGET_DEBUG) << "Tray::setDownloading"; if (downloading) { if (status() == KStatusNotifierItem::Active) return; setStatus(KStatusNotifierItem::Active); setOverlayIconByName("media-playback-start"); } else { if (status() == KStatusNotifierItem::Passive) return; setStatus(KStatusNotifierItem::Passive); setOverlayIconByName(QString()); } } bool Tray::isDownloading() { // KStatusNotifierItem::NeedsAttention is not handled here, // as we do not use it. return (status() == KStatusNotifierItem::Active); }