1
0
Fork 0
mirror of https://github.com/transmission/transmission synced 2024-12-23 08:13:27 +00:00

feat: render file tree in legacy html-based QuickLook preview extension (#6091)

Signed-off-by: Dzmitry Neviadomski <nevack.d@gmail.com>
This commit is contained in:
Dzmitry Neviadomski 2023-10-13 02:53:57 +03:00 committed by GitHub
parent 1500417704
commit 8ef7eba3bd
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23

View file

@ -3,8 +3,11 @@
@import QuickLook; @import QuickLook;
#include <string> #include <string>
#include <unordered_map>
#include <vector>
#include <libtransmission/torrent-metainfo.h> #include <libtransmission/torrent-metainfo.h>
#include <libtransmission/utils.h>
#import "NSStringAdditions.h" #import "NSStringAdditions.h"
@ -13,6 +16,31 @@ OSStatus GeneratePreviewForURL(void* thisInterface, QLPreviewRequestRef preview,
void CancelPreviewGeneration(void* thisInterface, QLPreviewRequestRef preview); void CancelPreviewGeneration(void* thisInterface, QLPreviewRequestRef preview);
QL_EXTERN_C_END QL_EXTERN_C_END
static NSUInteger const kIconWidth = 16;
namespace
{
class FileTreeNode
{
public:
FileTreeNode() = default;
~FileTreeNode() = default;
auto MaybeCreateChild(std::string_view child_name)
{
return children_.try_emplace(std::string{ child_name });
}
private:
FileTreeNode(FileTreeNode const&) = delete;
FileTreeNode& operator=(FileTreeNode&) = delete;
std::unordered_map<std::string, FileTreeNode> children_;
};
} // namespace
NSString* generateIconData(NSString* fileExtension, NSUInteger width, NSMutableDictionary* allImgProps) NSString* generateIconData(NSString* fileExtension, NSUInteger width, NSMutableDictionary* allImgProps)
{ {
NSString* rawFilename = ![fileExtension isEqualToString:@""] ? fileExtension : @"blank_file_name_transmission"; NSString* rawFilename = ![fileExtension isEqualToString:@""] ? fileExtension : @"blank_file_name_transmission";
@ -189,24 +217,63 @@ OSStatus GeneratePreviewForURL(void* /*thisInterface*/, QLPreviewRequestRef prev
localizedStringWithFormat:NSLocalizedStringFromTableInBundle(@"%lu Files", nil, bundle, "quicklook file header"), n_files]; localizedStringWithFormat:NSLocalizedStringFromTableInBundle(@"%lu Files", nil, bundle, "quicklook file header"), n_files];
[listSection appendFormat:@"<tr><th>%@</th></tr>", fileTitleString]; [listSection appendFormat:@"<tr><th>%@</th></tr>", fileTitleString];
#warning display folders? FileTreeNode root{};
for (auto const& [path, size] : metainfo.files().sortedByPath()) for (auto const& [path, size] : metainfo.files().sortedByPath())
{ {
NSString* fullFilePath = @(path.c_str()); FileTreeNode* curNode = &root;
NSCAssert([fullFilePath hasPrefix:[name stringByAppendingString:@"/"]], @"Expected file path %@ to begin with %@/", fullFilePath, name); size_t level = 0;
NSString* shortenedFilePath = [fullFilePath substringFromIndex:name.length + 1]; auto subpath = std::string_view{ path };
NSString* fileSize = [NSString stringForFileSize:size]; auto path_vec = std::vector<std::string_view>{};
auto token = std::string_view{};
while (tr_strv_sep(&subpath, &token, '/'))
{
path_vec.emplace_back(token);
}
size_t const last = path_vec.size() - 1;
NSUInteger const icon_width = 16; for (auto const& part : path_vec)
[listSection appendFormat:@"<tr><td><img class=\"icon\" src=\"%@\" width=\"%ld\" height=\"%ld\" />%@</td><td class=\"grey\">%@</td></tr>", {
generateIconData(shortenedFilePath.pathExtension, icon_width, allImgProps), auto [it, inserted] = curNode->MaybeCreateChild(part);
icon_width, if (inserted)
icon_width, {
shortenedFilePath, NSString* prefix = @"";
for (size_t i = 0; i < level; ++i)
{
prefix = [prefix stringByAppendingString:@"&emsp;"];
}
NSString* pathPart = @(it->first.c_str());
NSString* pathExt = nil;
NSString* fileSize = nil;
if (level < last)
{
// This node is a directory.
pathExt = NSFileTypeForHFSTypeCode(kGenericFolderIcon);
fileSize = @"";
}
else
{
// This node is a leaf file.
pathExt = pathPart.pathExtension;
fileSize = [NSString stringForFileSize:size];
}
[listSection appendFormat:@"<tr><td>%@<img class=\"icon\" src=\"%@\" width=\"%ld\" height=\"%ld\" />%@</td><td class=\"grey\">%@</td></tr>",
prefix,
generateIconData(pathExt, kIconWidth, allImgProps),
kIconWidth,
kIconWidth,
pathPart,
fileSize]; fileSize];
} }
curNode = &it->second;
level++;
}
}
[listSection appendString:@"</table>"]; [listSection appendString:@"</table>"];
[lists addObject:listSection]; [lists addObject:listSection];