From 631e8a2c23f333390c3982f9fa6f2339ecd85a27 Mon Sep 17 00:00:00 2001 From: Christopher Snowhill Date: Thu, 27 Feb 2025 00:58:18 -0800 Subject: [PATCH] Feature: Add fractional track length tooltips Add fractional track length tooltips, for extra verbose info goodness. Signed-off-by: Christopher Snowhill --- Base.lproj/InfoInspector.xib | 5 ++- Formatters/SecondsFormatter.h | 5 +++ Formatters/SecondsFormatter.m | 80 +++++++++++++++++++++++++++++++++++ Playlist/PlaylistController.m | 6 +++ Playlist/PlaylistEntry.h | 2 + Playlist/PlaylistEntry.m | 11 +++++ 6 files changed, 107 insertions(+), 2 deletions(-) diff --git a/Base.lproj/InfoInspector.xib b/Base.lproj/InfoInspector.xib index faeed21c0..ac64cade0 100644 --- a/Base.lproj/InfoInspector.xib +++ b/Base.lproj/InfoInspector.xib @@ -1,8 +1,8 @@ - + - + @@ -167,6 +167,7 @@ + diff --git a/Formatters/SecondsFormatter.h b/Formatters/SecondsFormatter.h index 0803c1e33..cfb90f8de 100644 --- a/Formatters/SecondsFormatter.h +++ b/Formatters/SecondsFormatter.h @@ -24,3 +24,8 @@ } @end + +@interface SecondsFractionFormatter : NSFormatter { +} + +@end diff --git a/Formatters/SecondsFormatter.m b/Formatters/SecondsFormatter.m index 293fc78db..891f87120 100644 --- a/Formatters/SecondsFormatter.m +++ b/Formatters/SecondsFormatter.m @@ -99,3 +99,83 @@ } @end + +@implementation SecondsFractionFormatter + +- (NSString *)stringForObjectValue:(id)object { + NSString *result = nil; + double value; + unsigned days = 0; + unsigned hours = 0; + unsigned minutes = 0; + float seconds = 0.0; + + if(nil == object || NO == [object isKindOfClass:[NSNumber class]] || isnan([object doubleValue])) { + return @""; + } + + value = [object doubleValue]; + + seconds = fmod(value, 60.0); + minutes = (unsigned)floor(value / 60.0); + + while(60 <= minutes) { + minutes -= 60; + ++hours; + } + + while(24 <= hours) { + hours -= 24; + ++days; + } + + if(0 < days) { + result = [NSString stringWithFormat:@"%u:%.2u:%.2u:%06.3f", days, hours, minutes, seconds]; + } else if(0 < hours) { + result = [NSString stringWithFormat:@"%u:%.2u:%06.3f", hours, minutes, seconds]; + } else if(0 < minutes) { + result = [NSString stringWithFormat:@"%u:%06.3f", minutes, seconds]; + } else { + result = [NSString stringWithFormat:@"0:%06.3f", seconds]; + } + + return result; +} + +- (BOOL)getObjectValue:(id *)object forString:(NSString *)string errorDescription:(NSString **)error { + NSScanner *scanner = nil; + BOOL result = NO; + double value = 0.0; + double seconds = 0.0; + + scanner = [NSScanner scannerWithString:string]; + + while(NO == [scanner isAtEnd]) { + // Grab a value + if([scanner scanDouble:&value]) { + seconds *= 60.0; + seconds += value; + result = YES; + } + + // Grab the separator, if present + [scanner scanString:@":" intoString:NULL]; + } + + if(result && NULL != object) { + *object = @(seconds); + } else if(NULL != error) { + *error = @"Couldn't convert value to seconds"; + } + + return result; +} + +- (NSAttributedString *)attributedStringForObjectValue:(id)object withDefaultAttributes:(NSDictionary *)attributes { + NSAttributedString *result = nil; + + result = [[NSAttributedString alloc] initWithString:[self stringForObjectValue:object] attributes:attributes]; + return result; +} + +@end diff --git a/Playlist/PlaylistController.m b/Playlist/PlaylistController.m index 8324a8c54..ce909e0e1 100644 --- a/Playlist/PlaylistController.m +++ b/Playlist/PlaylistController.m @@ -445,6 +445,7 @@ static void *playlistControllerContext = &playlistControllerContext; NSImage *cellImage = nil; NSString *cellText = @""; NSString *cellIdentifier = @""; + NSString *cellToolTip = nil; NSTextAlignment cellTextAlignment = NSTextAlignmentLeft; PlaylistEntry *pe = [[self arrangedObjects] objectAtIndex:row]; @@ -486,6 +487,7 @@ static void *playlistControllerContext = &playlistControllerContext; case 6: cellText = pe.lengthText; cellTextAlignment = NSTextAlignmentRight; + cellToolTip = pe.lengthInfo; break; case 7: @@ -583,6 +585,10 @@ static void *playlistControllerContext = &playlistControllerContext; else cellView.textField.toolTip = [pe statusMessage]; + if(cellToolTip) { + cellView.textField.toolTip = cellToolTip; + } + NSRect cellFrameRect = cellView.textField.frame; cellFrameRect.origin.y = 1; cellFrameRect.size.height = frameRect.size.height; diff --git a/Playlist/PlaylistEntry.h b/Playlist/PlaylistEntry.h index dc14a4389..6be1cc657 100644 --- a/Playlist/PlaylistEntry.h +++ b/Playlist/PlaylistEntry.h @@ -25,6 +25,7 @@ + (NSSet *_Nonnull)keyPathsForValuesAffectingAlbumArt; + (NSSet *_Nonnull)keyPathsForValuesAffectingTrackText; + (NSSet *_Nonnull)keyPathsForValuesAffectingLengthText; ++ (NSSet *_Nonnull)keyPathsForValuesAffectingLengthInfo; + (NSSet *_Nonnull)keyPathsForValuesAffectingYearText; + (NSSet *_Nonnull)keyPathsForValuesAffectingCuesheetPresent; + (NSSet *_Nonnull)keyPathsForValuesAffectingGainCorrection; @@ -42,6 +43,7 @@ @property(nonatomic, readonly) NSString *_Nonnull positionText; @property(nonatomic, readonly) NSString *_Nonnull lengthText; +@property(nonatomic, readonly) NSString *_Nonnull lengthInfo; @property(nonatomic, readonly) NSString *_Nonnull yearText; diff --git a/Playlist/PlaylistEntry.m b/Playlist/PlaylistEntry.m index 9a8042f90..8d59b20dd 100644 --- a/Playlist/PlaylistEntry.m +++ b/Playlist/PlaylistEntry.m @@ -91,6 +91,10 @@ extern NSMutableDictionary *kArtworkDictionary; return [NSSet setWithObject:@"length"]; } ++ (NSSet *)keyPathsForValuesAffectingLengthInfo { + return [NSSet setWithObject:@"length"]; +} + + (NSSet *)keyPathsForValuesAffectingAlbumArt { return [NSSet setWithObjects:@"albumArtInternal", @"artId", nil]; } @@ -316,6 +320,13 @@ extern NSMutableDictionary *kArtworkDictionary; return time; } +@dynamic lengthInfo; +- (NSString *)lengthInfo { + SecondsFractionFormatter * secondsFormatter = [[SecondsFractionFormatter alloc] init]; + NSString *time = [secondsFormatter stringForObjectValue:self.length]; + return time; +} + @dynamic albumArt; - (NSImage *)albumArt { if(!self.albumArtInternal || ![self.albumArtInternal length]) return nil;