diff --git a/Quicksilver/PlugIns-Main/Finder/Info.plist b/Quicksilver/PlugIns-Main/Finder/Info.plist index 9f4b29f51..a0265743e 100644 --- a/Quicksilver/PlugIns-Main/Finder/Info.plist +++ b/Quicksilver/PlugIns-Main/Finder/Info.plist @@ -43,12 +43,43 @@ children + icon + FinderIcon + source + QSFileSystemObjectSource + settings + + parser + QSDirectoryParser + folderTypes + + com.apple.application + + folderDepth + 1 + scanContents + 1 + kind + Folder + path + /System/Library/CoreServices/Finder.app/Contents/Applications/ + skipItem + 1 + type + + ID QSPresetSidebarItems + name + Finder Sidebar Items + + + ID + QSPresetSidebarFavorites icon FinderIcon name - Finder Sidebar Items + Finder Favorites settings path diff --git a/Quicksilver/PlugIns-Main/QSCorePlugIn/Code/QSSharedFileListSource.m b/Quicksilver/PlugIns-Main/QSCorePlugIn/Code/QSSharedFileListSource.m index 8b602393a..ffe3bc0d9 100644 --- a/Quicksilver/PlugIns-Main/QSCorePlugIn/Code/QSSharedFileListSource.m +++ b/Quicksilver/PlugIns-Main/QSCorePlugIn/Code/QSSharedFileListSource.m @@ -11,6 +11,30 @@ @implementation QSSharedFileListSource +/** + * Returns the valid path for an SFL file. + * Tries .sfl3, .sfl2, and .sfl extensions in order. + * @return NSString path, or nil if no valid file exists + */ +- (NSString *)validPathForSfl:(NSString *)sflPath +{ + NSString *basePath = [sflPath stringByStandardizingPath]; + // Strip any existing .sfl* extension + basePath = [basePath stringByDeletingPathExtension]; + + // Try extensions in order: .sfl3, .sfl2, .sfl + NSFileManager *manager = [NSFileManager defaultManager]; + NSArray *extensions = @[@".sfl3", @".sfl2", @".sfl"]; + for (NSString *ext in extensions) { + NSString *testPath = [basePath stringByAppendingString:ext]; + BOOL isDir = NO; + if ([manager fileExistsAtPath:testPath isDirectory:&isDir] && !isDir) { + return testPath; + } + } + return nil; +} + - (BOOL)indexIsValidFromDate:(NSDate *)indexDate forEntry:(NSDictionary *)theEntry { NSDictionary *settings = [theEntry objectForKey:kItemSettings]; @@ -18,15 +42,13 @@ - (BOOL)indexIsValidFromDate:(NSDate *)indexDate forEntry:(NSDictionary *)theEnt if (!sflPath) { return YES; } - NSString *path = [sflPath stringByStandardizingPath]; - if ([NSApplication isHighSierra]) { - path = [path stringByReplacingOccurrencesOfString:@".sfl" withString:@".sfl2"]; - } - NSFileManager *manager = [NSFileManager defaultManager]; - BOOL isDir = NO; - if (![[NSFileManager defaultManager] fileExistsAtPath:path isDirectory:&isDir] || isDir) { + + NSString *path = [self validPathForSfl:sflPath]; + if (!path) { return YES; } + + NSFileManager *manager = [NSFileManager defaultManager]; NSDate *modDate = [[manager attributesOfItemAtPath:path error:NULL] fileModificationDate]; if ([modDate compare:indexDate] == NSOrderedDescending) { return NO; @@ -39,29 +61,45 @@ - (NSArray *)objectsForEntry:(NSDictionary *)theEntry NSDictionary *settings = [theEntry objectForKey:kItemSettings]; NSMutableArray *sflItemArray = [NSMutableArray arrayWithCapacity:0]; NSString *sflPath = [settings objectForKey:kItemPath]; - NSString *path = [sflPath stringByStandardizingPath]; - if ([NSApplication isHighSierra]) { - path = [path stringByReplacingOccurrencesOfString:@".sfl" withString:@".sfl2"]; - } - BOOL isDir = NO; - if (![[NSFileManager defaultManager] fileExistsAtPath:path isDirectory:&isDir] || isDir) { + + NSString *path = [self validPathForSfl:sflPath]; + if (!path) { return nil; } + + NSString *extension = [path pathExtension]; + NSDictionary *sflData = [NSKeyedUnarchiver unarchiveObjectWithFile:path]; NSString *kItems = @"items"; if (![[sflData allKeys] containsObject:kItems]) { return nil; } - if ([NSApplication isHighSierra]) { + + // Parse based on file extension: sfl2 and sfl3 use bookmark data, sfl uses SFLListItem + if ([extension isEqualToString:@"sfl2"] || [extension isEqualToString:@"sfl3"]) { return [sflData[kItems] arrayByEnumeratingArrayUsingBlock:^id(NSDictionary *item) { - NSData *bookmarkData = item[@"Bookmark"]; + // Bookmark data might be direct NSData or wrapped in a dictionary with NS.data key + id bookmarkValue = item[@"Bookmark"]; + NSData *bookmarkData = nil; + + if ([bookmarkValue isKindOfClass:[NSData class]]) { + bookmarkData = bookmarkValue; + } else if ([bookmarkValue isKindOfClass:[NSDictionary class]]) { + // Try NS.data key (common in sfl3) + bookmarkData = bookmarkValue[@"NS.data"]; + } + + if (!bookmarkData) { + return nil; + } + NSURL *url = [NSURL URLByResolvingBookmarkData:bookmarkData options:NSURLBookmarkResolutionWithoutUI|NSURLBookmarkResolutionWithoutMounting relativeToURL:nil bookmarkDataIsStale:nil error:nil]; if ([url isFileURL]) { return [QSObject fileObjectWithFileURL:url]; } return [QSObject URLObjectWithURL:[url absoluteString] title:item[@"Name"]]; }]; - } else { + } else if ([extension isEqualToString:@"sfl"]) { for (SFLListItem *item in sflData[kItems]) { // item's class is SFLListItem if ([item URL]) { diff --git a/Quicksilver/PlugIns-Main/QSCorePlugIn/QSCorePlugIn-Info.plist b/Quicksilver/PlugIns-Main/QSCorePlugIn/QSCorePlugIn-Info.plist index c2aeeb5ca..02292f86a 100644 --- a/Quicksilver/PlugIns-Main/QSCorePlugIn/QSCorePlugIn-Info.plist +++ b/Quicksilver/PlugIns-Main/QSCorePlugIn/QSCorePlugIn-Info.plist @@ -652,7 +652,6 @@ feature 1 - catalogPath / @@ -1139,8 +1138,8 @@ icon QSDirectObjectIconProxy - runInMainThread - + runInMainThread + QSObjectShowChildMenu diff --git a/Quicksilver/PlugIns-Main/QSCorePlugIn/Resources/en.lproj/QSCatalogPreset.name.strings b/Quicksilver/PlugIns-Main/QSCorePlugIn/Resources/en.lproj/QSCatalogPreset.name.strings index b0174e768..4595178b3 100644 --- a/Quicksilver/PlugIns-Main/QSCorePlugIn/Resources/en.lproj/QSCatalogPreset.name.strings +++ b/Quicksilver/PlugIns-Main/QSCorePlugIn/Resources/en.lproj/QSCatalogPreset.name.strings @@ -212,6 +212,8 @@ Shelf & Clipboard QSPresetSidebarItems Finder Sidebar + QSPresetSidebarFavorites + Finder Favorites QSPresetSogudiShortcuts Sogudi Shortcuts QSPresetSpeakableItems diff --git a/Quicksilver/Quicksilver.xcodeproj/project.pbxproj b/Quicksilver/Quicksilver.xcodeproj/project.pbxproj index 800c170f5..6638448fd 100644 --- a/Quicksilver/Quicksilver.xcodeproj/project.pbxproj +++ b/Quicksilver/Quicksilver.xcodeproj/project.pbxproj @@ -1245,7 +1245,7 @@ 4DFE7DA10E081A30000B9AA3 /* README.rtf */ = {isa = PBXFileReference; lastKnownFileType = text.rtf; path = README.rtf; sourceTree = ""; }; 4DFE7DAD0E081BFD000B9AA3 /* QuickLook.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = QuickLook.framework; path = /System/Library/Frameworks/QuickLook.framework; sourceTree = ""; }; 6008C51E2AAF433900512CB2 /* QSPathsTests.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = QSPathsTests.m; sourceTree = ""; }; - 600950BC2ABB76AF00F67DEB /* QSCorePlugIn-Info-Testing.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = "QSCorePlugIn-Info-Testing.plist"; sourceTree = ""; }; + 600950BC2ABB76AF00F67DEB /* QSCorePlugIn-Info-Testing.plist */ = {isa = PBXFileReference; explicitFileType = text.plist.xml; fileEncoding = 4; path = "QSCorePlugIn-Info-Testing.plist"; sourceTree = ""; }; 604EAB042CDE5B1E005B3451 /* QSSwiftObj.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = QSSwiftObj.swift; sourceTree = ""; }; 60FCBED02844C9770091AB6B /* OSAKit.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = OSAKit.framework; path = System/Library/Frameworks/OSAKit.framework; sourceTree = SDKROOT; }; 6535A8DE1086EF23009D5C90 /* English */ = {isa = PBXFileReference; fileEncoding = 10; lastKnownFileType = text.plist.strings; name = English; path = en.lproj/Localizable.strings; sourceTree = ""; }; @@ -2098,7 +2098,7 @@ D46D3C7916B33D0B00387EA9 /* countBadge4@2x.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = "countBadge4@2x.png"; sourceTree = ""; }; D46D3C7A16B33D0B00387EA9 /* countBadge5@2x.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = "countBadge5@2x.png"; sourceTree = ""; }; D48F231B1C99CCC4006504A8 /* QSSharedFileListSource.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = QSSharedFileListSource.h; sourceTree = ""; }; - D48F231C1C99CCC4006504A8 /* QSSharedFileListSource.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = QSSharedFileListSource.m; sourceTree = ""; }; + D48F231C1C99CCC4006504A8 /* QSSharedFileListSource.m */ = {isa = PBXFileReference; fileEncoding = 4; indentWidth = 2; lastKnownFileType = sourcecode.c.objc; path = QSSharedFileListSource.m; sourceTree = ""; usesTabs = 1; }; D48FC4FB1FBE89B4009600EB /* QSWebSource.xib */ = {isa = PBXFileReference; lastKnownFileType = file.xib; path = QSWebSource.xib; sourceTree = ""; }; D49399091350078E00B908C6 /* QSDownloads.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = QSDownloads.h; sourceTree = ""; usesTabs = 1; }; D493990A1350078E00B908C6 /* QSDownloads.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = QSDownloads.m; sourceTree = ""; usesTabs = 1; }; @@ -2198,7 +2198,7 @@ E180011607B2B48900010DB0 /* QSTextSource.m */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.c.objc; path = QSTextSource.m; sourceTree = ""; }; E180011707B2B48900010DB0 /* QSWebSource.h */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.c.h; path = QSWebSource.h; sourceTree = ""; }; E180011807B2B48900010DB0 /* QSWebSource.m */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.c.objc; path = QSWebSource.m; sourceTree = ""; }; - E180011907B2B48900010DB0 /* QSCorePlugIn-Info.plist */ = {isa = PBXFileReference; explicitFileType = text.plist.info; fileEncoding = 30; path = "QSCorePlugIn-Info.plist"; sourceTree = ""; }; + E180011907B2B48900010DB0 /* QSCorePlugIn-Info.plist */ = {isa = PBXFileReference; explicitFileType = text.plist.info; fileEncoding = 4; path = "QSCorePlugIn-Info.plist"; sourceTree = ""; }; E18001C507B2BBB800010DB0 /* main.m */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.c.objc; path = main.m; sourceTree = ""; usesTabs = 1; }; E18001C807B2BBB800010DB0 /* QSApp.h */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.c.h; path = QSApp.h; sourceTree = ""; usesTabs = 1; }; E18001C907B2BBB800010DB0 /* QSApp.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = QSApp.m; sourceTree = ""; usesTabs = 1; };