diff --git a/CMakeLists.txt b/CMakeLists.txt index c0d1084..5062a6a 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -53,7 +53,9 @@ set (PPIC_CPP_FILES app/exiv2wrapper.cpp app/playlistmanager.cpp app/fileopeneventhandler.cpp - app/winplaylistpatch.cpp + app/playlistscout.cpp + app/playlistscouts/qtscout.cpp + app/playlistscouts/explorerscout.cpp ) set (PPIC_HEADER_FILES @@ -71,7 +73,9 @@ set (PPIC_HEADER_FILES app/exiv2wrapper.h app/playlistmanager.h app/fileopeneventhandler.h - app/winplaylistpatch.h + app/playlistscout.h + app/playlistscouts/qtscout.h + app/playlistscouts/explorerscout.h ) set (PPIC_UI_FILES diff --git a/app/playlistmanager.cpp b/app/playlistmanager.cpp index 6ac67b6..804d3df 100644 --- a/app/playlistmanager.cpp +++ b/app/playlistmanager.cpp @@ -3,7 +3,7 @@ // SPDX-License-Identifier: MIT #include "playlistmanager.h" -#include "winplaylistpatch.h" +#include "playlistscout.h" #include #include @@ -72,50 +72,14 @@ bool PlaylistManager::loadSingleFilePlaylist(const QUrl& url) { if (!checkUrl(url)) return false; - QList playlist; - std::optional idx = std::nullopt; - -#ifdef Q_OS_WIN - // In Windows, Qt approach can not aware Windows Explorer custom sort order. - // So we need to try using Win32 functions for fetching it at first - if (!WinPlaylistPatch::loadSingleFilePlaylist(url, m_allowedSuffixes, playlist, idx)) { -#endif // Q_OS_WIN - - // If Windows approach is failed or we are not in Windows, - // use Qt way to process it. - - QFileInfo info(url.toLocalFile()); - QDir dir(info.path()); - QString currentFileName = info.fileName(); - - QStringList entryList = dir.entryList( - m_allowedSuffixes, QDir::Files | QDir::NoSymLinks, QDir::NoSort); - - QCollator collator; - collator.setNumericMode(true); - - std::sort(entryList.begin(), entryList.end(), collator); - - playlist.clear(); - idx = std::nullopt; - for (qsizetype i = 0; i < entryList.count(); i++) { - const QString &fileName = entryList.at(i); - const QString &oneEntry = dir.absoluteFilePath(fileName); - const QUrl &url = QUrl::fromLocalFile(oneEntry); - if (!checkUrl(url)) { - return false; - } - playlist.append(url); - if (fileName == currentFileName) { - idx = i; - } - } - -#ifdef Q_OS_WIN + // Scout playlist by given file + auto scoutResult = PlaylistScout::scout(url, m_allowedSuffixes); + if (!scoutResult.has_value()) { + return false; } -#endif // Q_OS_WIN - setPlaylist(std::move(playlist), idx); + // Setup playlist with index + setPlaylist(std::move(scoutResult->playlist), scoutResult->indexHint); return true; } diff --git a/app/playlistscout.cpp b/app/playlistscout.cpp new file mode 100644 index 0000000..f39c7f3 --- /dev/null +++ b/app/playlistscout.cpp @@ -0,0 +1,25 @@ +// SPDX-FileCopyrightText: 2026 yyc12345 +// +// SPDX-License-Identifier: MIT + +#include "playlistscout.h" + +#include "playlistscouts/qtscout.h" +#include "playlistscouts/explorerscout.h" + +std::optional PlaylistScout::scout( + const QUrl &url, const QStringList &allowedSuffixes) { + +#ifdef Q_OS_WIN + // In Windows, Qt approach can not aware Windows Explorer custom sort order. + // So we need to try using Win32 functions for fetching it at first. + if (auto rv = PlaylistScout::Explorer::scout(url, allowedSuffixes); rv.has_value()) { + return rv; + } +#endif // Q_OS_WIN + + // TODO: We may add macOS Finder, KDE Dolphin and etc supports in future. + + // Qt way is the last fallback. + return PlaylistScout::Qt::scout(url, allowedSuffixes); +} diff --git a/app/playlistscout.h b/app/playlistscout.h new file mode 100644 index 0000000..4d62a10 --- /dev/null +++ b/app/playlistscout.h @@ -0,0 +1,31 @@ +// SPDX-FileCopyrightText: 2026 yyc12345 +// +// SPDX-License-Identifier: MIT + +#pragma once + +#include +#include +#include +#include + +namespace PlaylistScout { + /// The result of scout + struct ScoutResult { + /// The final fetched playlist (it can be empty). + /// Each item must be a QUrl pointing to a local file. + QList playlist; + /// The index of given file located in playlist. + /// Nullopt if it is not presented in playlist. + std::optional indexHint; + }; + + /// @brief Scout available image files in given file's parent dir with given allowed suffixes. + /// @param[in] url The path to file whose parent dir is our target dir for finding image files. + /// @param[in] allowedSuffixes + /// Each items in this list is a wildcard pattern for matching allowed file name, like \c *.png . + /// The fetched item will be preserved if its name matching any of them. + /// If this list is empty, there is no restrictions for finding files. + /// @return Scout result if success, otherwise nullopt. + std::optional scout(const QUrl &url, const QStringList &allowedSuffixes); +} diff --git a/app/winplaylistpatch.cpp b/app/playlistscouts/explorerscout.cpp similarity index 96% rename from app/winplaylistpatch.cpp rename to app/playlistscouts/explorerscout.cpp index 5fe10b6..79c5a62 100644 --- a/app/winplaylistpatch.cpp +++ b/app/playlistscouts/explorerscout.cpp @@ -2,7 +2,7 @@ // // SPDX-License-Identifier: MIT -#include "winplaylistpatch.h" +#include "explorerscout.h" #ifdef Q_OS_WIN @@ -242,9 +242,9 @@ static ComPtr FindLiveFolderView(const QString& targetFolderPath) return nullptr; } -bool WinPlaylistPatch::loadSingleFilePlaylist( - const QUrl &url, const QStringList &allowedSuffixes, QList &playlist, - std::optional &indexHint) { +std::optional PlaylistScout::Explorer::scout( + const QUrl &url, const QStringList &allowedSuffixes) { + PlaylistScout::ScoutResult rv; QFileInfo info(url.toLocalFile()); QString folderPath = info.path(); @@ -263,14 +263,14 @@ bool WinPlaylistPatch::loadSingleFilePlaylist( ComGuard comGuard; if (!comGuard.IsInitialized()) { ReportError("COM initialization failed"); - return false; + return std::nullopt; } // Find a live Explorer window that is currently displaying the target folder. ComPtr folderView2 = FindLiveFolderView(folderPath); if (!folderView2) { ReportError("No open Explorer window matches the folder: " + folderPath); - return false; + return std::nullopt; } // Get items already sorted in Explorer's view order, as an IShellItemArray. @@ -279,7 +279,7 @@ bool WinPlaylistPatch::loadSingleFilePlaylist( if (FAILED(folderView2->Items(SVGIO_ALLVIEW | SVGIO_FLAG_VIEWORDER, IID_PPV_ARGS(itemArray.GetAddressOf())))) { ReportError("IFolderView2::Items(SVGIO_ALLVIEW | SVGIO_FLAG_VIEWORDER) failed"); - return false; + return std::nullopt; } DWORD itemCount = 0; @@ -320,13 +320,13 @@ bool WinPlaylistPatch::loadSingleFilePlaylist( continue; } - playlist.append(QUrl::fromLocalFile(filePath)); + rv.playlist.append(QUrl::fromLocalFile(filePath)); if (fileName == currentFileName) { - indexHint = playlist.size() - 1; + rv.indexHint = rv.playlist.size() - 1; } } - return true; + return rv; } #endif // Q_OS_WIN diff --git a/app/playlistscouts/explorerscout.h b/app/playlistscouts/explorerscout.h new file mode 100644 index 0000000..3b516d1 --- /dev/null +++ b/app/playlistscouts/explorerscout.h @@ -0,0 +1,16 @@ +// SPDX-FileCopyrightText: 2026 yyc12345 +// +// SPDX-License-Identifier: MIT + +#pragma once + +#include "../playlistscout.h" +#include + +#ifdef Q_OS_WIN + +namespace PlaylistScout::Explorer { + std::optional scout(const QUrl &url, const QStringList &allowedSuffixes); +} + +#endif // Q_OS_WIN diff --git a/app/playlistscouts/qtscout.cpp b/app/playlistscouts/qtscout.cpp new file mode 100644 index 0000000..4d1b1b6 --- /dev/null +++ b/app/playlistscouts/qtscout.cpp @@ -0,0 +1,40 @@ +// SPDX-FileCopyrightText: 2025 Gary Wang +// SPDX-FileCopyrightText: 2026 yyc12345 +// +// SPDX-License-Identifier: MIT + +#include "qtscout.h" + +#include +#include +#include + +std::optional PlaylistScout::Qt::scout( + const QUrl &url, const QStringList &allowedSuffixes) { + PlaylistScout::ScoutResult rv; + + QFileInfo info(url.toLocalFile()); + QDir dir(info.path()); + QString currentFileName = info.fileName(); + + QStringList entryList = dir.entryList( + allowedSuffixes, QDir::Files | QDir::NoSymLinks, QDir::NoSort); + + QCollator collator; + collator.setNumericMode(true); + + std::sort(entryList.begin(), entryList.end(), collator); + + for (qsizetype i = 0; i < entryList.count(); i++) { + const QString &fileName = entryList.at(i); + const QString &oneEntry = dir.absoluteFilePath(fileName); + const QUrl &url = QUrl::fromLocalFile(oneEntry); + + rv.playlist.append(url); + if (fileName == currentFileName) { + rv.indexHint = i; + } + } + + return rv; +} diff --git a/app/playlistscouts/qtscout.h b/app/playlistscouts/qtscout.h new file mode 100644 index 0000000..90e9fb2 --- /dev/null +++ b/app/playlistscouts/qtscout.h @@ -0,0 +1,11 @@ +// SPDX-FileCopyrightText: 2026 yyc12345 +// +// SPDX-License-Identifier: MIT + +#pragma once + +#include "../playlistscout.h" + +namespace PlaylistScout::Qt { + std::optional scout(const QUrl &url, const QStringList &allowedSuffixes); +} diff --git a/app/winplaylistpatch.h b/app/winplaylistpatch.h deleted file mode 100644 index 86f7954..0000000 --- a/app/winplaylistpatch.h +++ /dev/null @@ -1,21 +0,0 @@ -// SPDX-FileCopyrightText: 2026 yyc12345 -// -// SPDX-License-Identifier: MIT - -#pragma once - -#include - -#ifdef Q_OS_WIN - -#include -class WinPlaylistPatch -{ -public: - static bool loadSingleFilePlaylist(const QUrl &url, - const QStringList &allowedSuffixes, - QList &playlist, - std::optional &indexHint); -}; - -#endif // Q_OS_WIN