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; };