diff --git a/src/base/bittorrent/torrenthandle.cpp b/src/base/bittorrent/torrenthandle.cpp index 41a1a0a05..7061b2987 100644 --- a/src/base/bittorrent/torrenthandle.cpp +++ b/src/base/bittorrent/torrenthandle.cpp @@ -59,6 +59,7 @@ #include "base/profile.h" #include "base/tristatebool.h" #include "base/utils/fs.h" +#include "base/utils/string.h" #include "downloadpriority.h" #include "peerinfo.h" #include "session.h" @@ -1481,6 +1482,7 @@ void TorrentHandle::moveStorage(const QString &newPath, bool overwrite) void TorrentHandle::renameFile(const int index, const QString &name) { + m_oldPath[LTFileIndex {index}].push_back(filePath(index)); ++m_renameCount; m_nativeHandle.rename_file(index, Utils::Fs::toNativePath(name).toStdString()); } @@ -1744,29 +1746,42 @@ void TorrentHandle::handleFastResumeRejectedAlert(const lt::fastresume_rejected_ void TorrentHandle::handleFileRenamedAlert(const lt::file_renamed_alert *p) { - const QString newName = Utils::Fs::fromNativePath(p->new_name()); - - // TODO: Check this! - if (filesCount() > 1) { - // Check if folders were renamed - QStringList oldPathParts = m_torrentInfo.origFilePath(p->index).split('/'); - oldPathParts.removeLast(); - QString oldPath = oldPathParts.join('/'); - QStringList newPathParts = newName.split('/'); - newPathParts.removeLast(); - const QString newPath = newPathParts.join('/'); - if (!newPathParts.isEmpty() && (oldPath != newPath)) { - qDebug("oldPath(%s) != newPath(%s)", qUtf8Printable(oldPath), qUtf8Printable(newPath)); - oldPath = QString("%1/%2").arg(savePath(true), oldPath); - qDebug("Detected folder renaming, attempt to delete old folder: %s", qUtf8Printable(oldPath)); - QDir().rmpath(oldPath); - } - } - // We don't really need to call updateStatus() in this place. // All we need to do is make sure we have a valid instance of the TorrentInfo object. m_torrentInfo = TorrentInfo {m_nativeHandle.torrent_file()}; + // remove empty leftover folders + // for example renaming "a/b/c" to "d/b/c", then folders "a/b" and "a" will + // be removed if they are empty + const QString oldFilePath = m_oldPath[LTFileIndex {p->index}].takeFirst(); + const QString newFilePath = Utils::Fs::fromNativePath(p->new_name()); + + if (m_oldPath[LTFileIndex {p->index}].isEmpty()) + m_oldPath.remove(LTFileIndex {p->index}); + + QVector oldPathParts = oldFilePath.splitRef('/', QString::SkipEmptyParts); + oldPathParts.removeLast(); // drop file name part + QVector newPathParts = newFilePath.splitRef('/', QString::SkipEmptyParts); + newPathParts.removeLast(); // drop file name part + +#if defined(Q_OS_WIN) + const Qt::CaseSensitivity caseSensitivity = Qt::CaseInsensitive; +#else + const Qt::CaseSensitivity caseSensitivity = Qt::CaseSensitive; +#endif + + int pathIdx = 0; + while ((pathIdx < oldPathParts.size()) && (pathIdx < newPathParts.size())) { + if (oldPathParts[pathIdx].compare(newPathParts[pathIdx], caseSensitivity) != 0) + break; + ++pathIdx; + } + + for (int i = (oldPathParts.size() - 1); i >= pathIdx; --i) { + QDir().rmdir(savePath() + Utils::String::join(oldPathParts, QLatin1String("/"))); + oldPathParts.removeLast(); + } + --m_renameCount; while (!isMoveInProgress() && (m_renameCount == 0) && !m_moveFinishedTriggers.isEmpty()) m_moveFinishedTriggers.takeFirst()(); @@ -1774,12 +1789,14 @@ void TorrentHandle::handleFileRenamedAlert(const lt::file_renamed_alert *p) void TorrentHandle::handleFileRenameFailedAlert(const lt::file_rename_failed_alert *p) { - Q_UNUSED(p); - LogMsg(tr("File rename failed. Torrent: \"%1\", file: \"%2\", reason: \"%3\"") .arg(name(), filePath(p->index) , QString::fromStdString(p->error.message())), Log::WARNING); + m_oldPath[LTFileIndex {p->index}].removeFirst(); + if (m_oldPath[LTFileIndex {p->index}].isEmpty()) + m_oldPath.remove(LTFileIndex {p->index}); + --m_renameCount; while (!isMoveInProgress() && (m_renameCount == 0) && !m_moveFinishedTriggers.isEmpty()) m_moveFinishedTriggers.takeFirst()(); diff --git a/src/base/bittorrent/torrenthandle.h b/src/base/bittorrent/torrenthandle.h index 4513d805e..7471957c3 100644 --- a/src/base/bittorrent/torrenthandle.h +++ b/src/base/bittorrent/torrenthandle.h @@ -355,6 +355,12 @@ namespace BitTorrent private: typedef std::function EventTrigger; +#if (LIBTORRENT_VERSION_NUM < 10200) + using LTFileIndex = int; +#else + using LTFileIndex = lt::file_index_t; +#endif + void updateStatus(); void updateStatus(const lt::torrent_status &nativeStatus); void updateState(); @@ -417,6 +423,10 @@ namespace BitTorrent QQueue m_moveFinishedTriggers; int m_renameCount; + // Until libtorrent provide an "old_name" field in `file_renamed_alert` + // we will rely on this workaround to remove empty leftover folders + QHash> m_oldPath; + bool m_useAutoTMM; // Persistent data diff --git a/src/base/utils/string.cpp b/src/base/utils/string.cpp index f1412b23e..78dd080df 100644 --- a/src/base/utils/string.cpp +++ b/src/base/utils/string.cpp @@ -198,3 +198,14 @@ TriStateBool Utils::String::parseTriStateBool(const QString &string) return TriStateBool::False; return TriStateBool::Undefined; } + +QString Utils::String::join(const QVector &strings, const QString &separator) +{ + if (strings.empty()) + return {}; + + QString ret = strings[0].toString(); + for (int i = 1; i < strings.count(); ++i) + ret += (separator + strings[i]); + return ret; +} diff --git a/src/base/utils/string.h b/src/base/utils/string.h index a04ac6fc3..53e6392da 100644 --- a/src/base/utils/string.h +++ b/src/base/utils/string.h @@ -31,8 +31,10 @@ #define UTILS_STRING_H #include +#include class QString; +class QStringRef; class TriStateBool; @@ -66,6 +68,8 @@ namespace Utils bool parseBool(const QString &string, bool defaultValue); TriStateBool parseTriStateBool(const QString &string); + + QString join(const QVector &strings, const QString &separator); } } diff --git a/src/gui/torrentcontenttreeview.cpp b/src/gui/torrentcontenttreeview.cpp index 1a0761a23..af38e34d3 100644 --- a/src/gui/torrentcontenttreeview.cpp +++ b/src/gui/torrentcontenttreeview.cpp @@ -204,15 +204,6 @@ void TorrentContentTreeView::renameSelectedFile(BitTorrent::TorrentHandle *torre if (needForceRecheck) torrent->forceRecheck(); - // Remove old folder - const QString oldFullPath = torrent->savePath(true) + oldPath; - int timeout = 10; - while (!QDir().rmpath(oldFullPath) && (timeout > 0)) { - // FIXME: We should not sleep here (freezes the UI for 1 second) - QThread::msleep(100); - --timeout; - } - model->setData(modelIndex, newName); } }