diff --git a/QLSmartFolder.xcodeproj/project.pbxproj b/QLSmartFolder.xcodeproj/project.pbxproj index a952430..25e11b9 100644 --- a/QLSmartFolder.xcodeproj/project.pbxproj +++ b/QLSmartFolder.xcodeproj/project.pbxproj @@ -7,14 +7,15 @@ objects = { /* Begin PBXBuildFile section */ + 4A2A823A213565AE0057AE57 /* smartFolderIcon.png in Resources */ = {isa = PBXBuildFile; fileRef = 4A2A8239213565AE0057AE57 /* smartFolderIcon.png */; }; 4A8AF8DB2133B9FF00B0A97B /* GenerateThumbnailForURL.m in Sources */ = {isa = PBXBuildFile; fileRef = 4A8AF8DA2133B9FF00B0A97B /* GenerateThumbnailForURL.m */; }; 4A8AF8DD2133B9FF00B0A97B /* GeneratePreviewForURL.m in Sources */ = {isa = PBXBuildFile; fileRef = 4A8AF8DC2133B9FF00B0A97B /* GeneratePreviewForURL.m */; }; 4A8AF8DF2133B9FF00B0A97B /* main.c in Sources */ = {isa = PBXBuildFile; fileRef = 4A8AF8DE2133B9FF00B0A97B /* main.c */; }; 4A8AF8E92133BEF100B0A97B /* template.html in Resources */ = {isa = PBXBuildFile; fileRef = 4A8AF8E82133BEF100B0A97B /* template.html */; }; - 4ABF9B502133ED51009DEE78 /* CoreSpotlight.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 4ABF9B4F2133ED51009DEE78 /* CoreSpotlight.framework */; }; /* End PBXBuildFile section */ /* Begin PBXFileReference section */ + 4A2A8239213565AE0057AE57 /* smartFolderIcon.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = smartFolderIcon.png; sourceTree = ""; }; 4A8AF8D72133B9FE00B0A97B /* QLSmartFolder.qlgenerator */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = QLSmartFolder.qlgenerator; sourceTree = BUILT_PRODUCTS_DIR; }; 4A8AF8DA2133B9FF00B0A97B /* GenerateThumbnailForURL.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = GenerateThumbnailForURL.m; sourceTree = ""; }; 4A8AF8DC2133B9FF00B0A97B /* GeneratePreviewForURL.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = GeneratePreviewForURL.m; sourceTree = ""; }; @@ -22,7 +23,6 @@ 4A8AF8E02133B9FF00B0A97B /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; 4A8AF8E62133BB3100B0A97B /* Shared.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = Shared.h; sourceTree = ""; }; 4A8AF8E82133BEF100B0A97B /* template.html */ = {isa = PBXFileReference; lastKnownFileType = text.html; path = template.html; sourceTree = ""; }; - 4ABF9B4F2133ED51009DEE78 /* CoreSpotlight.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = CoreSpotlight.framework; path = System/Library/Frameworks/CoreSpotlight.framework; sourceTree = SDKROOT; }; /* End PBXFileReference section */ /* Begin PBXFrameworksBuildPhase section */ @@ -30,7 +30,6 @@ isa = PBXFrameworksBuildPhase; buildActionMask = 2147483647; files = ( - 4ABF9B502133ED51009DEE78 /* CoreSpotlight.framework in Frameworks */, ); runOnlyForDeploymentPostprocessing = 0; }; @@ -42,7 +41,6 @@ children = ( 4A8AF8D92133B9FE00B0A97B /* QLSmartFolder */, 4A8AF8D82133B9FE00B0A97B /* Products */, - 4ABF9B4E2133ED50009DEE78 /* Frameworks */, ); sourceTree = ""; }; @@ -70,19 +68,12 @@ 4A8AF8E72133BED800B0A97B /* Resources */ = { isa = PBXGroup; children = ( + 4A2A8239213565AE0057AE57 /* smartFolderIcon.png */, 4A8AF8E82133BEF100B0A97B /* template.html */, ); path = Resources; sourceTree = ""; }; - 4ABF9B4E2133ED50009DEE78 /* Frameworks */ = { - isa = PBXGroup; - children = ( - 4ABF9B4F2133ED51009DEE78 /* CoreSpotlight.framework */, - ); - name = Frameworks; - sourceTree = ""; - }; /* End PBXGroup section */ /* Begin PBXHeadersBuildPhase section */ @@ -150,6 +141,7 @@ isa = PBXResourcesBuildPhase; buildActionMask = 2147483647; files = ( + 4A2A823A213565AE0057AE57 /* smartFolderIcon.png in Resources */, 4A8AF8E92133BEF100B0A97B /* template.html in Resources */, ); runOnlyForDeploymentPostprocessing = 0; diff --git a/QLSmartFolder.xcodeproj/project.xcworkspace/xcuserdata/hatanokenta.xcuserdatad/UserInterfaceState.xcuserstate b/QLSmartFolder.xcodeproj/project.xcworkspace/xcuserdata/hatanokenta.xcuserdatad/UserInterfaceState.xcuserstate index 2b41026..8a2c448 100644 Binary files a/QLSmartFolder.xcodeproj/project.xcworkspace/xcuserdata/hatanokenta.xcuserdatad/UserInterfaceState.xcuserstate and b/QLSmartFolder.xcodeproj/project.xcworkspace/xcuserdata/hatanokenta.xcuserdatad/UserInterfaceState.xcuserstate differ diff --git a/QLSmartFolder/GeneratePreviewForURL.m b/QLSmartFolder/GeneratePreviewForURL.m index f14969d..d34f57c 100644 --- a/QLSmartFolder/GeneratePreviewForURL.m +++ b/QLSmartFolder/GeneratePreviewForURL.m @@ -16,70 +16,80 @@ OSStatus GeneratePreviewForURL(void *thisInterface, QLPreviewRequestRef preview, CFURLRef url, CFStringRef contentTypeUTI, CFDictionaryRef options) { @autoreleasepool { + NSURL *imgURL = [[NSBundle bundleWithIdentifier:kPluginBundleId] URLForResource:@"smartFolderIcon" withExtension:@"png"]; + NSURL *htmlURL = [[NSBundle bundleWithIdentifier:kPluginBundleId] URLForResource:@"template" withExtension:@"html"]; NSMutableString *previewHtml = [NSMutableString stringWithContentsOfURL:htmlURL encoding:NSUTF8StringEncoding error:NULL]; NSURL *URL = (__bridge NSURL *)url; NSString *name = [[[URL absoluteString] lastPathComponent] stringByRemovingPercentEncoding]; + NSDictionary *urlAttributes = [URL resourceValuesForKeys:@[NSURLContentAccessDateKey] error:NULL]; + NSDate *lastAccessDate = urlAttributes[NSURLContentAccessDateKey]; + NSString *error; NSPropertyListFormat format; NSDictionary *contentDict = [NSPropertyListSerialization propertyListWithData:[NSData dataWithContentsOfURL:URL] options:0 format:&format error:&error]; - NSString *queryString = [((NSString *)contentDict[@"RawQueryDict"][@"RawQuery"]) stringByReplacingOccurrencesOfString:@"kMDItem" withString:@""]; + NSString *rawQueryString = contentDict[@"RawQueryDict"][@"RawQuery"]; + + NSMutableString *queryString = [[NSMutableString alloc] init]; NSArray *criteriaSlices = contentDict[@"SearchCriteria"][@"FXCriteriaSlices"]; - NSMutableArray *attributes = [[NSMutableArray alloc] init]; - NSMutableDictionary *criteriaTable = [[NSMutableDictionary alloc] init]; for (NSDictionary *criteriaSlice in criteriaSlices) { - NSArray *criteriaArray = (criteriaSlice[@"criteria"]); - NSString *originalCriteria = [((NSString *)criteriaArray[0]) stringByReplacingOccurrencesOfString:@"kMDItem" withString:@""]; - NSString *newCriteria = [[[originalCriteria substringToIndex:1] lowercaseString] stringByAppendingString:[originalCriteria substringFromIndex:1]]; - [attributes addObject:newCriteria]; - - criteriaTable[originalCriteria] = newCriteria; + if ([queryString length] > 0) { + [queryString appendString:@" & "]; + } + NSArray *displayValuesArray = (criteriaSlice[@"displayValues"]); + NSString *displayValues = [displayValuesArray componentsJoinedByString:@" "]; + [queryString appendString:displayValues]; } - for (NSString *key in criteriaTable) { - queryString = [queryString stringByReplacingOccurrencesOfString:key withString:criteriaTable[key]]; + NSDictionary *scopesReplacement = @{ @"kMDQueryScopeHome": @"Home", + @"kMDQueryScopeComputer": @"Computer", + @"kMDQueryScopeNetwork": @"Network", + @"kMDQueryScopeAllIndexed": @"All Indexed", + @"kMDQueryScopeComputerIndexed": @"Computer Indexed", + @"kMDQueryScopeNetworkIndexed": @"Network Indexed" + }; + + NSMutableArray *scopes = contentDict[@"RawQueryDict"][@"SearchScopes"]; + for (NSInteger i = 0; i < [scopes count]; i++) { + for (NSString *key in scopesReplacement) { + if ([scopes[i] isEqualToString:key]) { + scopes[i] = scopesReplacement[key]; + } + } } + NSDateFormatter *formatter = [[NSDateFormatter alloc] init]; + [formatter setDateFormat:@"yyyy/MM/dd HH:mm:ss"]; + [formatter setTimeZone:[NSTimeZone systemTimeZone]]; + NSMutableDictionary *previewReplacement = [[NSMutableDictionary alloc] init]; previewReplacement[@"__Name_Value__"] = name; + previewReplacement[@"__Last_Access_Date__"] = @"Last Access Date"; + previewReplacement[@"__Last_Access_Date_Value__"] = [formatter stringFromDate:lastAccessDate]; previewReplacement[@"__Scopes__"] = @"Scopes"; - previewReplacement[@"__Scopes_Value__"] = [(NSArray *)(contentDict[@"RawQueryDict"][@"SearchScopes"]) componentsJoinedByString:@", "]; + previewReplacement[@"__Scopes_Value__"] = [scopes componentsJoinedByString:@", "]; previewReplacement[@"__Query__"] = @"Query"; previewReplacement[@"__Query_Value__"] = queryString; + previewReplacement[@"__Raw_Query__"] = @"Raw Query"; + previewReplacement[@"__Raw_Query_Value__"] = rawQueryString; - __block NSMutableArray *foundItems = [[NSMutableArray alloc] init]; - __block NSString *errorString = @""; - __block BOOL searchFinished = NO; - - CSSearchQuery *query = [[CSSearchQuery alloc] initWithQueryString:queryString attributes:attributes]; - query.foundItemsHandler = ^(NSArray * _Nonnull items) { - [foundItems addObjectsFromArray:items]; - }; - query.completionHandler = ^(NSError * _Nullable error) { - errorString = [error description] ?: @""; - searchFinished = YES; - }; - [query start]; - - NSInteger i = 0; - while ([query foundItemCount] < 10 && i < 100 && ![query isCancelled] && !searchFinished) { - [NSThread sleepForTimeInterval:0.1f]; - i++; - } - - [previewHtml appendFormat:@"", (long)[query foundItemCount]]; - for (CSSearchableItem *item in foundItems) { - [previewHtml appendFormat:@"
%ld Files
", [item uniqueIdentifier]]; - } [previewHtml appendString:@"
%@
"]; - [previewHtml appendString:errorString]; + + NSData *imgData = [[NSData alloc] initWithContentsOfURL:imgURL]; NSDictionary *properties = @{(__bridge NSString *)kQLPreviewPropertyTextEncodingNameKey : @"UTF-8", - (__bridge NSString *)kQLPreviewPropertyMIMETypeKey : @"text/html" }; + (__bridge NSString *)kQLPreviewPropertyMIMETypeKey : @"text/html", + (__bridge NSString *)kQLPreviewPropertyAttachmentsKey : @{ + @"icon" : @{ + (__bridge NSString*)kQLPreviewPropertyMIMETypeKey : @"image/png", + (__bridge NSString*)kQLPreviewPropertyAttachmentDataKey: imgData, + } + } + }; for (NSString *key in previewReplacement) { [previewHtml replaceOccurrencesOfString:key @@ -88,8 +98,6 @@ OSStatus GeneratePreviewForURL(void *thisInterface, QLPreviewRequestRef preview, range:NSMakeRange(0, [previewHtml length])]; } - [query cancel]; - QLPreviewRequestSetDataRepresentation(preview, (__bridge CFDataRef)[previewHtml dataUsingEncoding:NSUTF8StringEncoding], kUTTypeHTML, (__bridge CFDictionaryRef)properties); } diff --git a/QLSmartFolder/Info.plist b/QLSmartFolder/Info.plist index aa5a39d..fa3a32d 100644 --- a/QLSmartFolder/Info.plist +++ b/QLSmartFolder/Info.plist @@ -50,9 +50,9 @@ QLNeedsToBeRunInMainThread QLPreviewHeight - 600 + 400 QLPreviewWidth - 800 + 600 QLSupportsConcurrentRequests QLThumbnailMinimumSize diff --git a/QLSmartFolder/Resources/smartFolderIcon.png b/QLSmartFolder/Resources/smartFolderIcon.png new file mode 100644 index 0000000..3362339 Binary files /dev/null and b/QLSmartFolder/Resources/smartFolderIcon.png differ diff --git a/QLSmartFolder/Resources/template.html b/QLSmartFolder/Resources/template.html index b5b3fed..0d6022d 100644 --- a/QLSmartFolder/Resources/template.html +++ b/QLSmartFolder/Resources/template.html @@ -5,21 +5,40 @@ color: #333333; background: #F8F8F8; } +img { + width: 64px; + height: 64px; +} table { border: 1px #666666; font-size: small; } +span.lastAccessDate { + font-size: small; +} td { font-family: sans-serif; + padding: 1px; } td.title { font-weight: bold; } h2 { font-family: sans-serif; + display: inline; } -

__Name_Value__

+ + + + + +
Smart Folder Icon   +

__Name_Value__

+
+ __Last_Access_Date__ : __Last_Access_Date_Value__ +
+
+ + + +
@@ -37,4 +56,12 @@

__Name_Value__

__Query_Value__
+ __Raw_Query__ + + __Raw_Query_Value__ +
diff --git a/QLSmartFolder/Shared.h b/QLSmartFolder/Shared.h index 36e6e83..ce5ebc3 100644 --- a/QLSmartFolder/Shared.h +++ b/QLSmartFolder/Shared.h @@ -7,6 +7,5 @@ // #import -#import static NSString * const kPluginBundleId = @"jp.nita.QLSmartFolder";