diff --git a/Base.lproj/MainMenu.xib b/Base.lproj/MainMenu.xib index 79a499f1d..38a3313ba 100644 --- a/Base.lproj/MainMenu.xib +++ b/Base.lproj/MainMenu.xib @@ -25,13 +25,13 @@ - + - + - + @@ -127,8 +127,8 @@ - - + + @@ -141,11 +141,11 @@ - + - + @@ -171,6 +171,50 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + - + @@ -401,11 +445,11 @@ - + - + @@ -441,7 +485,7 @@ - + @@ -2140,13 +2184,8 @@ Gw - + - - - - - NSIsNotNil @@ -2166,12 +2205,8 @@ Gw - + - - - - NSIsNotNil @@ -2182,12 +2217,8 @@ Gw - + - - - - NSIsNotNil @@ -2197,12 +2228,8 @@ Gw - + - - - - NSIsNotNil @@ -2216,12 +2243,8 @@ Gw - + - - - - NSIsNotNil @@ -2230,13 +2253,19 @@ Gw - - + + + NSIsNotNil + + + + + + + + + - - - - NSIsNotNil @@ -2245,12 +2274,8 @@ Gw - + - - - - NSIsNotNil @@ -2260,12 +2285,8 @@ Gw - + - - - - NSIsNotNil @@ -2275,12 +2296,8 @@ Gw - + - - - - NSIsNotNil @@ -2291,12 +2308,8 @@ Gw - + - - - - NSIsNotNil diff --git a/DataModel.xcdatamodeld/DataModel.xcdatamodel/contents b/DataModel.xcdatamodeld/DataModel.xcdatamodel/contents index 5d003293b..201a5da34 100644 --- a/DataModel.xcdatamodeld/DataModel.xcdatamodel/contents +++ b/DataModel.xcdatamodeld/DataModel.xcdatamodel/contents @@ -1,5 +1,5 @@ - + @@ -11,6 +11,7 @@ + @@ -67,7 +68,7 @@ - + diff --git a/Playlist/PlaylistController.h b/Playlist/PlaylistController.h index f745406ad..a76806d83 100644 --- a/Playlist/PlaylistController.h +++ b/Playlist/PlaylistController.h @@ -143,6 +143,7 @@ typedef NS_ENUM(NSInteger, URLOrigin) { // Reset playcount of selection - (IBAction)resetPlaycounts:(id _Nullable)sender; +- (IBAction)removeRatings:(id _Nullable)sender; // Play statistics - (void)updatePlayCountForTrack:(PlaylistEntry *_Nonnull)pe; @@ -153,4 +154,6 @@ typedef NS_ENUM(NSInteger, URLOrigin) { - (void)insertObjectsUnsynced:(NSArray *_Nullable)objects atArrangedObjectIndexes:(NSIndexSet *_Nullable)indexes; +- (void)tableView:(NSTableView *)view didClickRow:(NSInteger)clickedRow column:(NSInteger)clickedColumn atPoint:(NSPoint)cellPoint; + @end diff --git a/Playlist/PlaylistController.m b/Playlist/PlaylistController.m index 56e20dda2..f41efa546 100644 --- a/Playlist/PlaylistController.m +++ b/Playlist/PlaylistController.m @@ -47,7 +47,7 @@ static void *playlistControllerContext = &playlistControllerContext; + (void)initialize { cellIdentifiers = @[@"index", @"status", @"title", @"albumartist", @"artist", @"album", @"length", @"year", @"genre", @"track", @"path", - @"filename", @"codec"]; + @"filename", @"codec", @"rating"]; NSValueTransformer *repeatNoneTransformer = [[RepeatModeTransformer alloc] initWithMode:RepeatModeNoRepeat]; @@ -282,6 +282,26 @@ static void *playlistControllerContext = &playlistControllerContext; } } +- (void)ratingUpdatedWithEntry:(PlaylistEntry *)pe rating:(CGFloat)rating { + if(pe && !pe.deLeted) { + PlayCount *pc = pe.playCountItem; + + if(!pc) { + pc = [NSEntityDescription insertNewObjectForEntityForName:@"PlayCount" inManagedObjectContext:self.persistentContainer.viewContext]; + pc.count = 0; + pc.firstSeen = [NSDate date]; + pc.album = pe.album; + pc.artist = pe.artist; + pc.title = pe.title; + pc.filename = pe.filenameFragment; + } + + pc.rating = rating; + + [self commitPersistentStore]; + } +} + - (void)resetPlayCountForTrack:(PlaylistEntry *)pe { PlayCount *pc = pe.playCountItem; @@ -290,6 +310,14 @@ static void *playlistControllerContext = &playlistControllerContext; } } +- (void)removeRatingForTrack:(PlaylistEntry *)pe { + PlayCount *pc = pe.playCountItem; + + if(pc) { + pc.rating = 0; + } +} + - (void)updatePlaylistIndexes { NSArray *arranged = [self arrangedObjects]; NSUInteger n = [arranged count]; @@ -379,6 +407,8 @@ static void *playlistControllerContext = &playlistControllerContext; PlaylistEntry *pe = [[self arrangedObjects] objectAtIndex:row]; float fontSize = [[[NSUserDefaultsController sharedUserDefaultsController] defaults] floatForKey:@"fontSize"]; + + BOOL cellRating = NO; if(pe) { cellIdentifier = [tableColumn identifier]; @@ -440,6 +470,21 @@ static void *playlistControllerContext = &playlistControllerContext; case 12: if([pe codec]) cellText = pe.codec; break; + + case 13: + { + NSString *filledStar = @"★"; + NSString *emptyStar = @"☆"; + NSUInteger rating = (NSUInteger)ceil(pe.rating); + if(rating < 0) + rating = 0; + else if(rating > 5) + rating = 5; + cellText = [@"" stringByPaddingToLength:rating withString:filledStar startingAtIndex:0]; + cellText = [cellText stringByPaddingToLength:5 withString:emptyStar startingAtIndex:0]; + cellRating = YES; + break; + } } } @@ -486,6 +531,26 @@ static void *playlistControllerContext = &playlistControllerContext; return view; } +- (void)tableView:(NSTableView *)view didClickRow:(NSInteger)clickedRow column:(NSInteger)clickedColumn atPoint:(NSPoint)cellPoint { + NSTableColumn *column = [view tableColumns][clickedColumn]; + NSString *cellIdentifier = [column identifier]; + NSUInteger index = [cellIdentifiers indexOfObject:cellIdentifier]; + if(index == 13) { + NSInteger rating = ((CGFloat)ceil(cellPoint.x * 5.0 / 64.0)); + if(rating < 1) rating = 1; + else if(rating > 5) rating = 5; + + PlaylistEntry *pe = [[self arrangedObjects] objectAtIndex:clickedRow]; + + [self ratingUpdatedWithEntry:pe rating:rating]; + + NSIndexSet *refreshRow = [NSIndexSet indexSetWithIndex:clickedRow]; + NSIndexSet *refreshColumn = [NSIndexSet indexSetWithIndex:clickedColumn]; + + [self.tableView reloadDataForRowIndexes:refreshRow columnIndexes:refreshColumn]; + } +} + - (void)updateRowSize { [self.tableView reloadData]; if(currentEntry != nil) [self.tableView scrollRowToVisible:currentEntry.index]; @@ -1737,4 +1802,26 @@ static void *playlistControllerContext = &playlistControllerContext; } } +- (IBAction)removeRatings:(id)sender { + NSArray *selectedobjects = [self selectedObjects]; + if([selectedobjects count]) { + for(PlaylistEntry *pe in selectedobjects) { + [self removeRatingForTrack:pe]; + } + [self commitPersistentStore]; + + NSMutableIndexSet *refreshSet = [[NSMutableIndexSet alloc] init]; + + for(PlaylistEntry *pe in selectedobjects) { + if(pe.index >= 0 && pe.index < NSNotFound) { + [refreshSet addIndex:pe.index]; + } + } + + // Refresh entire row to refresh tooltips + unsigned long columns = [[self.tableView tableColumns] count]; + [self.tableView reloadDataForRowIndexes:refreshSet columnIndexes:[NSIndexSet indexSetWithIndexesInRange:NSMakeRange(0, columns)]]; + } +} + @end diff --git a/Playlist/PlaylistEntry.h b/Playlist/PlaylistEntry.h index 4f10a07b6..02e210fc7 100644 --- a/Playlist/PlaylistEntry.h +++ b/Playlist/PlaylistEntry.h @@ -72,6 +72,8 @@ @property(nonatomic, readonly) NSString *_Nonnull playCount; @property(nonatomic, readonly) NSString *_Nonnull playCountInfo; +@property(nonatomic, readonly) float rating; + - (void)setMetadata:(NSDictionary *_Nonnull)metadata; @end diff --git a/Playlist/PlaylistEntry.m b/Playlist/PlaylistEntry.m index d83305865..c82a01f98 100644 --- a/Playlist/PlaylistEntry.m +++ b/Playlist/PlaylistEntry.m @@ -560,4 +560,14 @@ NSURL *_Nullable urlForPath(NSString *_Nullable path) { return @""; } +@dynamic rating; +- (float)rating { + PlayCount *pc = self.playCountItem; + if(pc) { + return pc.rating; + } else { + return 0; + } +} + @end diff --git a/Playlist/PlaylistView.m b/Playlist/PlaylistView.m index 010c0f908..e964a49be 100644 --- a/Playlist/PlaylistView.m +++ b/Playlist/PlaylistView.m @@ -111,15 +111,28 @@ } - (BOOL)acceptsFirstMouse:(NSEvent *)mouseDownEvent { - return NO; + return YES; } - (void)mouseDown:(NSEvent *)e { [super mouseDown:e]; - if([e type] == NSEventTypeLeftMouseDown && [e clickCount] == 2 && - [[self selectedRowIndexes] count] == 1) { - [playbackController play:self]; + if([e type] == NSEventTypeLeftMouseDown) { + if([e clickCount] == 1) { + NSPoint globalLocation = [e locationInWindow]; + NSPoint localLocation = [self convertPoint:globalLocation fromView:nil]; + NSInteger clickedRow = [self rowAtPoint:localLocation]; + NSInteger clickedColumn = [self columnAtPoint:localLocation]; + + if(clickedRow != -1 && clickedColumn != -1) { + NSView *cellView = [self viewAtColumn:clickedColumn row:clickedRow makeIfNecessary:YES]; + NSPoint cellPoint = [cellView convertPoint:localLocation fromView:self]; + + [playlistController tableView:self didClickRow:clickedRow column:clickedColumn atPoint:cellPoint]; + } + } else if([e clickCount] == 2 && [[self selectedRowIndexes] count] == 1) { + [playbackController play:self]; + } } } diff --git a/en.lproj/MainMenu.strings b/en.lproj/MainMenu.strings index c664fcab7..6b6920097 100644 --- a/en.lproj/MainMenu.strings +++ b/en.lproj/MainMenu.strings @@ -101,8 +101,8 @@ /* Class = "NSMenuItem"; title = "Zoom"; ObjectID = "197"; */ "197.title" = "Zoom"; -/* Class = "NSTableColumn"; headerCell.title = "Title"; ObjectID = "208"; */ -"208.headerCell.title" = "Title"; +/* Class = "NSTableColumn"; headerCell.title = "Rating"; ObjectID = "208"; */ +"208.headerCell.title" = "Rating"; /* Class = "NSTableColumn"; headerCell.title = "#"; ObjectID = "209"; */ "209.headerCell.title" = "#"; diff --git a/es.lproj/MainMenu.strings b/es.lproj/MainMenu.strings index c664fcab7..6b6920097 100644 --- a/es.lproj/MainMenu.strings +++ b/es.lproj/MainMenu.strings @@ -101,8 +101,8 @@ /* Class = "NSMenuItem"; title = "Zoom"; ObjectID = "197"; */ "197.title" = "Zoom"; -/* Class = "NSTableColumn"; headerCell.title = "Title"; ObjectID = "208"; */ -"208.headerCell.title" = "Title"; +/* Class = "NSTableColumn"; headerCell.title = "Rating"; ObjectID = "208"; */ +"208.headerCell.title" = "Rating"; /* Class = "NSTableColumn"; headerCell.title = "#"; ObjectID = "209"; */ "209.headerCell.title" = "#";