Fix the Spotlight search panel

It was previously crashing horribly on adding search results. This makes
it actually functional, and renders it using a view-based table instead.

Signed-off-by: Christopher Snowhill <kode54@gmail.com>
xcode15
Christopher Snowhill 2022-07-15 03:02:41 -07:00
parent 838c0d08e8
commit 96acc738e3
5 changed files with 262 additions and 13 deletions

View File

@ -48,7 +48,7 @@ DQ
<rect key="frame" x="1" y="1" width="423" height="226"/>
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
<subviews>
<tableView focusRingType="none" verticalHuggingPriority="750" allowsExpansionToolTips="YES" alternatingRowBackgroundColors="YES" autosaveName="CogSpotlightPlaylist" headerView="25" id="28" customClass="PlaylistView">
<tableView focusRingType="none" verticalHuggingPriority="750" allowsExpansionToolTips="YES" alternatingRowBackgroundColors="YES" autosaveName="CogSpotlightPlaylist" rowSizeStyle="automatic" headerView="25" viewBased="YES" id="28" customClass="PlaylistView">
<rect key="frame" x="0.0" y="0.0" width="447" height="203"/>
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
<size key="intercellSpacing" width="3" height="2"/>
@ -67,6 +67,26 @@ DQ
</textFieldCell>
<sortDescriptor key="sortDescriptorPrototype" selector="caseInsensitiveCompare:" sortKey="title"/>
<tableColumnResizingMask key="resizingMask" resizeWithTable="YES" userResizable="YES"/>
<prototypeCellViews>
<tableCellView id="vIL-tT-nsx">
<rect key="frame" x="1" y="1" width="134" height="17"/>
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
<subviews>
<textField horizontalHuggingPriority="251" verticalHuggingPriority="750" horizontalCompressionResistancePriority="250" fixedFrame="YES" translatesAutoresizingMaskIntoConstraints="NO" id="dow-05-P5d">
<rect key="frame" x="0.0" y="1" width="134" height="16"/>
<autoresizingMask key="autoresizingMask" widthSizable="YES" flexibleMinY="YES" flexibleMaxY="YES"/>
<textFieldCell key="cell" lineBreakMode="truncatingTail" sendsActionOnEndEditing="YES" title="Table View Cell" id="nbX-Qx-ta8">
<font key="font" usesAppearanceFont="YES"/>
<color key="textColor" name="controlTextColor" catalog="System" colorSpace="catalog"/>
<color key="backgroundColor" name="textBackgroundColor" catalog="System" colorSpace="catalog"/>
</textFieldCell>
</textField>
</subviews>
<connections>
<outlet property="textField" destination="dow-05-P5d" id="dKF-cf-DXu"/>
</connections>
</tableCellView>
</prototypeCellViews>
<connections>
<binding destination="16" name="value" keyPath="arrangedObjects.title" id="93">
<dictionary key="options">
@ -88,6 +108,26 @@ DQ
</textFieldCell>
<sortDescriptor key="sortDescriptorPrototype" selector="caseInsensitiveCompare:" sortKey="artist"/>
<tableColumnResizingMask key="resizingMask" resizeWithTable="YES" userResizable="YES"/>
<prototypeCellViews>
<tableCellView id="VrH-Fp-XeF">
<rect key="frame" x="138" y="1" width="124" height="17"/>
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
<subviews>
<textField horizontalHuggingPriority="251" verticalHuggingPriority="750" horizontalCompressionResistancePriority="250" fixedFrame="YES" translatesAutoresizingMaskIntoConstraints="NO" id="f5E-6Y-uFf">
<rect key="frame" x="0.0" y="1" width="124" height="16"/>
<autoresizingMask key="autoresizingMask" widthSizable="YES" flexibleMinY="YES" flexibleMaxY="YES"/>
<textFieldCell key="cell" lineBreakMode="truncatingTail" sendsActionOnEndEditing="YES" title="Table View Cell" id="zeT-Bx-Fer">
<font key="font" usesAppearanceFont="YES"/>
<color key="textColor" name="controlTextColor" catalog="System" colorSpace="catalog"/>
<color key="backgroundColor" name="textBackgroundColor" catalog="System" colorSpace="catalog"/>
</textFieldCell>
</textField>
</subviews>
<connections>
<outlet property="textField" destination="f5E-6Y-uFf" id="V2x-bq-F1X"/>
</connections>
</tableCellView>
</prototypeCellViews>
<connections>
<binding destination="16" name="value" keyPath="arrangedObjects.artist" id="104">
<dictionary key="options">
@ -109,6 +149,26 @@ DQ
</textFieldCell>
<sortDescriptor key="sortDescriptorPrototype" selector="caseInsensitiveCompare:" sortKey="album"/>
<tableColumnResizingMask key="resizingMask" resizeWithTable="YES" userResizable="YES"/>
<prototypeCellViews>
<tableCellView id="sZe-dt-0bF">
<rect key="frame" x="265" y="1" width="127" height="17"/>
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
<subviews>
<textField horizontalHuggingPriority="251" verticalHuggingPriority="750" horizontalCompressionResistancePriority="250" fixedFrame="YES" translatesAutoresizingMaskIntoConstraints="NO" id="wXR-zH-kaq">
<rect key="frame" x="0.0" y="1" width="127" height="16"/>
<autoresizingMask key="autoresizingMask" widthSizable="YES" flexibleMinY="YES" flexibleMaxY="YES"/>
<textFieldCell key="cell" lineBreakMode="truncatingTail" sendsActionOnEndEditing="YES" title="Table View Cell" id="DBh-Jn-CLe">
<font key="font" usesAppearanceFont="YES"/>
<color key="textColor" name="controlTextColor" catalog="System" colorSpace="catalog"/>
<color key="backgroundColor" name="textBackgroundColor" catalog="System" colorSpace="catalog"/>
</textFieldCell>
</textField>
</subviews>
<connections>
<outlet property="textField" destination="wXR-zH-kaq" id="zTm-AX-xhO"/>
</connections>
</tableCellView>
</prototypeCellViews>
<connections>
<binding destination="16" name="value" keyPath="arrangedObjects.album" id="101">
<dictionary key="options">
@ -129,8 +189,28 @@ DQ
<color key="backgroundColor" name="controlBackgroundColor" catalog="System" colorSpace="catalog"/>
</textFieldCell>
<tableColumnResizingMask key="resizingMask" resizeWithTable="YES" userResizable="YES"/>
<prototypeCellViews>
<tableCellView id="w6e-lJ-LQP">
<rect key="frame" x="1" y="1" width="0.0" height="17"/>
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
<subviews>
<textField horizontalHuggingPriority="251" verticalHuggingPriority="750" horizontalCompressionResistancePriority="250" fixedFrame="YES" translatesAutoresizingMaskIntoConstraints="NO" id="kNC-PB-Kja">
<rect key="frame" x="0.0" y="1" width="4" height="16"/>
<autoresizingMask key="autoresizingMask" widthSizable="YES" flexibleMinY="YES" flexibleMaxY="YES"/>
<textFieldCell key="cell" lineBreakMode="truncatingTail" sendsActionOnEndEditing="YES" title="Table View Cell" id="Uvp-HF-CkO">
<font key="font" usesAppearanceFont="YES"/>
<color key="textColor" name="controlTextColor" catalog="System" colorSpace="catalog"/>
<color key="backgroundColor" name="textBackgroundColor" catalog="System" colorSpace="catalog"/>
</textFieldCell>
</textField>
</subviews>
<connections>
<outlet property="textField" destination="kNC-PB-Kja" id="qWQ-5p-Fw9"/>
</connections>
</tableCellView>
</prototypeCellViews>
<connections>
<binding destination="16" name="value" keyPath="arrangedObjects.spotlightLength" id="gWF-nL-fqJ">
<binding destination="16" name="value" keyPath="arrangedObjects.length" id="4eP-Is-O9p">
<dictionary key="options">
<bool key="NSConditionallySetsEditable" value="YES"/>
</dictionary>
@ -149,6 +229,26 @@ DQ
<color key="backgroundColor" name="controlBackgroundColor" catalog="System" colorSpace="catalog"/>
</textFieldCell>
<tableColumnResizingMask key="resizingMask" resizeWithTable="YES" userResizable="YES"/>
<prototypeCellViews>
<tableCellView id="1lW-Vx-WF3">
<rect key="frame" x="1" y="1" width="0.0" height="17"/>
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
<subviews>
<textField horizontalHuggingPriority="251" verticalHuggingPriority="750" horizontalCompressionResistancePriority="250" fixedFrame="YES" translatesAutoresizingMaskIntoConstraints="NO" id="acc-7f-dXh">
<rect key="frame" x="0.0" y="1" width="4" height="16"/>
<autoresizingMask key="autoresizingMask" widthSizable="YES" flexibleMinY="YES" flexibleMaxY="YES"/>
<textFieldCell key="cell" lineBreakMode="truncatingTail" sendsActionOnEndEditing="YES" title="Table View Cell" id="9ZK-JX-P74">
<font key="font" usesAppearanceFont="YES"/>
<color key="textColor" name="controlTextColor" catalog="System" colorSpace="catalog"/>
<color key="backgroundColor" name="textBackgroundColor" catalog="System" colorSpace="catalog"/>
</textFieldCell>
</textField>
</subviews>
<connections>
<outlet property="textField" destination="acc-7f-dXh" id="Hha-9Z-fS4"/>
</connections>
</tableCellView>
</prototypeCellViews>
<connections>
<binding destination="16" name="value" keyPath="arrangedObjects.year" id="94">
<dictionary key="options">
@ -170,6 +270,26 @@ DQ
</textFieldCell>
<sortDescriptor key="sortDescriptorPrototype" selector="caseInsensitiveCompare:" sortKey="genre"/>
<tableColumnResizingMask key="resizingMask" resizeWithTable="YES" userResizable="YES"/>
<prototypeCellViews>
<tableCellView id="HJP-mb-r2M">
<rect key="frame" x="1" y="1" width="0.0" height="17"/>
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
<subviews>
<textField horizontalHuggingPriority="251" verticalHuggingPriority="750" horizontalCompressionResistancePriority="250" fixedFrame="YES" translatesAutoresizingMaskIntoConstraints="NO" id="5n5-AC-puO">
<rect key="frame" x="0.0" y="1" width="4" height="16"/>
<autoresizingMask key="autoresizingMask" widthSizable="YES" flexibleMinY="YES" flexibleMaxY="YES"/>
<textFieldCell key="cell" lineBreakMode="truncatingTail" sendsActionOnEndEditing="YES" title="Table View Cell" id="R66-n6-DSb">
<font key="font" usesAppearanceFont="YES"/>
<color key="textColor" name="controlTextColor" catalog="System" colorSpace="catalog"/>
<color key="backgroundColor" name="textBackgroundColor" catalog="System" colorSpace="catalog"/>
</textFieldCell>
</textField>
</subviews>
<connections>
<outlet property="textField" destination="5n5-AC-puO" id="Wno-mK-MDi"/>
</connections>
</tableCellView>
</prototypeCellViews>
<connections>
<binding destination="16" name="value" keyPath="arrangedObjects.genre" id="102">
<dictionary key="options">
@ -191,6 +311,26 @@ DQ
</textFieldCell>
<sortDescriptor key="sortDescriptorPrototype" selector="compareTrackNumbers:" sortKey="trackText"/>
<tableColumnResizingMask key="resizingMask" resizeWithTable="YES" userResizable="YES"/>
<prototypeCellViews>
<tableCellView id="AdY-0L-7wP">
<rect key="frame" x="395" y="1" width="50" height="17"/>
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
<subviews>
<textField horizontalHuggingPriority="251" verticalHuggingPriority="750" horizontalCompressionResistancePriority="250" fixedFrame="YES" translatesAutoresizingMaskIntoConstraints="NO" id="6ak-Iu-0Dr">
<rect key="frame" x="0.0" y="1" width="50" height="16"/>
<autoresizingMask key="autoresizingMask" widthSizable="YES" flexibleMinY="YES" flexibleMaxY="YES"/>
<textFieldCell key="cell" lineBreakMode="truncatingTail" sendsActionOnEndEditing="YES" title="Table View Cell" id="5fO-3i-lEM">
<font key="font" usesAppearanceFont="YES"/>
<color key="textColor" name="controlTextColor" catalog="System" colorSpace="catalog"/>
<color key="backgroundColor" name="textBackgroundColor" catalog="System" colorSpace="catalog"/>
</textFieldCell>
</textField>
</subviews>
<connections>
<outlet property="textField" destination="6ak-Iu-0Dr" id="Afl-RA-rk1"/>
</connections>
</tableCellView>
</prototypeCellViews>
<connections>
<binding destination="16" name="value" keyPath="arrangedObjects.trackText" id="VFy-Rw-QP2">
<dictionary key="options">
@ -209,6 +349,7 @@ DQ
</dictionary>
</binding>
<outlet property="dataSource" destination="16" id="151"/>
<outlet property="delegate" destination="16" id="LPj-YJ-Alq"/>
<outlet property="menu" destination="171" id="176"/>
<outlet property="playlistController" destination="16" id="184"/>
</connections>

View File

@ -12,6 +12,7 @@
@interface SpotlightPlaylistController : PlaylistController {
}
- (BOOL)tableView:(NSTableView *)tv writeRowsWithIndexes:(NSIndexSet *)rowIndexes toPasteboard:(NSPasteboard *)pboard;
- (BOOL)tableView:(NSTableView *_Nonnull)tv writeRowsWithIndexes:(NSIndexSet *_Nonnull)rowIndexes toPasteboard:(NSPasteboard *_Nonnull)pboard;
- (NSView *_Nullable)tableView:(NSTableView *_Nonnull)tableView viewForTableColumn:(NSTableColumn *_Nullable)tableColumn row:(NSInteger)row;
@end

View File

@ -9,8 +9,16 @@
#import "SpotlightPlaylistController.h"
#import "SpotlightWindowController.h"
#import "PlaylistEntry.h"
static NSArray *cellIdentifiers = nil;
@implementation SpotlightPlaylistController
+ (void)initialize {
cellIdentifiers = @[@"title", @"artist", @"album", @"length", @"year", @"genre", @"track"];
}
// Allow drag and drop from Spotlight into main playlist
- (BOOL)tableView:(NSTableView *)tv
writeRowsWithIndexes:(NSIndexSet *)rowIndexes
@ -39,4 +47,89 @@ writeRowsWithIndexes:(NSIndexSet *)rowIndexes
return NSDragOperationNone;
}
- (NSView *_Nullable)tableView:(NSTableView *)tableView viewForTableColumn:(NSTableColumn *_Nullable)tableColumn row:(NSInteger)row {
NSString *cellText = @"";
NSString *cellIdentifier = @"";
NSTextAlignment cellTextAlignment = NSTextAlignmentLeft;
PlaylistEntry *pe = [[self arrangedObjects] objectAtIndex:row];
if(pe) {
cellIdentifier = [tableColumn identifier];
NSUInteger index = [cellIdentifiers indexOfObject:cellIdentifier];
switch(index) {
case 0:
if([pe title]) cellText = pe.title;
break;
case 1:
if([pe artist]) cellText = pe.artist;
break;
case 2:
if([pe album]) cellText = pe.album;
break;
case 3:
cellText = pe.lengthText;
cellTextAlignment = NSTextAlignmentRight;
break;
case 4:
if([pe year]) cellText = pe.yearText;
cellTextAlignment = NSTextAlignmentRight;
break;
case 5:
if([pe genre]) cellText = pe.genre;
break;
case 6:
if([pe track]) cellText = pe.trackText;
cellTextAlignment = NSTextAlignmentRight;
break;
}
}
NSString *cellTextTruncated = cellText;
if([cellTextTruncated length] > 1023) {
cellTextTruncated = [cellTextTruncated substringToIndex:1023];
cellTextTruncated = [cellTextTruncated stringByAppendingString:@"…"];
}
NSView *view = [tableView makeViewWithIdentifier:cellIdentifier owner:nil];
if(view && [view isKindOfClass:[NSTableCellView class]]) {
NSTableCellView *cellView = (NSTableCellView *)view;
NSRect frameRect = cellView.frame;
frameRect.origin.y = 1;
frameRect.size.height = tableView.rowHeight;
cellView.frame = frameRect;
if(cellView.textField) {
cellView.textField.allowsDefaultTighteningForTruncation = YES;
NSFont *font = [NSFont monospacedDigitSystemFontOfSize:13 weight:NSFontWeightRegular];
cellView.textField.font = font;
cellView.textField.stringValue = cellTextTruncated;
cellView.textField.alignment = cellTextAlignment;
if(cellView.textField.intrinsicContentSize.width > cellView.textField.frame.size.width - 4)
cellView.textField.toolTip = cellTextTruncated;
else
cellView.textField.toolTip = [pe statusMessage];
NSRect cellFrameRect = cellView.textField.frame;
cellFrameRect.origin.y = 1;
cellFrameRect.size.height = frameRect.size.height;
cellView.textField.frame = cellFrameRect;
}
cellView.rowSizeStyle = NSTableViewRowSizeStyleCustom;
}
return view;
}
@end

View File

@ -26,16 +26,12 @@ extern NSPersistentContainer *kPersistentContainer;
NSArray *artistTransform =
@[@"artist", @"AuthorToArtistTransformer"];
// Track numbers must sometimes be converted from NSNumber to NSString
NSArray *trackTransform =
@[@"spotlightTrack", @"NumberToStringTransformer"];
importKeys = @{ @"kMDItemTitle": @"title",
@"kMDItemAlbum": @"album",
@"kMDItemAudioTrackNumber": trackTransform,
@"kMDItemAudioTrackNumber": @"track",
@"kMDItemRecordingYear": @"year",
@"kMDItemMusicalGenre": @"genre",
@"kMDItemDurationSeconds": @"spotlightLength",
@"kMDItemDurationSeconds": @"length",
@"kMDItemPath": URLTransform,
@"kMDItemAuthors": artistTransform };
}
@ -45,13 +41,23 @@ extern NSPersistentContainer *kPersistentContainer;
entry.deLeted = YES;
NSMutableDictionary *dict = [[NSMutableDictionary alloc] init];
// loop through the keys we want to extract
for(NSString *mdKey in importKeys) {
if(![metadataItem valueForAttribute:mdKey]) continue;
id importTarget = [importKeys objectForKey:mdKey];
// Just copy the object from metadata
if([importTarget isKindOfClass:[NSString class]]) {
[entry setValue:[metadataItem valueForAttribute:mdKey]
forKey:importTarget];
if([importTarget isEqualToString:@"length"]) {
// fake it
NSNumber *number = [metadataItem valueForAttribute:mdKey];
[dict setValue:@(44100.0) forKey:@"samplerate"];
[dict setValue:@(44100.0 * [number doubleValue]) forKey:@"totalFrames"];
} else {
[dict setValue:[metadataItem valueForAttribute:mdKey]
forKey:importTarget];
}
}
// Transform the value in metadata before copying it in
else if([importTarget isKindOfClass:[NSArray class]]) {
@ -60,7 +66,7 @@ extern NSPersistentContainer *kPersistentContainer;
[NSValueTransformer valueTransformerForName:[importTarget objectAtIndex:1]];
id transformedValue = [transformer transformedValue:
[metadataItem valueForAttribute:mdKey]];
[entry setValue:transformedValue forKey:importKey];
[dict setValue:transformedValue forKey:importKey];
}
// The importKeys dictionary contains something strange...
else {
@ -69,6 +75,14 @@ extern NSPersistentContainer *kPersistentContainer;
NSAssert(NO, errString);
}
}
NSURL *url = [dict objectForKey:@"url"];
[dict removeObjectForKey:@"url"];
entry.url = url;
[entry setMetadata:dict];
return entry;
}

View File

@ -37,7 +37,7 @@ static NSPredicate *musicOnlyPredicate = nil;
[NSValueTransformer setValueTransformer:authorToArtistTransformer forName:@"AuthorToArtistTransformer"];
NSValueTransformer *pathToURLTransformer = [[PathToURLTransformer alloc] init];
[NSValueTransformer setValueTransformer:pathToURLTransformer forName:@"PathToURLTransformers"];
[NSValueTransformer setValueTransformer:pathToURLTransformer forName:@"PathToURLTransformer"];
NSValueTransformer *stringToSearchScopeTransformer = [[StringToSearchScopeTransformer alloc] init];
[NSValueTransformer setValueTransformer:stringToSearchScopeTransformer forName:@"StringToSearchScopeTransformer"];