diff --git a/CMakeLists.txt b/CMakeLists.txt index 02cdb6d7..54fc127d 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -1,338 +1,338 @@ cmake_minimum_required(VERSION 3.0) # KDE Application Version, managed by release script set(KDE_APPLICATIONS_VERSION_MAJOR "20") set(KDE_APPLICATIONS_VERSION_MINOR "03") 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.55.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 KIO Notifications NotifyConfig Parts Service TextWidgets Wallet WidgetsAddons WindowSystem XmlGui KDELibs4Support # KLocale ) #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) + +add_definitions(-DQT_NO_URL_CAST_FROM_STRING) 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 "https://download.kde.org/stable/qca-qt5/" TYPE REQUIRED) find_package(Gpgmepp 1.7.0) set_package_properties(Gpgmepp_FOUND PROPERTIES DESCRIPTION "The GpgME++ library" URL "https://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::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::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}) if (${ECM_VERSION} STRGREATER "5.58.0") install(FILES kget.categories DESTINATION ${KDE_INSTALL_LOGGINGCATEGORIESDIR}) else() install(FILES kget.categories DESTINATION ${KDE_INSTALL_CONFDIR}) endif() install(FILES org.kde.kget.appdata.xml DESTINATION ${CMAKE_INSTALL_METAINFODIR}) feature_summary(WHAT ALL FATAL_ON_MISSING_REQUIRED_PACKAGES) diff --git a/core/transfertreemodel.cpp b/core/transfertreemodel.cpp index ee7164f1..e2ee7679 100644 --- a/core/transfertreemodel.cpp +++ b/core/transfertreemodel.cpp @@ -1,796 +1,796 @@ /* 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 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())); + m_mimeType = QIcon::fromTheme(KIO::iconNameForUrl(m_transferHandler->dest())); } 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; std::sort(sortedIndexes.begin(), sortedIndexes.end(), [](const QModelIndex &a, const QModelIndex &b) { return b < a; }); 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) << "Unsupported 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/extensions/konqueror/kget_plug_in.cpp b/extensions/konqueror/kget_plug_in.cpp index 838deb13..f932443f 100644 --- a/extensions/konqueror/kget_plug_in.cpp +++ b/extensions/konqueror/kget_plug_in.cpp @@ -1,243 +1,243 @@ /* This file is part of the KDE project Copyright (C) 2002 Patrick Charbonnier Copyright (C) 2002 Carsten Pfeiffer Copyright (C) 2007 Urs Wolfer Copyright (C) 2010 Dawit Alemayehu 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 "kget_plug_in.h" #include "kget_interface.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #define QL1S(x) QLatin1String(x) K_PLUGIN_FACTORY(KGetPluginFactory, registerPlugin();) K_EXPORT_PLUGIN(KGetPluginFactory("kgetplugin")) static QWidget* partWidget(QObject* obj) { KParts::ReadOnlyPart* part = qobject_cast(obj); return part ? part->widget() : nullptr; } KGetPlugin::KGetPlugin(QObject *parent, const QVariantList&) :KParts::Plugin(parent) { KActionMenu *menu = new KActionMenu(QIcon::fromTheme("kget"), i18n("Download Manager"), actionCollection()); actionCollection()->addAction("kget_menu", menu); menu->setDelayed( false ); connect( menu->menu(), SIGNAL(aboutToShow()), SLOT(showPopup())); m_dropTargetAction = new KToggleAction(i18n("Show Drop Target"), actionCollection()); connect(m_dropTargetAction, SIGNAL(triggered()), this, SLOT(slotShowDrop())); actionCollection()->addAction(QL1S("show_drop"), m_dropTargetAction); menu->addAction(m_dropTargetAction); QAction *showLinksAction = actionCollection()->addAction(QL1S("show_links")); showLinksAction->setText(i18n("List All Links")); connect(showLinksAction, SIGNAL(triggered()), SLOT(slotShowLinks())); menu->addAction(showLinksAction); QAction *showSelectedLinksAction = actionCollection()->addAction(QL1S("show_selected_links")); showSelectedLinksAction->setText(i18n("List Selected Links")); connect(showSelectedLinksAction, SIGNAL(triggered()), SLOT(slotShowSelectedLinks())); menu->addAction(showSelectedLinksAction); // Hide this plugin if the parent part does not support either // The FileInfo or Html extensions... if (!KParts::HtmlExtension::childObject(parent) && !KParts::FileInfoExtension::childObject(parent)) menu->setVisible(false); } KGetPlugin::~KGetPlugin() { } static bool hasDropTarget() { bool found = false; if (QDBusConnection::sessionBus().interface()->isServiceRegistered("org.kde.kget")) { OrgKdeKgetMainInterface kgetInterface("org.kde.kget", "/KGet", QDBusConnection::sessionBus()); QDBusReply reply = kgetInterface.dropTargetVisible(); if (reply.isValid()) { found = reply.value(); } } return found; } void KGetPlugin::showPopup() { // Check for HtmlExtension support... KParts::HtmlExtension* htmlExtn = KParts::HtmlExtension::childObject(parent()); if (htmlExtn) { KParts::SelectorInterface* selector = qobject_cast(htmlExtn); if (selector) { m_dropTargetAction->setChecked(hasDropTarget()); const KParts::SelectorInterface::QueryMethods methods = selector->supportedQueryMethods(); bool enable = (methods & KParts::SelectorInterface::EntireContent); actionCollection()->action(QL1S("show_links"))->setEnabled(enable); enable = (htmlExtn->hasSelection() && (methods & KParts::SelectorInterface::SelectedContent)); actionCollection()->action(QL1S("show_selected_links"))->setEnabled(enable); enable = (actionCollection()->action(QL1S("show_links"))->isEnabled() || actionCollection()->action(QL1S("show_selected_links"))->isEnabled()); actionCollection()->action(QL1S("show_drop"))->setEnabled(enable); return; } } // Check for FileInfoExtension support... KParts::FileInfoExtension* fileinfoExtn = KParts::FileInfoExtension::childObject(parent()); if (fileinfoExtn) { m_dropTargetAction->setChecked(hasDropTarget()); const KParts::FileInfoExtension::QueryModes modes = fileinfoExtn->supportedQueryModes(); bool enable = (modes & KParts::FileInfoExtension::AllItems); actionCollection()->action(QL1S("show_links"))->setEnabled(enable); enable = (fileinfoExtn->hasSelection() && (modes & KParts::FileInfoExtension::SelectedItems)); actionCollection()->action(QL1S("show_selected_links"))->setEnabled(enable); enable = (actionCollection()->action(QL1S("show_links"))->isEnabled() || actionCollection()->action(QL1S("show_selected_links"))->isEnabled()); actionCollection()->action(QL1S("show_drop"))->setEnabled(enable); return; } actionCollection()->action(QL1S("show_selected_links"))->setEnabled(false); actionCollection()->action(QL1S("show_links"))->setEnabled(false); actionCollection()->action(QL1S("show_drop"))->setEnabled(false); if (m_dropTargetAction->isChecked()) m_dropTargetAction->setChecked(false); } void KGetPlugin::slotShowDrop() { if (!QDBusConnection::sessionBus().interface()->isServiceRegistered("org.kde.kget")) { KRun::runCommand("kget --showDropTarget --hideMainWindow", "kget", "kget", partWidget(parent())); } else { OrgKdeKgetMainInterface kgetInterface("org.kde.kget", "/KGet", QDBusConnection::sessionBus()); kgetInterface.setDropTargetVisible(m_dropTargetAction->isChecked()); } } void KGetPlugin::slotShowLinks() { getLinks(false); } void KGetPlugin::slotShowSelectedLinks() { getLinks(true); } void KGetPlugin::slotImportLinks() { if (m_linkList.isEmpty()) { KMessageBox::sorry(partWidget(parent()), i18n("No downloadable links were found."), i18n("No Links")); return; } // Remove any duplicates links from the list... m_linkList.removeDuplicates(); if (!QDBusConnection::sessionBus().interface()->isServiceRegistered("org.kde.kget") && KToolInvocation::kdeinitExecWait("kget") != 0) { KMessageBox::sorry(partWidget(parent()), i18n("Unable to communicate with the KGet download manager."), i18n("Communication Error")); return; } OrgKdeKgetMainInterface kgetInterface("org.kde.kget", "/KGet", QDBusConnection::sessionBus()); kgetInterface.importLinks(m_linkList); } void KGetPlugin::getLinks(bool selectedOnly) { KParts::HtmlExtension* htmlExtn = KParts::HtmlExtension::childObject(parent()); if (htmlExtn) { KParts::SelectorInterface* selector = qobject_cast(htmlExtn); if (selector) { m_linkList.clear(); const QUrl baseUrl = htmlExtn->baseUrl(); const QString query = QL1S("a[href], img[src], audio[src], video[src], embed[src], object[data]"); const KParts::SelectorInterface::QueryMethod method = (selectedOnly ? KParts::SelectorInterface::SelectedContent: KParts::SelectorInterface::EntireContent); const QList elements = selector->querySelectorAll(query, method); QString attr; Q_FOREACH(const KParts::SelectorInterface::Element& element, elements) { if (element.hasAttribute(QL1S("href"))) attr = QL1S("href"); else if (element.hasAttribute(QL1S("src"))) attr = QL1S("src"); else if (element.hasAttribute(QL1S("data"))) attr = QL1S("data"); - const QUrl resolvedUrl (baseUrl.resolved(element.attribute(attr))); + const QUrl resolvedUrl (baseUrl.resolved(QUrl(element.attribute(attr)))); // Only select valid and non-local links for download... if (resolvedUrl.isValid() && !resolvedUrl.isLocalFile() && !resolvedUrl.host().isEmpty()) { if (element.hasAttribute(QL1S("type"))) m_linkList << QString(QL1S("url ") + resolvedUrl.url() + QL1S(" type ") + element.attribute(QL1S("type"))); else m_linkList << resolvedUrl.url(); } } } slotImportLinks(); } KParts::FileInfoExtension* fileinfoExtn = KParts::FileInfoExtension::childObject(parent()); if (fileinfoExtn) { m_linkList.clear(); const KParts::FileInfoExtension::QueryMode mode = (selectedOnly ? KParts::FileInfoExtension::SelectedItems: KParts::FileInfoExtension::AllItems); const KFileItemList items = fileinfoExtn->queryFor(mode); Q_FOREACH(const KFileItem& item, items) { const QUrl url = item.url(); // Only select valid and non local links for download... if (item.isReadable() && item.isFile() && !item.isLocalFile() && !url.host().isEmpty()) { if (item.mimetype().isEmpty()) m_linkList << url.url(); else m_linkList << QString(QL1S("url ") + url.url() + QL1S(" type ") + item.mimetype()); } } slotImportLinks(); } } #include "kget_plug_in.moc" diff --git a/mainwindow.cpp b/mainwindow.cpp index b078ebe8..0999e92c 100644 --- a/mainwindow.cpp +++ b/mainwindow.cpp @@ -1,1225 +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 #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 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()); + NewTransferDialogHandler::showNewTransferDialog(list.first()); if (msgBoxResult == 4) //Load KGet::load(list.first().url()); } else { if (list.count() == 1) - NewTransferDialogHandler::showNewTransferDialog(list.first().url()); + NewTransferDialogHandler::showNewTransferDialog(list.first()); else NewTransferDialogHandler::showNewTransferDialog(list); } } else { NewTransferDialogHandler::showNewTransferDialog(QUrl()); } } diff --git a/transfer-plugins/bittorrent/btsettingswidget.cpp b/transfer-plugins/bittorrent/btsettingswidget.cpp index 38d01d48..68485c83 100644 --- a/transfer-plugins/bittorrent/btsettingswidget.cpp +++ b/transfer-plugins/bittorrent/btsettingswidget.cpp @@ -1,67 +1,67 @@ /* 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 "btsettingswidget.h" #include "kget_macro.h" #include "bittorrentsettings.h" #include "kget_debug.h" KGET_EXPORT_PLUGIN_CONFIG(BTSettingsWidget) BTSettingsWidget::BTSettingsWidget(QWidget * parent, const QVariantList &args) : KCModule(/*KGetFactory::componentData(),*/ parent, args) { setupUi(this); connect(portBox, SIGNAL(valueChanged(int)), SLOT(changed())); connect(uploadBox, SIGNAL(valueChanged(int)), SLOT(changed())); connect(downloadBox, SIGNAL(valueChanged(int)), SLOT(changed())); connect(torrentEdit, SIGNAL(textChanged(QString)), SLOT(changed())); connect(tempEdit, SIGNAL(textChanged(QString)), SLOT(changed())); connect(preallocBox, SIGNAL(stateChanged(int)), SLOT(changed())); connect(utpBox, SIGNAL(stateChanged(int)), SLOT(changed())); } void BTSettingsWidget::load() { torrentEdit->setMode(KFile::Directory); tempEdit->setMode(KFile::Directory); defaults(); } void BTSettingsWidget::save() { qCDebug(KGET_DEBUG) << "Save Bittorrent-config"; BittorrentSettings::setPort(portBox->value()); BittorrentSettings::setUploadLimit(uploadBox->value()); BittorrentSettings::setDownloadLimit(downloadBox->value()); BittorrentSettings::setTorrentDir(torrentEdit->url().url()); BittorrentSettings::setTmpDir(tempEdit->url().url()); BittorrentSettings::setPreAlloc(preallocBox->isChecked()); BittorrentSettings::setEnableUTP(utpBox->isChecked()); BittorrentSettings::self()->save(); } void BTSettingsWidget::defaults() { portBox->setValue(BittorrentSettings::port()); uploadBox->setValue(BittorrentSettings::uploadLimit()); downloadBox->setValue(BittorrentSettings::downloadLimit()); - torrentEdit->setUrl(BittorrentSettings::torrentDir()); - tempEdit->setUrl(BittorrentSettings::tmpDir()); + torrentEdit->setUrl(QUrl::fromLocalFile(BittorrentSettings::torrentDir())); + tempEdit->setUrl(QUrl::fromLocalFile(BittorrentSettings::tmpDir())); preallocBox->setChecked(BittorrentSettings::preAlloc()); utpBox->setChecked(BittorrentSettings::enableUTP()); } #include "btsettingswidget.moc" diff --git a/transfer-plugins/bittorrent/bttransfer.cpp b/transfer-plugins/bittorrent/bttransfer.cpp index d6f82289..a51dec25 100644 --- a/transfer-plugins/bittorrent/bttransfer.cpp +++ b/transfer-plugins/bittorrent/bttransfer.cpp @@ -1,836 +1,837 @@ /* This file is part of the KDE project Copyright (C) 2007-2008 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 "bttransfer.h" #include "bittorrentsettings.h" #include "bttransferhandler.h" //#include "btchunkselector.h" #include "advanceddetails/monitor.h" #include "core/kget.h" #include "core/filemodel.h" #include "core/download.h" #include "kget_version.h" #include #include #include #include #include #include #include #include #include #include #include #include #include "kget_debug.h" #include #include #include #include #include #include #include #include #include #include #ifdef ERROR #undef ERROR #endif BTTransfer::BTTransfer(TransferGroup* parent, TransferFactory* factory, Scheduler* scheduler, const QUrl &src, const QUrl& dest, const QDomElement * e) : Transfer(parent, factory, scheduler, src, dest, e), torrent(nullptr), m_tmp(QStandardPaths::writableLocation(QStandardPaths::DataLocation) + QStringLiteral("/tmp/")), m_ready(false), m_downloadFinished(false), m_movingFile(false), m_fileModel(nullptr), m_updateCounter(0) { QString tmpDirName = QStandardPaths::writableLocation(QStandardPaths::DataLocation) + QStringLiteral("/tmp/"); // make sure that the /tmp directory exists (earlier this used to be handled by KStandardDirs) if (!QFileInfo::exists(tmpDirName)) { QDir().mkpath(tmpDirName); } m_directory = KIO::upUrl(m_dest);//FIXME test setCapabilities(Transfer::Cap_Moving | Transfer::Cap_Renaming | Transfer::Cap_Resuming | Transfer::Cap_SpeedLimit); } BTTransfer::~BTTransfer() { if (torrent && m_ready) torrent->setMonitor(nullptr); delete torrent; } void BTTransfer::deinit(Transfer::DeleteOptions options) { qDebug() << "****************************DEINIT"; if (torrent && (options & Transfer::DeleteFiles)) {//FIXME: Also delete when torrent does not exist torrent->deleteDataFiles(); } if (options & Transfer::DeleteTemporaryFiles) { QDir tmpDir(m_tmp); qCDebug(KGET_DEBUG) << m_tmp + m_source.fileName().remove(".torrent"); tmpDir.rmdir(m_source.fileName().remove(".torrent") + "/dnd"); tmpDir.cd(m_source.fileName().remove(".torrent")); QStringList list = tmpDir.entryList(); foreach (const QString &file, list) { tmpDir.remove(file); } tmpDir.cdUp(); tmpDir.rmdir(m_source.fileName().remove(".torrent")); //only remove the .torrent file if it was downloaded by KGet if (!m_tmpTorrentFile.isEmpty()) { qCDebug(KGET_DEBUG) << "Removing" << m_tmpTorrentFile; QFile torrentFile(m_tmpTorrentFile); torrentFile.remove(); } } } /** Reimplemented functions from Transfer-Class **/ bool BTTransfer::isStalled() const { return (status() == Job::Running) && (downloadSpeed() == 0) && torrent && torrent->getStats().status == bt::STALLED; } bool BTTransfer::isWorking() const { if (!torrent) return false; const bt::TorrentStats stats = torrent->getStats(); return (stats.status != bt::ERROR) && (stats.status != bt::STALLED) && (stats.status != bt::NO_SPACE_LEFT) && (stats.status != bt::INVALID_STATUS); } void BTTransfer::start() { if (m_movingFile) { return; } if (!torrent) { if (!m_source.isLocalFile()) { qCDebug(KGET_DEBUG) << m_dest.path(); QString tmpDirName = QStandardPaths::writableLocation(QStandardPaths::DataLocation) + QStringLiteral("/tmp/"); m_tmpTorrentFile = tmpDirName + m_dest.fileName(); Download *download = new Download(m_source, QUrl::fromLocalFile(m_tmpTorrentFile)); setStatus(Job::Stopped, i18n("Downloading Torrent File...."), SmallIcon("document-save")); setTransferChange(Tc_Status, true); //m_source = tmpDirName + m_source.fileName(); connect(download, SIGNAL(finishedSuccessfully(QUrl,QByteArray)), SLOT(btTransferInit(QUrl,QByteArray))); } else btTransferInit(); } else startTorrent(); } bool BTTransfer::setDirectory(const QUrl &newDirectory) { //check if the newDestination is the same as the old QUrl temp = newDirectory; temp = temp.adjusted(QUrl::StripTrailingSlash); temp.setPath(temp.path() + '/' + (torrent->getStats().torrent_name)); if (newDirectory.isValid() && (newDirectory != dest()) && (temp != dest())) { if (torrent->changeOutputDir(newDirectory.url(QUrl::PreferLocalFile), bt::TorrentInterface::MOVE_FILES)) { connect(torrent, SIGNAL(aboutToBeStarted(bt::TorrentInterface*,bool&)), this, SLOT(newDestResult())); m_movingFile = true; m_directory = newDirectory; m_dest = m_directory; m_dest = m_dest.adjusted(QUrl::StripTrailingSlash); m_dest.setPath(m_dest.path() + '/' + (torrent->getStats().torrent_name)); setStatus(Job::Stopped, i18nc("changing the destination of the file", "Changing destination"), SmallIcon("media-playback-pause")); setTransferChange(Tc_Status, true); return true; } } m_movingFile = false; return false; } void BTTransfer::newDestResult() { disconnect(torrent, SIGNAL(aboutToBeStarted(bt::TorrentInterface*,bool&)), this, SLOT(newDestResult())); m_movingFile = false; setStatus(Job::Running, i18nc("transfer state: downloading", "Downloading...."), SmallIcon("media-playback-start")); setTransferChange(Tc_FileName | Tc_Status, true); } void BTTransfer::stop() { if (m_movingFile) return; if (m_ready) { stopTorrent(); } } /**Own public functions**/ void BTTransfer::update() { if (m_movingFile) { return; } if (torrent) { QStringList files; if (torrent->hasMissingFiles(files)) { torrent->recreateMissingFiles(); } updateTorrent(); } else timer.stop(); } void BTTransfer::load(const QDomElement *element) { Transfer::load(element); if ((m_totalSize == m_downloadedSize) && (m_totalSize != 0)) { setStatus(Job::Stopped, i18nc("transfer state: finished", "Finished"), SmallIcon("dialog-ok")); } } // void BTTransfer::save(const QDomElement &element) // { // qCDebug(KGET_DEBUG); // // QDomElement e = element; // // Transfer::save(e); // } /**Public functions of BTTransfer**/ void BTTransfer::setPort(int port) { bt::Globals::instance().getTCPServer().changePort(port); if (BittorrentSettings::enableUTP()) bt::Globals::instance().getUTPServer().changePort(port + 1); } void BTTransfer::setSpeedLimits(int ulLimit, int dlLimit) { qCDebug(KGET_DEBUG); if (!torrent) return; torrent->setTrafficLimits(ulLimit * 1000, dlLimit * 1000); } void BTTransfer::addTracker(const QString &url) { qCDebug(KGET_DEBUG); if(torrent->getStats().priv_torrent) { KMessageBox::sorry(nullptr, i18n("Cannot add a tracker to a private torrent.")); return; } - if(!QUrl(url).isValid()) { + QUrl u(url); + if(!u.isValid()) { KMessageBox::error(nullptr, i18n("Malformed URL.")); return; } - torrent->getTrackersList()->addTracker(url,true); + torrent->getTrackersList()->addTracker(u,true); } /**Private functions**/ void BTTransfer::startTorrent() { if (m_ready) { //qCDebug(KGET_DEBUG) << "Going to download that stuff :-0"; setSpeedLimits(uploadLimit(Transfer::InvisibleSpeedLimit), downloadLimit(Transfer::InvisibleSpeedLimit));//Set traffic-limits before starting torrent->setMonitor(this); torrent->start(); timer.start(250); if (chunksTotal() == chunksDownloaded()/* && !m_downloadFinished*/) { slotDownloadFinished(torrent); } else { setStatus(Job::Running, i18nc("transfer state: downloading", "Downloading...."), SmallIcon("media-playback-start")); } m_totalSize = torrent->getStats().total_bytes_to_download; setTransferChange(Tc_Status | Tc_TrackersList | Tc_TotalSize, true); updateFilesStatus(); } } void BTTransfer::stopTorrent() { torrent->stop(); torrent->setMonitor(nullptr); m_downloadSpeed = 0; timer.stop(); if (m_downloadFinished) { setStatus(Job::Stopped, i18nc("transfer state: finished", "Finished"), SmallIcon("dialog-ok")); } else { setStatus(Job::Stopped, i18nc("transfer state: stopped", "Stopped"), SmallIcon("process-stop")); } setTransferChange(Tc_Status, true); updateFilesStatus(); } void BTTransfer::updateTorrent() { //qCDebug(KGET_DEBUG) << "Update torrent"; bt::UpdateCurrentTime(); bt::AuthenticationMonitor::instance().update(); torrent->update(); ChangesFlags changesFlags = 0; if (m_downloadedSize != (m_downloadedSize = torrent->getStats().bytes_downloaded)) changesFlags |= Tc_DownloadedSize; if (m_uploadSpeed != static_cast(torrent->getStats().upload_rate)) { m_uploadSpeed = torrent->getStats().upload_rate; changesFlags |= Tc_UploadSpeed; } if (m_downloadSpeed != static_cast(torrent->getStats().download_rate)) { m_downloadSpeed = torrent->getStats().download_rate; changesFlags |= Tc_DownloadSpeed; } int percent = (chunksDownloaded() * 100) / chunksTotal(); if (m_percent != percent) { m_percent = percent; changesFlags |= Tc_Percent; } setTransferChange(changesFlags, true); //update the files status every 3 seconds if (!m_updateCounter) { updateFilesStatus(); m_updateCounter = 12; } --m_updateCounter; } void BTTransfer::updateFilesStatus() { const Job::Status currentStatus = this->status(); if (!torrent) { return; } const bt::TorrentStats *stats = &torrent->getStats(); if (stats->multi_file_torrent) { QHash::const_iterator it; QHash::const_iterator itEnd = m_files.constEnd(); for (it = m_files.constBegin(); it != itEnd; ++it) { QModelIndex status = m_fileModel->index(it.key(), FileItem::Status); if (!(*it)->doNotDownload() && (currentStatus == Job::Running)) { m_fileModel->setData(status, Job::Running); } else { m_fileModel->setData(status, Job::Stopped); } if (qFuzzyCompare((*it)->getDownloadPercentage(), 100.0f)) { m_fileModel->setData(status, Job::Finished); } } } else { QModelIndexList indexes = fileModel()->fileIndexes(FileItem::Status); if (indexes.count() != 1) { return; } QModelIndex index = indexes.first(); if (stats->bytes_left_to_download) { if (currentStatus == Job::Running) { fileModel()->setData(index, Job::Running); } else { fileModel()->setData(index, Job::Stopped); } } else { fileModel()->setData(index, Job::Finished); } } } void BTTransfer::btTransferInit(const QUrl &src, const QByteArray &data) { Q_UNUSED(data) qCDebug(KGET_DEBUG); if (src != m_source && !src.isEmpty()) m_source = src; QFile file(m_source.toLocalFile()); if (!file.open(QIODevice::ReadOnly)) { setError(i18n("Torrent file does not exist"), SmallIcon("dialog-cancel"), Job::NotSolveable); setTransferChange(Tc_Status, true); return; } setStatus(Job::Stopped, i18n("Analyzing torrent...."), SmallIcon("document-preview")); // jpetso says: you should probably use the "process-working" icon here (from the animations category), but that's a multi-frame PNG so it's hard for me to test setTransferChange(Tc_Status, true); bt::InitLog(QStandardPaths::writableLocation(QStandardPaths::DataLocation) + QStringLiteral("/torrentlog.log"), false, false);//initialize the torrent-log bt::SetClientInfo("KGet", KGET_VERSION_MAJOR, KGET_VERSION_MINOR, KGET_VERSION_PATCH, bt::NORMAL, "KG");//Set client info to KGet bt::Uint16 i = 0; while (!bt::Globals::instance().initTCPServer(BittorrentSettings::port() + i) && i < 10) i++; if (i == 10) { setError(i18n("Cannot initialize port..."), SmallIcon("dialog-cancel")); setTransferChange(Tc_Status); return; } if (BittorrentSettings::enableUTP()) { while (!bt::Globals::instance().initUTPServer(BittorrentSettings::port() + i) && i < 10) //We don't care if it fails for now as UTP is experimental... i++; } QDir tmpDir(m_tmp + m_source.fileName().remove(".torrent")); if (tmpDir.exists()) { tmpDir.remove("torrent"); } try { torrent = new bt::TorrentControl(); if (!BittorrentSettings::tmpDir().isEmpty() && QFileInfo(BittorrentSettings::tmpDir()).isDir()) { m_tmp = BittorrentSettings::tmpDir(); } m_ready = true; qDebug() << "Source:" << m_source.path() << "Destination:" << m_dest.path(); m_dest = m_dest.adjusted(QUrl::StripTrailingSlash); torrent->init(nullptr, file.readAll(), m_tmp + m_source.fileName().remove(".torrent"), QUrl::fromLocalFile(m_dest.adjusted(QUrl::RemoveFilename).path()).toLocalFile()); m_dest = QUrl::fromLocalFile(torrent->getStats().output_path); if (!torrent->getStats().multi_file_torrent && (m_dest.fileName() != torrent->getStats().torrent_name))//TODO check if this is needed, so if that case is true at some point { m_dest = m_dest.adjusted(QUrl::StripTrailingSlash); m_dest.setPath(m_dest.path() + '/' + (torrent->getStats().torrent_name)); } torrent->createFiles(); torrent->setPreallocateDiskSpace(BittorrentSettings::preAlloc()); connect(torrent, SIGNAL(stoppedByError(bt::TorrentInterface*,QString)), SLOT(slotStoppedByError(bt::TorrentInterface*,QString))); connect(torrent, SIGNAL(finished(bt::TorrentInterface*)), this, SLOT(slotDownloadFinished(bt::TorrentInterface*))); //FIXME connect(tc,SIGNAL(corruptedDataFound(bt::TorrentInterface*)), this, SLOT(emitCorruptedData(bt::TorrentInterface*)));//TODO: Fix it } catch (bt::Error &err) { m_ready = false; torrent->deleteLater(); torrent = nullptr; setError(err.toString(), SmallIcon("dialog-cancel"), Job::NotSolveable); setTransferChange(Tc_Status); return; } startTorrent(); connect(&timer, SIGNAL(timeout()), SLOT(update())); } void BTTransfer::slotStoppedByError(const bt::TorrentInterface* &error, const QString &errormsg) { Q_UNUSED(error) stop(); setError(errormsg, SmallIcon("dialog-cancel"), Job::NotSolveable); setTransferChange(Tc_Status); } void BTTransfer::slotDownloadFinished(bt::TorrentInterface* ti) { qCDebug(KGET_DEBUG) << "Start seeding *********************************************************************"; Q_UNUSED(ti) m_downloadFinished = true; //timer.stop(); setStatus(Job::FinishedKeepAlive, i18nc("Transfer status: seeding", "Seeding...."), SmallIcon("media-playback-start")); setTransferChange(Tc_Status, true); } /**Property-Functions**/ QList BTTransfer::trackersList() const { if (!torrent) return QList(); QList trackers; foreach (bt::TrackerInterface * tracker, torrent->getTrackersList()->getTrackers()) trackers << tracker->trackerURL(); return trackers; } int BTTransfer::sessionBytesDownloaded() const { if (!torrent) return -1; return torrent->getStats().session_bytes_downloaded; } int BTTransfer::sessionBytesUploaded() const { if (!torrent) return -1; return torrent->getStats().session_bytes_uploaded; } int BTTransfer::chunksTotal() const { if (!torrent) return -1; return torrent->getTorrent().getNumChunks(); } int BTTransfer::chunksDownloaded() const { if (!torrent) return -1; return torrent->downloadedChunksBitSet().numOnBits(); } int BTTransfer::chunksExcluded() const { if (!torrent) return -1; return torrent->excludedChunksBitSet().numOnBits(); } int BTTransfer::chunksLeft() const { if (!torrent) return -1; return chunksTotal() - chunksDownloaded(); } int BTTransfer::seedsConnected() const { if (!torrent) return -1; return torrent->getStats().seeders_connected_to; } int BTTransfer::seedsDisconnected() const { if (!torrent) return -1; return torrent->getStats().seeders_total; } int BTTransfer::leechesConnected() const { if (!torrent) return -1; return torrent->getStats().leechers_connected_to; } int BTTransfer::leechesDisconnected() const { if (!torrent) return -1; return torrent->getStats().leechers_total; } int BTTransfer::elapsedTime() const { if (!torrent) return -1; return torrent->getRunningTimeDL(); } int BTTransfer::remainingTime() const { if (!torrent) return Transfer::remainingTime(); return torrent->getETA(); } bt::TorrentControl * BTTransfer::torrentControl() { return torrent; } bool BTTransfer::ready() { return m_ready; } void BTTransfer::downloadRemoved(bt::ChunkDownloadInterface* cd) { if (static_cast(handler())->torrentMonitor()) static_cast(handler())->torrentMonitor()->downloadRemoved(cd); setTransferChange(Tc_ChunksDownloaded | Tc_ChunksExcluded | Tc_ChunksLeft, true); } void BTTransfer::downloadStarted(bt::ChunkDownloadInterface* cd) { if (static_cast(handler())->torrentMonitor()) static_cast(handler())->torrentMonitor()->downloadStarted(cd); setTransferChange(Tc_ChunksDownloaded | Tc_ChunksExcluded | Tc_ChunksLeft, true); } void BTTransfer::peerAdded(bt::PeerInterface* peer) { if (static_cast(handler())->torrentMonitor()) static_cast(handler())->torrentMonitor()->peerAdded(peer); setTransferChange(Tc_SeedsConnected | Tc_SeedsDisconnected | Tc_LeechesConnected | Tc_LeechesDisconnected, true); } void BTTransfer::peerRemoved(bt::PeerInterface* peer) { if (static_cast(handler())->torrentMonitor()) static_cast(handler())->torrentMonitor()->peerRemoved(peer); setTransferChange(Tc_SeedsConnected | Tc_SeedsDisconnected | Tc_LeechesConnected | Tc_LeechesDisconnected, true); } void BTTransfer::stopped() { if (static_cast(handler())->torrentMonitor()) static_cast(handler())->torrentMonitor()->stopped(); } void BTTransfer::destroyed() { if (static_cast(handler())->torrentMonitor()) static_cast(handler())->torrentMonitor()->destroyed(); } QList BTTransfer::files() const { QList urls; if (!torrent) { return urls; } //multiple files if (torrent->getStats().multi_file_torrent) { for (uint i = 0; i < torrent->getNumFiles(); ++i) { const QString path = torrent->getTorrentFile(i).getPathOnDisk(); urls.append(QUrl(path)); } } //one single file else { QUrl temp = m_dest; if (m_dest.fileName() != torrent->getStats().torrent_name)//TODO check if the body is ever entered! { temp = temp.adjusted(QUrl::StripTrailingSlash); temp.setPath(temp.path() + '/' + (torrent->getStats().torrent_name)); } urls.append(temp); } return urls; } void BTTransfer::filesSelected() { QModelIndexList indexes = fileModel()->fileIndexes(FileItem::File); //one single file if (indexes.count() == 1) { QModelIndex index = indexes.first(); const bool doDownload = index.data(Qt::CheckStateRole).toBool(); if (torrent && torrent->getStats().bytes_left_to_download) { if (doDownload) { start(); } else { stop(); } } } //multiple files else { foreach (const QModelIndex &index, indexes) { const QUrl dest = fileModel()->getUrl(index); const bool doDownload = index.data(Qt::CheckStateRole).toBool(); bt::TorrentFileInterface *file = m_files[dest]; file->setDoNotDownload(!doDownload); } } // setTransferChange(Tc_TotalSize | Tc_DownloadedSize | Tc_Percent, true); } FileModel *BTTransfer::fileModel()//TODO correct file model for one-file-torrents { if (!m_fileModel) { if (!torrent) { return 0; } //multiple files if (torrent->getStats().multi_file_torrent) { for (bt::Uint32 i = 0; i < torrent->getNumFiles(); ++i) { bt::TorrentFileInterface *file = &torrent->getTorrentFile(i); m_files[QUrl(file->getPathOnDisk())] = file; } m_fileModel = new FileModel(m_files.keys(), directory(), this); // connect(m_fileModel, SIGNAL(rename(QUrl,QUrl)), this, SLOT(slotRename(QUrl,QUrl))); connect(m_fileModel, SIGNAL(checkStateChanged()), this, SLOT(filesSelected())); //set the checkstate, the status and the size of the model items QHash::const_iterator it; QHash::const_iterator itEnd = m_files.constEnd(); const Job::Status curentStatus = this->status(); for (it = m_files.constBegin(); it != itEnd; ++it) { QModelIndex size = m_fileModel->index(it.key(), FileItem::Size); m_fileModel->setData(size, static_cast((*it)->getSize())); const bool doDownload = !(*it)->doNotDownload(); QModelIndex checkIndex = m_fileModel->index(it.key(), FileItem::File); const Qt::CheckState checkState = doDownload ? Qt::Checked : Qt::Unchecked; m_fileModel->setData(checkIndex, checkState, Qt::CheckStateRole); QModelIndex status = m_fileModel->index(it.key(), FileItem::Status); if (doDownload && (curentStatus == Job::Running)) { m_fileModel->setData(status, Job::Running); } else { m_fileModel->setData(status, Job::Stopped); } if (qFuzzyCompare((*it)->getDownloadPercentage(), 100.0f)) { m_fileModel->setData(status, Job::Finished); } } } //one single file else { QList urls; QUrl temp = m_dest; if (m_dest.fileName() != torrent->getStats().torrent_name)//TODO check if the body is ever entered! { temp = temp.adjusted(QUrl::StripTrailingSlash); temp.setPath(temp.path() + '/' + (torrent->getStats().torrent_name)); } const QUrl url = temp; urls.append(url); m_fileModel = new FileModel(urls, directory(), this); // connect(m_fileModel, SIGNAL(rename(QUrl,QUrl)), this, SLOT(slotRename(QUrl,QUrl))); connect(m_fileModel, SIGNAL(checkStateChanged()), this, SLOT(filesSelected())); QModelIndex size = m_fileModel->index(url, FileItem::Size); m_fileModel->setData(size, static_cast(torrent->getStats().total_bytes)); QModelIndex checkIndex = m_fileModel->index(url, FileItem::File); m_fileModel->setData(checkIndex, Qt::Checked, Qt::CheckStateRole); QModelIndex status = m_fileModel->index(url, FileItem::Status); if (this->status() == Job::Running) { m_fileModel->setData(status, Job::Running); } else { m_fileModel->setData(status, Job::Stopped); } if (!torrent->getStats().bytes_left_to_download) { m_fileModel->setData(status, Job::Finished); } } } return m_fileModel; } diff --git a/transfer-plugins/kio/transferKio.cpp b/transfer-plugins/kio/transferKio.cpp index 273df03d..12fe50d6 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 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())) { + QString oldPath = m_dest.toLocalFile() + ".part"; + if (QFile::exists(oldPath)) { 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); + KIO::Job *move = KIO::file_move(QUrl::fromLocalFile(oldPath), QUrl::fromLocalFile(newDestination.toLocalFile() + ".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); + KIO::Job *del = KIO::del(QUrl::fromLocalFile(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().toSecsSinceEpoch(); 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/metalink/abstractmetalink.cpp b/transfer-plugins/metalink/abstractmetalink.cpp index 2babfe62..937f6efb 100644 --- a/transfer-plugins/metalink/abstractmetalink.cpp +++ b/transfer-plugins/metalink/abstractmetalink.cpp @@ -1,550 +1,551 @@ /* 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 "abstractmetalink.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 AbstractMetalink::AbstractMetalink(TransferGroup * parent, TransferFactory * factory, Scheduler * scheduler, const QUrl & source, const QUrl & dest, const QDomElement * e) : Transfer(parent, factory, scheduler, source, dest, e), m_fileModel(nullptr), m_currentFiles(), m_ready(false), m_speedCount(0), m_tempAverageSpeed(0), m_averageSpeed(0) { } AbstractMetalink::~AbstractMetalink() { } void AbstractMetalink::slotDataSourceFactoryChange(Transfer::ChangesFlags change) { if ((change & Tc_Status) | (change & Tc_TotalSize)) { DataSourceFactory *factory = qobject_cast(sender()); if (change & Tc_Status) { bool changeStatus; updateStatus(factory, &changeStatus); if (!changeStatus) { change &= ~Tc_Status; } } if (change & Tc_TotalSize) { recalculateTotalSize(factory); } } if (change & Tc_DownloadedSize) { recalculateProcessedSize(); change |= Tc_Percent; } if (change & Tc_DownloadSpeed) { recalculateSpeed(); } setTransferChange(change, true); } void AbstractMetalink::recalculateTotalSize(DataSourceFactory *sender) { m_totalSize = 0; foreach (DataSourceFactory *factory, m_dataSourceFactory) { if (factory->doDownload()) { m_totalSize += factory->size(); } } if (m_fileModel) { if (sender) { QModelIndex sizeIndex = m_fileModel->index(sender->dest(), FileItem::Size); m_fileModel->setData(sizeIndex, static_cast(sender->size())); } } } void AbstractMetalink::recalculateProcessedSize() { m_downloadedSize = 0; foreach (DataSourceFactory *factory, m_dataSourceFactory) { if (factory->doDownload()) { m_downloadedSize += factory->downloadedSize(); } } if (m_totalSize) { m_percent = (m_downloadedSize * 100) / m_totalSize; } else { m_percent = 0; } } void AbstractMetalink::recalculateSpeed() { m_downloadSpeed = 0; foreach (DataSourceFactory *factory, m_dataSourceFactory) { if (factory->doDownload()) { m_downloadSpeed += factory->currentSpeed(); } } //calculate the average of the last three speeds m_tempAverageSpeed += m_downloadSpeed; ++m_speedCount; if (m_speedCount == 3) { m_averageSpeed = m_tempAverageSpeed / 3; m_speedCount = 0; m_tempAverageSpeed = 0; } } int AbstractMetalink::remainingTime() const { if (!m_averageSpeed) { m_averageSpeed = m_downloadSpeed; } return KIO::calculateRemainingSeconds(m_totalSize, m_downloadedSize, m_averageSpeed); } void AbstractMetalink::updateStatus(DataSourceFactory *sender, bool *changeStatus) { Job::Status status = (sender ? sender->status() : Job::Stopped); *changeStatus = true; switch (status) { case Job::Aborted: case Job::Stopped: { m_currentFiles = 0; foreach (DataSourceFactory *factory, m_dataSourceFactory) { //one factory is still running, do not change the status if (factory->doDownload() && (factory->status() == Job::Running)) { *changeStatus = false; ++m_currentFiles; } } if (*changeStatus) { setStatus(status); } break; } case Job::Finished: //one file that has been downloaded now is finished//FIXME ignore downloads that were finished in the previous download!!!! if (m_currentFiles) { --m_currentFiles; startMetalink(); } foreach (DataSourceFactory *factory, m_dataSourceFactory) { //one factory is not finished, do not change the status if (factory->doDownload() && (factory->status() != Job::Finished)) { *changeStatus = false; break; } } if (*changeStatus) { setStatus(Job::Finished); } break; default: setStatus(status); break; } if (m_fileModel) { if (sender) { QModelIndex statusIndex = m_fileModel->index(sender->dest(), FileItem::Status); m_fileModel->setData(statusIndex, status); } } } void AbstractMetalink::slotVerified(bool isVerified) { Q_UNUSED(isVerified) if (status() == Job::Finished) { //see if some files are NotVerified QStringList brokenFiles; foreach (DataSourceFactory *factory, m_dataSourceFactory) { if (m_fileModel) { QModelIndex checksumVerified = m_fileModel->index(factory->dest(), FileItem::ChecksumVerified); m_fileModel->setData(checksumVerified, factory->verifier()->status()); } if (factory->doDownload() && (factory->verifier()->status() == Verifier::NotVerified)) { brokenFiles.append(factory->dest().toString()); } } if (brokenFiles.count()) { if (KMessageBox::warningYesNoCancelList(nullptr, i18n("The download could not be verified, do you want to repair (if repairing does not work the download would be restarted) it?"), brokenFiles) == KMessageBox::Yes) { if (repair()) { return; } } } } } void AbstractMetalink::slotSignatureVerified() { if (status() == Job::Finished) { //see if some files are NotVerified QStringList brokenFiles; foreach (DataSourceFactory *factory, m_dataSourceFactory) { if (m_fileModel) { QModelIndex signatureVerified = m_fileModel->index(factory->dest(), FileItem::SignatureVerified); m_fileModel->setData(signatureVerified, factory->signature()->status()); } if (factory->doDownload() && (factory->verifier()->status() == Verifier::NotVerified)) { brokenFiles.append(factory->dest().toString()); } } /* if (brokenFiles.count())//TODO { if (KMessageBox::warningYesNoCancelList(0, i18n("The download could not be verified, try to repair it?"), brokenFiles) == KMessageBox::Yes) { if (repair()) { return; } } }*/ } } bool AbstractMetalink::repair(const QUrl &file) { if (file.isValid()) { if (m_dataSourceFactory.contains(file)) { DataSourceFactory *broken = m_dataSourceFactory[file]; if (broken->verifier()->status() == Verifier::NotVerified) { broken->repair(); return true; } } } else { QList broken; foreach (DataSourceFactory *factory, m_dataSourceFactory) { if (factory->doDownload() && (factory->verifier()->status() == Verifier::NotVerified)) { broken.append(factory); } } if (broken.count()) { foreach (DataSourceFactory *factory, broken) { factory->repair(); } return true; } } return false; } Verifier *AbstractMetalink::verifier(const QUrl &file) { if (!m_dataSourceFactory.contains(file)) { return 0; } return m_dataSourceFactory[file]->verifier(); } Signature *AbstractMetalink::signature(const QUrl &file) { if (!m_dataSourceFactory.contains(file)) { return 0; } return m_dataSourceFactory[file]->signature(); } QList AbstractMetalink::files() const { return m_dataSourceFactory.keys(); } FileModel *AbstractMetalink::fileModel() { if (!m_fileModel) { m_fileModel = new FileModel(files(), directory(), this); connect(m_fileModel, SIGNAL(rename(QUrl,QUrl)), this, SLOT(slotRename(QUrl,QUrl))); connect(m_fileModel, SIGNAL(checkStateChanged()), this, SLOT(filesSelected())); foreach (DataSourceFactory *factory, m_dataSourceFactory) { const QUrl dest = factory->dest(); QModelIndex size = m_fileModel->index(dest, FileItem::Size); m_fileModel->setData(size, static_cast(factory->size())); QModelIndex status = m_fileModel->index(dest, FileItem::Status); m_fileModel->setData(status, factory->status()); QModelIndex checksumVerified = m_fileModel->index(dest, FileItem::ChecksumVerified); m_fileModel->setData(checksumVerified, factory->verifier()->status()); QModelIndex signatureVerified = m_fileModel->index(dest, FileItem::SignatureVerified); m_fileModel->setData(signatureVerified, factory->signature()->status()); if (!factory->doDownload()) { QModelIndex index = m_fileModel->index(factory->dest(), FileItem::File); m_fileModel->setData(index, Qt::Unchecked, Qt::CheckStateRole); } } } return m_fileModel; } void AbstractMetalink::slotRename(const QUrl &oldUrl, const QUrl &newUrl) { if (!m_dataSourceFactory.contains(oldUrl)) { return; } m_dataSourceFactory[newUrl] = m_dataSourceFactory[oldUrl]; m_dataSourceFactory.remove(oldUrl); m_dataSourceFactory[newUrl]->setNewDestination(newUrl); setTransferChange(Tc_FileName); } bool AbstractMetalink::setDirectory(const QUrl &new_directory) { if (new_directory == directory()) { return false; } if (m_fileModel) { m_fileModel->setDirectory(new_directory); } const QString oldDirectory = directory().toString(); const QString newDirectory = new_directory.toString(); const QString fileName = m_dest.fileName(); m_dest = new_directory; m_dest.setPath(m_dest.adjusted(QUrl::RemoveFilename).toString() + fileName); QHash newStorage; foreach (DataSourceFactory *factory, m_dataSourceFactory) { const QUrl oldUrl = factory->dest(); const QUrl newUrl = QUrl(oldUrl.toString().replace(oldDirectory, newDirectory)); factory->setNewDestination(newUrl); newStorage[newUrl] = factory; } m_dataSourceFactory = newStorage; setTransferChange(Tc_FileName); return true; } QHash > AbstractMetalink::availableMirrors(const QUrl &file) const { QHash > urls; if (m_dataSourceFactory.contains(file)) { urls = m_dataSourceFactory[file]->mirrors(); } return urls; } void AbstractMetalink::setAvailableMirrors(const QUrl &file, const QHash > &mirrors) { if (!m_dataSourceFactory.contains(file)) { return; } m_dataSourceFactory[file]->setMirrors(mirrors); } void AbstractMetalink::slotUpdateCapabilities() { Capabilities oldCap = capabilities(); Capabilities newCap = 0; foreach (DataSourceFactory *file, m_dataSourceFactory) { if (file->doDownload()) {//FIXME when a download did not start yet it should be moveable!!//FIXME why not working, when only two connections? if (newCap) { newCap &= file->capabilities(); } else { newCap = file->capabilities(); } } } if (newCap != oldCap) { setCapabilities(newCap); } } void AbstractMetalink::untickAllFiles() { for (int row = 0; row < fileModel()->rowCount(); ++row) { QModelIndex index = fileModel()->index(row, FileItem::File); if (index.isValid()) { fileModel()->setData(index, Qt::Unchecked, Qt::CheckStateRole); } } } void AbstractMetalink::fileDlgFinished(int result) { //the dialog was not accepted untick every file, this ensures that the user does not //press start by accident without first selecting the desired files if (result != QDialog::Accepted) { untickAllFiles(); } filesSelected(); //no files selected to download or dialog rejected, stop the download if (!m_numFilesSelected || (result != QDialog::Accepted)) { setStatus(Job::Stopped); setTransferChange(Tc_Status, true); return; } startMetalink(); } void AbstractMetalink::filesSelected() { bool overwriteAll = false; bool autoSkip = false; bool cancel = false; QModelIndexList files = fileModel()->fileIndexes(FileItem::File); m_numFilesSelected = 0; //sets the CheckState of the fileModel to the according DataSourceFactories //and asks the user if there are existing files already foreach (const QModelIndex &index, files) { const QUrl dest = fileModel()->getUrl(index); bool doDownload = index.data(Qt::CheckStateRole).toBool(); if (m_dataSourceFactory.contains(dest)) { DataSourceFactory *factory = m_dataSourceFactory[dest]; //ignore finished transfers if ((factory->status() == Job::Finished) || (factory->status() == Job::FinishedKeepAlive)) { continue; } //check if the file at dest exists already and ask the user what to do in this case, ignore already running transfers if (doDownload && (factory->status() != Job::Running) && QFile::exists(dest.toLocalFile())) { //user has chosen to skip all files that exist already before if (autoSkip) { fileModel()->setData(index, Qt::Unchecked, Qt::CheckStateRole); doDownload = false; //ask the user, unless he has chosen overwriteAll before } else if (!overwriteAll) { - KIO::RenameDialog dlg(0, i18n("File already exists"), index.data().toString(), dest, KIO::RenameDialog_Options(KIO::RenameDialog_MultipleItems | KIO::RenameDialog_Overwrite | KIO::RenameDialog_Skip)); + KIO::RenameDialog dlg(0, i18n("File already exists"), QUrl(index.data().toString()), dest, + KIO::RenameDialog_Options(KIO::RenameDialog_MultipleItems | KIO::RenameDialog_Overwrite | KIO::RenameDialog_Skip)); const int result = dlg.exec(); if (result == KIO::Result_Rename) { //no reason to use FileModel::rename() since the file does not exist yet, so simply skip it //avoids having to deal with signals const QUrl newDest = dlg.newDestUrl(); factory->setDoDownload(doDownload); factory->setNewDestination(newDest); fileModel()->setData(index, newDest.fileName(), FileItem::File); ++m_numFilesSelected; m_dataSourceFactory.remove(dest); m_dataSourceFactory[newDest] = factory; continue; } else if (result == KIO::Result_Skip) { fileModel()->setData(index, Qt::Unchecked, Qt::CheckStateRole); doDownload = false; } else if (result == KIO::Result_Cancel) { cancel = true; break; } else if (result == KIO::Result_AutoSkip) { autoSkip = true; fileModel()->setData(index, Qt::Unchecked, Qt::CheckStateRole); doDownload = false; } else if (result == KIO::Result_OverwriteAll) { overwriteAll = true; } } } factory->setDoDownload(doDownload); if (doDownload && (factory->status() != Finished) && (factory->status() != FinishedKeepAlive)) { ++m_numFilesSelected; } } } //the user decided to cancel, so untick all files if (cancel) { m_numFilesSelected = 0; untickAllFiles(); foreach (DataSourceFactory *factory, m_dataSourceFactory) { factory->setDoDownload(false); } } Transfer::ChangesFlags change = (Tc_TotalSize | Tc_DownloadSpeed); //some files have been selected that are not finished yet, set them to stop if the transfer is not running (checked in slotStatus) if (m_numFilesSelected) { change |= Tc_Status; } slotDataSourceFactoryChange(change); } void AbstractMetalink::stop() { qCDebug(KGET_DEBUG) << "metalink::Stop"; if (m_ready && ((status() != Stopped) || (status() != Finished))) { m_currentFiles = 0; foreach (DataSourceFactory *factory, m_dataSourceFactory) { factory->stop(); } } } diff --git a/transfer-plugins/metalink/metalinkhttp.cpp b/transfer-plugins/metalink/metalinkhttp.cpp index efd2a46a..819b3309 100644 --- a/transfer-plugins/metalink/metalinkhttp.cpp +++ b/transfer-plugins/metalink/metalinkhttp.cpp @@ -1,312 +1,313 @@ /* 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 "metalinkhttp.h" #include "metalinksettings.h" #include "metalinkxml.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 /** * @return Hex value from a base64 value * @note needed for hex based signature verification */ QString base64ToHex(const QString& b64) { return QString(QByteArray::fromBase64(b64.toAscii()).toHex()); } MetalinkHttp::MetalinkHttp(TransferGroup * parent, TransferFactory * factory, Scheduler * scheduler, const QUrl & source, const QUrl & dest, KGetMetalink::MetalinkHttpParser *httpParser, const QDomElement * e) : AbstractMetalink(parent,factory,scheduler,source, dest, e) , m_signatureUrl(QUrl()), m_httpparser(httpParser) { m_httpparser->setParent(this); } MetalinkHttp::~MetalinkHttp() { } void MetalinkHttp::load(const QDomElement *element) { qCDebug(KGET_DEBUG); Transfer::load(element); DataSourceFactory * fac = new DataSourceFactory(this, m_dest); m_dataSourceFactory.insert(m_dest, fac); connect(fac, SIGNAL(capabilitiesChanged()), this, SLOT(slotUpdateCapabilities())); connect(fac, SIGNAL(dataSourceFactoryChange(Transfer::ChangesFlags)), this, SLOT(slotDataSourceFactoryChange(Transfer::ChangesFlags))); connect(fac->verifier(), SIGNAL(verified(bool)), this, SLOT(slotVerified(bool))); connect(fac->signature(), SIGNAL(verified(int)), this, SLOT(slotSignatureVerified())); connect(fac, SIGNAL(log(QString,Transfer::LogLevel)), this, SLOT(setLog(QString,Transfer::LogLevel))); fac->load(element); if (fac->mirrors().isEmpty()) { return; } m_ready = true; } void MetalinkHttp::save(const QDomElement &element) { qCDebug(KGET_DEBUG); Transfer::save(element); m_dataSourceFactory.begin().value()->save(element); } void MetalinkHttp::startMetalink() { if (m_ready) { foreach (DataSourceFactory *factory, m_dataSourceFactory) { //specified number of files is downloaded simultaneously if (m_currentFiles < MetalinkSettings::simultaneousFiles()) { const Job::Status 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 MetalinkHttp::start() { qDebug() << "metalinkhttp::start"; if (!m_ready) { setLinks(); setDigests(); if (metalinkHttpInit()) { startMetalink(); } } else { startMetalink(); } } void MetalinkHttp::setSignature(QUrl & dest, QByteArray & data, DataSourceFactory* dataFactory) { Q_UNUSED(dest); dataFactory->signature()->setSignature(data,Signature::AsciiDetached); } void MetalinkHttp::slotSignatureVerified() { if (status() == Job::Finished) { //see if some files are NotVerified QStringList brokenFiles; foreach (DataSourceFactory *factory, m_dataSourceFactory) { if (m_fileModel) { QModelIndex signatureVerified = m_fileModel->index(factory->dest(), FileItem::SignatureVerified); m_fileModel->setData(signatureVerified, factory->signature()->status()); } if (factory->doDownload() && (factory->verifier()->status() == Verifier::NotVerified)) { brokenFiles.append(factory->dest().toString()); } } if (brokenFiles.count()) { if (KMessageBox::warningYesNoCancelList(nullptr, i18n("The download could not be verified, try to repair it?"), brokenFiles) == KMessageBox::Yes) { if (repair()) { KGet::addTransfer(m_metalinkxmlUrl); //TODO Use a Notification instead. Check kget.h for how to use it. } } } } } bool MetalinkHttp::metalinkHttpInit() { qDebug() << "m_dest = " << m_dest; const QUrl tempDest = QUrl(m_dest.adjusted(QUrl::RemoveFilename)); - QUrl dest = tempDest.toString() + m_dest.fileName(); + QUrl dest = QUrl(tempDest.toString() + m_dest.fileName()); qDebug() << "dest = " << dest; //sort the urls according to their priority (highest first) std::stable_sort(m_linkheaderList.begin(), m_linkheaderList.end()); DataSourceFactory *dataFactory = new DataSourceFactory(this,dest); dataFactory->setMaxMirrorsUsed(MetalinkSettings::mirrorsPerFile()); 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 Mirrors Sources for(int i = 0; i < m_linkheaderList.size(); ++i) { const QUrl url = m_linkheaderList[i].url; if (url.isValid()) { if (m_linkheaderList[i].pref) { qDebug() << "found etag in a mirror" ; KGetMetalink::MetalinkHttpParser* eTagCher = new KGetMetalink::MetalinkHttpParser(url) ; if (eTagCher->getEtag() != m_httpparser->getEtag()) { //There is an ETag mismatch continue ; } } dataFactory->addMirror(url, MetalinkSettings::connectionsPerUrl()); } } //no datasource has been created, so remove the datasource factory if (dataFactory->mirrors().isEmpty()) { qDebug() << "data source factory being deleted" ; delete dataFactory; } else { QHashIterator itr(m_DigestList); while(itr.hasNext()) { itr.next(); qDebug() << itr.key() << ":" << itr.value() ; } dataFactory->verifier()->addChecksums(m_DigestList); //Add OpenPGP signatures if (m_signatureUrl != QUrl()) { // 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 *signat_download = new Download(m_signatureUrl, QStandardPaths::writableLocation(QStandardPaths::DataLocation) + QStringLiteral("/metalinks/") + m_source.fileName()); + const QString path = QStandardPaths::writableLocation(QStandardPaths::DataLocation) + QStringLiteral("/metalinks/") + m_source.fileName(); + Download *signat_download = new Download(m_signatureUrl, QUrl::fromLocalFile(path)); connect(signat_download, SIGNAL(finishedSuccessfully(QUrl,QByteArray)), SLOT(setSignature(QUrl,QByteArray))); } m_dataSourceFactory[dataFactory->dest()] = dataFactory; } if (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(); return true; } void MetalinkHttp::setLinks() { const QMultiMap* headerInf = m_httpparser->getHeaderInfo(); const QList linkVals = headerInf->values("link"); foreach (const QString link, linkVals) { const KGetMetalink::HttpLinkHeader linkheader(link); if (linkheader.reltype == "duplicate") { m_linkheaderList.append(linkheader); } else if (linkheader.reltype == "application/pgp-signature") { m_signatureUrl = linkheader.url; //There will only be one signature } else if (linkheader.reltype == "application/metalink4+xml") { m_metalinkxmlUrl = linkheader.url ; // There will only be one metalink xml (metainfo URL) } } } void MetalinkHttp::deinit(Transfer::DeleteOptions options) { foreach (DataSourceFactory *factory, m_dataSourceFactory) { if (options & Transfer::DeleteFiles) { factory->deinit(); } } } void MetalinkHttp::setDigests() { const QMultiMap* digestInfo = m_httpparser->getHeaderInfo(); const QList digestList = digestInfo->values("digest"); foreach(const QString digest, digestList) { const int eqDelimiter = digest.indexOf('='); const QString digestType = MetalinkHttp::adaptDigestType(digest.left(eqDelimiter).trimmed()); const QString hexDigestValue = base64ToHex(digest.mid(eqDelimiter + 1).trimmed()); m_DigestList.insertMulti(digestType,hexDigestValue); } } QString MetalinkHttp::adaptDigestType(const QString & hashType) { if (hashType == QString("SHA")) { return QString("sha"); } else if (hashType == QString("MD5")) { return QString("md5"); } else if (hashType == QString("SHA-256")) { return QString("sha256"); } else { return hashType; } } diff --git a/transfer-plugins/metalink/metalinkxml.cpp b/transfer-plugins/metalink/metalinkxml.cpp index b05d02d9..15565c27 100644 --- a/transfer-plugins/metalink/metalinkxml.cpp +++ b/transfer-plugins/metalink/metalinkxml.cpp @@ -1,332 +1,333 @@ /* 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 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()); + const QString path = QStandardPaths::writableLocation(QStandardPaths::DataLocation) + QStringLiteral("/metalinks/") + m_source.fileName(); + Download *download = new Download(m_source, QUrl::fromLocalFile(path)); 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); + KGetMetalink::HandleMetalink::load(m_localMetalinkLocation, &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) std::sort(urlList.begin(), urlList.end(), [](const KGetMetalink::Url &a, const KGetMetalink::Url &b) { return b < a; }); 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 simultaneously if (m_currentFiles < MetalinkSettings::simultaneousFiles()) { 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::simultaneousFiles()) { ++m_currentFiles; file->start(); } else { //enough simultaneous 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/mirrors.h b/transfer-plugins/mirrorsearch/mirrors.h index 7e483deb..f1ae9425 100644 --- a/transfer-plugins/mirrorsearch/mirrors.h +++ b/transfer-plugins/mirrorsearch/mirrors.h @@ -1,46 +1,47 @@ /* 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. */ #ifndef MIRROR_H #define MIRROR_H #include #include class mirror : public QObject { Q_OBJECT public: mirror(); void search(const QUrl &url, QObject *receiver, const char *member); void search(const QString &fileName, QObject *receiver, const char *member); Q_SIGNALS: void urls (QList&); private Q_SLOTS: void slotData(KIO::Job *, const QByteArray& data); void slotResult( KJob *job ); private: QString m_search_engine; KIO::TransferJob *m_job; QUrl m_url; QList m_Urls; QByteArray m_data; }; void MirrorSearch ( const QUrl &url, QObject *receiver, const char *member ); +void MirrorSearch ( const QString &fileName, QObject *receiver, const char *member ); #endif // MIRROR_H diff --git a/ui/droptarget.cpp b/ui/droptarget.cpp index c5e2433c..86b62290 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 #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()); + NewTransferDialogHandler::showNewTransferDialog(list.first()); if (msgBoxResult == 4) //Load KGet::load(list.first().url()); } else { if (list.count() == 1) { str = event->mimeData()->text(); - NewTransferDialogHandler::showNewTransferDialog(str); + NewTransferDialogHandler::showNewTransferDialog(QUrl(str)); } else NewTransferDialogHandler::showNewTransferDialog(list); } } else { NewTransferDialogHandler::showNewTransferDialog(); } if ( Settings::animateDropTarget() ) playAnimationSync(); } void DropTarget::closeEvent( QCloseEvent * e ) { 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); + KWindowSystem::setState(winId(), KWindowSystem::SkipTaskbar | KWindowSystem::KeepAbove | 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/groupsettingsdialog.cpp b/ui/groupsettingsdialog.cpp index 6d0e87e9..ff648f8c 100644 --- a/ui/groupsettingsdialog.cpp +++ b/ui/groupsettingsdialog.cpp @@ -1,69 +1,69 @@ /* 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 "groupsettingsdialog.h" #include "core/transfergrouphandler.h" GroupSettingsDialog::GroupSettingsDialog(QWidget *parent, TransferGroupHandler *group) : KGetSaveSizeDialog("GroupSettingsDialog", parent), m_group(group) { setWindowTitle(i18n("Group Settings for %1", group->name())); ui.setupUi(this); ui.downloadBox->setValue(group->downloadLimit(Transfer::VisibleSpeedLimit)); ui.uploadBox->setValue(group->uploadLimit(Transfer::VisibleSpeedLimit)); ui.defaultFolderRequester->setMode(KFile::Directory); QString path = group->defaultFolder(); - ui.defaultFolderRequester->setUrl(path); + ui.defaultFolderRequester->setUrl(QUrl::fromLocalFile(path)); ui.defaultFolderRequester->setStartDir(QUrl(KGet::generalDestDir(true))); ui.regExpEdit->setText(group->regExp().pattern()); ui.nepomukWidget->hide(); connect(ui.buttonBox, &QDialogButtonBox::accepted, this, &QDialog::accept); connect(ui.buttonBox, &QDialogButtonBox::rejected, this, &QDialog::reject); connect(this, &GroupSettingsDialog::accepted, this, &GroupSettingsDialog::save); } GroupSettingsDialog::~GroupSettingsDialog() { } QSize GroupSettingsDialog::sizeHint() const { QSize sh = QDialog::sizeHint(); sh.setWidth(sh.width() * 1.4); return sh; } void GroupSettingsDialog::save() { //check needed, otherwise "/" would be added as folder if the line was empty! if (ui.defaultFolderRequester->text().isEmpty()) { m_group->setDefaultFolder(QString()); } else { m_group->setDefaultFolder(ui.defaultFolderRequester->url().toLocalFile()); } m_group->setDownloadLimit(ui.downloadBox->value(), Transfer::VisibleSpeedLimit); m_group->setUploadLimit(ui.uploadBox->value(), Transfer::VisibleSpeedLimit); QRegExp regExp; regExp.setPattern(ui.regExpEdit->text()); m_group->setRegExp(regExp); } diff --git a/ui/history/transferhistory.cpp b/ui/history/transferhistory.cpp index ce8ef0bd..db1eb5d3 100644 --- a/ui/history/transferhistory.cpp +++ b/ui/history/transferhistory.cpp @@ -1,360 +1,360 @@ /* This file is part of the KDE project Copyright (C) 2007 by 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 "transferhistory.h" #include "ui/newtransferdialog.h" #include "settings.h" #include "ui/history/transferhistorycategorizedview.h" #include "ui/history/transferhistorycategorizeddelegate.h" #include "ui/history/rangetreewidget.h" #include "core/transferhistorystore.h" #include "core/job.h" #include "kget_debug.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include TransferHistory::TransferHistory(QWidget *parent) : KGetSaveSizeDialog("TransferHistory", parent), m_rangeType(TransferHistory::Date), m_progressBar(new QProgressBar(this)), m_iconModeEnabled(true) { setAttribute(Qt::WA_DeleteOnClose); setWindowTitle(i18n("Transfer History")); //Setup Ui-Parts from Designer QWidget *mainWidget = new QWidget(this); setupUi(mainWidget); m_view = new TransferHistoryCategorizedView(this); // list icon view auto &iconView = bt_iconview; auto &listView = bt_listview; listView->setIcon(QIcon::fromTheme("view-list-details")); iconView->setIcon(QIcon::fromTheme("view-list-icons")); connect(listView, SIGNAL(clicked()), SLOT(slotSetListMode())); connect(iconView, SIGNAL(clicked()), SLOT(slotSetIconMode())); clearButton->setIcon(QIcon::fromTheme("edit-clear-history")); actionDelete_Selected->setIcon(QIcon::fromTheme("edit-delete")); actionDownload->setIcon(QIcon::fromTheme("document-new")); m_openFile = new QAction(QIcon::fromTheme("document-open"), i18n("&Open File"), this); vboxLayout->addWidget(m_view); vboxLayout->addWidget(m_progressBar); QDialogButtonBox * buttonBox = new QDialogButtonBox(mainWidget); buttonBox->clear(); buttonBox->addButton(QDialogButtonBox::Close); vboxLayout->addWidget(buttonBox); setLayout(vboxLayout); watcher = new QFileSystemWatcher(); watcher->addPath(QStandardPaths::writableLocation(QStandardPaths::DataLocation) + QLatin1Char('/') + QString()); qCDebug(KGET_DEBUG) << watcher->directories(); m_store = TransferHistoryStore::getStore(); connect(actionDelete_Selected, SIGNAL(triggered()), this, SLOT(slotDeleteTransfer())); connect(actionDownload, SIGNAL(triggered()), this, SLOT(slotDownload())); connect(m_openFile, SIGNAL(triggered()), this, SLOT(slotOpenFile())); connect(clearButton, SIGNAL(clicked()), this, SLOT(slotClear())); connect(rangeType, SIGNAL(activated(int)), this, SLOT(slotLoadRangeType(int))); connect(m_view, SIGNAL(deletedTransfer(QString,QModelIndex)), SLOT(slotDeleteTransfer(QString,QModelIndex))); connect(m_view, SIGNAL(doubleClicked(QModelIndex)), SLOT(slotOpenFile(QModelIndex))); connect(m_store, SIGNAL(loadFinished()), SLOT(slotLoadFinished())); connect(m_store, SIGNAL(elementLoaded(int,int,TransferHistoryItem)), SLOT(slotElementLoaded(int,int,TransferHistoryItem))); connect(searchBar, SIGNAL(textChanged(QString)), m_view, SLOT(setFilterRegExp(QString))); connect(buttonBox, &QDialogButtonBox::rejected, this, &QDialog::reject); connect(buttonBox, &QDialogButtonBox::accepted, this, &QDialog::accept); slotAddTransfers(); } TransferHistory::~TransferHistory() { delete m_store; } QSize TransferHistory::sizeHint() const { QSize sh(800,600); return sh; } void TransferHistory::slotDeleteTransfer() { if (!m_iconModeEnabled) { RangeTreeWidget *range_view = qobject_cast (m_view); slotDeleteTransfer(range_view->currentItem(0)->text()); slotLoadRangeType(m_rangeType); } } void TransferHistory::slotDeleteTransfer(const QString &transferName, const QModelIndex &index) { Q_UNUSED(index) TransferHistoryItem item; item.setSource(transferName); m_store->deleteItem(item); slotLoadRangeType(m_rangeType); } void TransferHistory::slotAddTransfers() { m_progressBar->show(); m_store->load(); } void TransferHistory::slotClear() { m_store->clear(); slotLoadRangeType(m_rangeType); } void TransferHistory::slotWriteDefault() { // not neded ?? } void TransferHistory::slotDownload() { if (!m_iconModeEnabled) { - NewTransferDialogHandler::showNewTransferDialog(((RangeTreeWidget *) m_view)->currentItem(0)->text()); + NewTransferDialogHandler::showNewTransferDialog(QUrl(((RangeTreeWidget *) m_view)->currentItem(0)->text())); } } void TransferHistory::contextMenuEvent(QContextMenuEvent *event) { Q_UNUSED(event) if (!m_iconModeEnabled) { RangeTreeWidget *range_view = qobject_cast (m_view); if(range_view->currentIndex().parent().isValid()) { QMenu *contextMenu = new QMenu(this); contextMenu->addAction(actionDownload); contextMenu->addAction(actionDelete_Selected); if (range_view->currentItem(4)->data().toInt() == Job::Finished) contextMenu->addAction(m_openFile); contextMenu->exec(QCursor::pos()); } } } void TransferHistory::slotOpenFile(const QModelIndex &index) { QString file; if (!m_iconModeEnabled) { RangeTreeWidget *range_view = qobject_cast (m_view); file = range_view->currentItem(1)->text(); } else { TransferHistoryCategorizedView *categorized_view = qobject_cast (m_view); file = categorized_view->data(index, TransferHistoryCategorizedDelegate::RoleDest).toString(); } //qDebug() << "Try to open the file : " << file; if (!file.isEmpty()) { new KRun(QUrl::fromLocalFile(file), this, true); } } void TransferHistory::hideEvent(QHideEvent *event) { Q_UNUSED(event) disconnect(watcher, SIGNAL(directoryChanged(QString)), this, SLOT(slotAddTransfers()));//Prevent reloading of TransferHistory when saving deleteLater(); } QString TransferHistory::statusText(int status) const { switch (status) { case Job::Running: return i18nc("The transfer is running", "Running"); case Job::Stopped: return i18nc("The transfer is stopped", "Stopped"); case Job::Aborted: return i18nc("The transfer is aborted", "Aborted"); case Job::Finished: return i18nc("The transfer is finished", "Finished"); default: return QString(); } } void TransferHistory::slotLoadRangeType(int type) { m_rangeType = type; if (m_iconModeEnabled) { TransferHistoryCategorizedView *cat_view = qobject_cast (m_view); cat_view->clear(); switch(type) { case TransferHistory::Size : cat_view->setCategorizedDelegate(new SizeCategorizedDelegate()); break; case TransferHistory::Date : cat_view->setCategorizedDelegate(new DateCategorizedDelegate()); break; default: cat_view->setCategorizedDelegate(new HostCategorizedDelegate()); } } else { RangeTreeWidget *range_view = qobject_cast (m_view); QFontMetrics *font = new QFontMetrics(QFontDatabase::systemFont(QFontDatabase::GeneralFont)); range_view->clear(); range_view->setLabels(QStringList() << i18n("Source File") << i18n("Destination") << i18n("Time") << i18n("File Size") << i18n("Status")); range_view->setRangeDelegate(0); switch(m_rangeType) { case TransferHistory::Host : range_view->setRangeDelegate(new HostRangeDelegate(this)); break; case TransferHistory::Size : range_view->addRange(QVariant(0), QVariant(1024 * 1024), i18n("Less than 1MiB")); range_view->addRange(QVariant(1024 * 1024), QVariant(1024 * 1024 * 10), i18n("Between 1MiB-10MiB")); range_view->addRange(QVariant(1024 * 1024 * 10), QVariant(1024 * 1024 * 100), i18n("Between 10MiB-100MiB")); range_view->addRange(QVariant(1024 * 1024 * 100), QVariant(1024 * 1024 *1024), i18n("Between 100MiB-1GiB")); range_view->addRange(QVariant((double) 1024 * 1024 * 1024), QVariant((double) 1024 * 1024 * 1024 * 10), i18n("More than 1GiB")); break; default: range_view->addRange(QVariant(0), QVariant(1), i18n("Today")); range_view->addRange(QVariant(1), QVariant(7), i18n("Last week")); range_view->addRange(QVariant(7), QVariant(30), i18n("Last month")); range_view->addRange(QVariant(30), QVariant(-1), i18n("A long time ago")); } QList list = Settings::historyColumnWidths(); if (!list.isEmpty()) { int j = 0; foreach (int i, list) { range_view->setColumnWidth(j, i); j++; } } else { range_view->setColumnWidth(0, 200); range_view->setColumnWidth(1, 250); range_view->setColumnWidth(2, font->width(QDate::currentDate().toString())); range_view->setColumnWidth(3, font->width("1500000 KiB")); range_view->setColumnWidth(4, font->width(i18nc("the transfer has been finished", "Finished"))); } } slotAddTransfers(); } void TransferHistory::slotSetListMode() { m_iconModeEnabled = false; delete m_view; m_view = new RangeTreeWidget(this); vboxLayout->insertWidget(1, m_view); slotLoadRangeType(m_rangeType); connect(searchBar, SIGNAL(textChanged(QString)), m_view, SLOT(setFilterRegExp(QString))); // we connect the doubleClicked signal over an item to the open file action connect(m_view, SIGNAL(doubleClicked(QModelIndex)), SLOT(slotOpenFile(QModelIndex))); } void TransferHistory::slotSetIconMode() { m_iconModeEnabled = true; delete m_view; m_view = new TransferHistoryCategorizedView(this); vboxLayout->insertWidget(1, m_view); slotLoadRangeType(m_rangeType); connect(searchBar, SIGNAL(textChanged(QString)), m_view, SLOT(setFilterRegExp(QString))); connect(m_view, SIGNAL(deletedTransfer(QString,QModelIndex)), SLOT(slotDeleteTransfer(QString,QModelIndex))); connect(m_view, SIGNAL(doubleClicked(QModelIndex)), SLOT(slotOpenFile(QModelIndex))); } void TransferHistory::slotElementLoaded(int number, int total, const TransferHistoryItem &item) { m_progressBar->setValue(number*100/total); if (m_iconModeEnabled) { ((TransferHistoryCategorizedView *) m_view)->addData( item.dateTime().date(), item.source(), item.dest(), item.size()); } else { QVariantList attributeList; attributeList.append(item.source()); attributeList.append(item.dest()); attributeList.append(item.dateTime().date().toString()); attributeList.append(KIO::convertSize(item.size())); attributeList.append(statusText(item.state())); QVariant data; if(m_rangeType == TransferHistory::Date) { QDate date = item.dateTime().date(); data = QVariant(date.daysTo(QDate::currentDate())); } else if(m_rangeType == TransferHistory::Host) { data = QVariant(item.source()); } else { data = QVariant(item.size()); } ((RangeTreeWidget *) m_view)->add(data, attributeList); } } void TransferHistory::slotLoadFinished() { m_progressBar->hide(); } diff --git a/ui/history/transferhistoryitemdelegate.cpp b/ui/history/transferhistoryitemdelegate.cpp index 3e5ae334..3a02070b 100644 --- a/ui/history/transferhistoryitemdelegate.cpp +++ b/ui/history/transferhistoryitemdelegate.cpp @@ -1,207 +1,207 @@ /* This file is part of the KDE project Based in the kcategorizeditemsviewdelegate from kdebase/workspace/libs/plasma/appletbrowser by Ivan Cukic Copyright (C) 2008 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 "transferhistoryitemdelegate.h" #include "ui/history/transferhistorycategorizeddelegate.h" #include "ui/history/transferhistorycategorizedview.h" #include "ui/newtransferdialog.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include TransferHistoryItemDelegate::TransferHistoryItemDelegate(QWidget *parent) : QStyledItemDelegate(), m_selectedIndex() { m_view = parent; // Actions m_actionDownload = new QAction(this); m_actionDownload->setText(i18n("Download again")); m_actionDownload->setIcon(QIcon::fromTheme("document-new")); connect(m_actionDownload, &QAction::triggered, this, &TransferHistoryItemDelegate::slotDownload); m_actionDelete_Selected = new QAction(this); m_actionDelete_Selected->setText(i18nc("Delete selected history-item", "Delete selected")); m_actionDelete_Selected->setIcon(QIcon::fromTheme("edit-delete")); connect(m_actionDelete_Selected, &QAction::triggered, this, &TransferHistoryItemDelegate::slotDeleteTransfer); m_openFile = new QAction(this); m_openFile->setText(i18n("Open file")); m_openFile->setIcon(QIcon::fromTheme("document-open")); connect(m_openFile, &QAction::triggered, this, &TransferHistoryItemDelegate::slotOpenFile); } TransferHistoryItemDelegate::~TransferHistoryItemDelegate() { } void TransferHistoryItemDelegate::paint(QPainter *painter, const QStyleOptionViewItem &option, const QModelIndex &index) const { if (!option.state.testFlag(QStyle::State_Selected) && !option.state.testFlag(QStyle::State_MouseOver)) { // draw a separator painter->save(); QRect roundRect(option.rect.left() + 1, option.rect.top() + 1, option.rect.width() - 2, option.rect.height() - 2); QPainterPath path; path.addRoundRect(roundRect, 2, 2); QLinearGradient gradient(roundRect.left(), roundRect.top(), roundRect.left(), roundRect.bottom()); gradient.setColorAt(0, Qt::transparent); gradient.setColorAt(0.95, option.palette.color(QPalette::AlternateBase).darker(130)); QBrush brush(gradient); painter->fillPath(path, brush); painter->setPen(option.palette.color(QPalette::AlternateBase).darker(190)); painter->drawRoundRect(roundRect, 2, 2); painter->restore(); } QStyleOptionViewItem opt(option); QStyle *style = opt.widget ? opt.widget->style() : QApplication::style(); style->drawPrimitive(QStyle::PE_PanelItemViewItem, &opt, painter, opt.widget); const QAbstractItemModel *model = static_cast (index.model()); QUrl url(model->data(index, TransferHistoryCategorizedDelegate::RoleUrl).toString()); QString name = url.path().mid(url.path().lastIndexOf("/") + 1); QIcon icon(KIO::pixmapForUrl( model->data(index, TransferHistoryCategorizedDelegate::RoleDest).toUrl(), 0, KIconLoader::Panel)); QString size = KIO::convertSize(model->data(index, TransferHistoryCategorizedDelegate::RoleSize).toInt()); QString date = model->data(index, TransferHistoryCategorizedDelegate::RoleDate).toDate().toString("dd.MM.yyyy"); QString host = url.host(); // draw the host painter->save(); painter->setPen(option.palette.color(QPalette::Link)); // draw the host painter->drawText(option.rect.left() + PADDING, option.rect.top() + PADDING, option.rect.width() - PADDING * 2, 15, Qt::AlignTop | Qt::AlignLeft, host); painter->restore(); // draw the transfer icon icon.paint(painter, option.rect.left() + option.rect.width() / 2 - ICON_SIZE / 2, option.rect.top() + option.rect.height() / 2 - ICON_SIZE / 2 - PADDING * 2, ICON_SIZE, ICON_SIZE, Qt::AlignCenter, QIcon::Active); painter->save(); QColor subcolor = (option.state.testFlag(QStyle::State_Selected) || (option.state.testFlag(QStyle::State_MouseOver))) ? option.palette.color(QPalette::Text) : option.palette.color(QPalette::BrightText); // draw a separator line between the file name and his size and date painter->setPen(option.palette.color(QPalette::AlternateBase).darker(190)); painter->drawLine(option.rect.left() + 2, option.rect.bottom() + PADDING - 27, option.rect.right() - 2, option.rect.bottom() + PADDING - 27); painter->setPen(subcolor); // draw the size painter->drawText(option.rect.right() - PADDING - 100, option.rect.bottom() + PADDING - 25, 100 - PADDING * 2, 25, Qt::AlignTop | Qt::AlignRight, size); // draw the date painter->drawText(option.rect.left() + PADDING, option.rect.bottom() + PADDING - 25, 100 - PADDING * 2, 25, Qt::AlignTop | Qt::AlignLeft, date); painter->restore(); // draw the filenamne painter->save(); QColor foregroundColor = (option.state.testFlag(QStyle::State_Selected)) ? option.palette.color(QPalette::HighlightedText):option.palette.color(QPalette::Text); painter->setPen(foregroundColor); painter->setFont(option.font); painter->drawText(option.rect.left() + PADDING, option.rect.bottom() - PADDING - 35, 200 - PADDING * 2, 15, Qt::AlignBottom | Qt::AlignCenter, name); painter->restore(); } QSize TransferHistoryItemDelegate::sizeHint(const QStyleOptionViewItem &option, const QModelIndex &index) const { Q_UNUSED(option) Q_UNUSED(index) return QSize(200, 110); } bool TransferHistoryItemDelegate::editorEvent(QEvent * event, QAbstractItemModel * model, const QStyleOptionViewItem &option, const QModelIndex & index) { Q_UNUSED(option) Q_UNUSED(model) QMouseEvent * mouseEvent = dynamic_cast(event); if(mouseEvent && index.isValid()) { if(mouseEvent->button() == Qt::RightButton) { m_selectedIndex = index; QMenu *contextMenu = new QMenu(); contextMenu->addAction(m_actionDownload); contextMenu->addAction(m_actionDelete_Selected); contextMenu->addAction(m_openFile); contextMenu->exec(QCursor::pos()); } } return false; } void TransferHistoryItemDelegate::slotOpenFile() { const QAbstractItemModel *model = static_cast (m_selectedIndex.model()); new KRun(QUrl::fromLocalFile(model->data(m_selectedIndex, TransferHistoryCategorizedDelegate::RoleDest).toString()), m_view, true); } void TransferHistoryItemDelegate::slotDownload() { const QAbstractItemModel *model = static_cast (m_selectedIndex.model()); - NewTransferDialogHandler::showNewTransferDialog(model->data(m_selectedIndex, - TransferHistoryCategorizedDelegate::RoleUrl).toString()); + NewTransferDialogHandler::showNewTransferDialog(QUrl(model->data(m_selectedIndex, + TransferHistoryCategorizedDelegate::RoleUrl).toString())); } void TransferHistoryItemDelegate::slotDeleteTransfer() { const QAbstractItemModel *model = static_cast (m_selectedIndex.model()); emit deletedTransfer(model->data(m_selectedIndex, TransferHistoryCategorizedDelegate::RoleUrl).toString(), m_selectedIndex); } diff --git a/ui/linkview/kget_linkview.cpp b/ui/linkview/kget_linkview.cpp index cf8366c5..34b07d8b 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 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); + url = QUrl(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; + url = QUrl(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 avoiding 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/metalinker.cpp b/ui/metalinkcreator/metalinker.cpp index cd5ffbdd..123d532f 100644 --- a/ui/metalinkcreator/metalinker.cpp +++ b/ui/metalinkcreator/metalinker.cpp @@ -1,1423 +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 "kget_version.h" 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 signature = elem.text(); if (!type.isEmpty() && !signature.isEmpty()) { signatures[type] = signature; } } } 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 const auto timeZone = QTimeZone(temp.toUtf8()); if (timeZone.isValid()) { 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(); + url = QUrl(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/newtransferdialog.cpp b/ui/newtransferdialog.cpp index 3ab891a9..6f976d9e 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 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(); + QUrl m_srcUrl = sources.first(); 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)); + ui.destRequester->setUrl(QUrl(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 recognized 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 inputting 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 83898f6b..60d53510 100644 --- a/ui/signaturedlg.cpp +++ b/ui/signaturedlg.cpp @@ -1,374 +1,375 @@ /*************************************************************************** * 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 #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, &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)")); + const QUrl url = QFileDialog::getOpenFileUrl(this, i18n("Load Signature File"), QUrl::fromLocalFile(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/transfersettingsdialog.cpp b/ui/transfersettingsdialog.cpp index 0faa107c..7abe9164 100644 --- a/ui/transfersettingsdialog.cpp +++ b/ui/transfersettingsdialog.cpp @@ -1,184 +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 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->setUrl(m_transfer->directory()); 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(); } }