Handle deleting the current track gracefully
Now it should flow playback correctly to the next remaining track after the block of deleted tracks. And if the user deletes the next queued track, it will still be queued to flow past the deleted block. If the user undoes their deletes and restores the tracks, playback will resume after the originally deleted track. Signed-off-by: Christopher Snowhill <kode54@gmail.com>CQTexperiment
parent
c3cd4c34f4
commit
64c4aa2e25
|
@ -49,6 +49,8 @@ typedef NS_ENUM(NSInteger, URLOrigin) {
|
||||||
|
|
||||||
PlaylistEntry *currentEntry;
|
PlaylistEntry *currentEntry;
|
||||||
|
|
||||||
|
PlaylistEntry *nextEntryAfterDeleted;
|
||||||
|
|
||||||
NSUndoManager *undoManager;
|
NSUndoManager *undoManager;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -362,6 +362,31 @@ static inline void dispatch_sync_reentrant(dispatch_queue_t queue, dispatch_bloc
|
||||||
if(currentEntry != nil) [self.tableView scrollRowToVisible:currentEntry.index];
|
if(currentEntry != nil) [self.tableView scrollRowToVisible:currentEntry.index];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
- (void)updateNextAfterDeleted:(PlaylistEntry *)lastEntry withDeleteIndexes:(NSIndexSet *)indexes {
|
||||||
|
__block PlaylistEntry *pe = nil;
|
||||||
|
NSArray *allObjects = [self arrangedObjects];
|
||||||
|
[indexes enumerateRangesUsingBlock:^(NSRange range, BOOL *_Nonnull stop) {
|
||||||
|
if(range.location <= lastEntry.index &&
|
||||||
|
range.location + range.length > lastEntry.index) {
|
||||||
|
NSUInteger index = range.location + range.length;
|
||||||
|
if(index < [allObjects count])
|
||||||
|
pe = [allObjects objectAtIndex:index];
|
||||||
|
else
|
||||||
|
pe = nil;
|
||||||
|
} else if(pe && range.location <= [pe index] &&
|
||||||
|
range.location + range.length > [pe index]) {
|
||||||
|
NSUInteger index = range.location + range.length;
|
||||||
|
if(index < [allObjects count])
|
||||||
|
pe = [allObjects objectAtIndex:index];
|
||||||
|
else
|
||||||
|
pe = nil;
|
||||||
|
} else if(pe && range.location > [pe index]) {
|
||||||
|
*stop = YES;
|
||||||
|
}
|
||||||
|
}];
|
||||||
|
nextEntryAfterDeleted = pe;
|
||||||
|
}
|
||||||
|
|
||||||
- (void)tableView:(NSTableView *)tableView didClickTableColumn:(NSTableColumn *)tableColumn {
|
- (void)tableView:(NSTableView *)tableView didClickTableColumn:(NSTableColumn *)tableColumn {
|
||||||
if([self shuffle] != ShuffleOff) [self resetShuffleList];
|
if([self shuffle] != ShuffleOff) [self resetShuffleList];
|
||||||
}
|
}
|
||||||
|
@ -577,6 +602,10 @@ static inline void dispatch_sync_reentrant(dispatch_queue_t queue, dispatch_bloc
|
||||||
[NSString stringWithFormat:@"Adding %lu entries", (unsigned long)[objects count]];
|
[NSString stringWithFormat:@"Adding %lu entries", (unsigned long)[objects count]];
|
||||||
[[self undoManager] setActionName:actionName];
|
[[self undoManager] setActionName:actionName];
|
||||||
|
|
||||||
|
for(PlaylistEntry *pe in objects) {
|
||||||
|
pe.deleted = NO;
|
||||||
|
}
|
||||||
|
|
||||||
[[SQLiteStore sharedStore] playlistInsertTracks:objects
|
[[SQLiteStore sharedStore] playlistInsertTracks:objects
|
||||||
atObjectIndexes:indexes
|
atObjectIndexes:indexes
|
||||||
progressCall:^(double progress) {
|
progressCall:^(double progress) {
|
||||||
|
@ -607,11 +636,14 @@ static inline void dispatch_sync_reentrant(dispatch_queue_t queue, dispatch_bloc
|
||||||
NSMutableIndexSet *unarrangedIndexes = [[NSMutableIndexSet alloc] init];
|
NSMutableIndexSet *unarrangedIndexes = [[NSMutableIndexSet alloc] init];
|
||||||
for(PlaylistEntry *pe in objects) {
|
for(PlaylistEntry *pe in objects) {
|
||||||
[unarrangedIndexes addIndex:[pe index]];
|
[unarrangedIndexes addIndex:[pe index]];
|
||||||
|
pe.deleted = YES;
|
||||||
}
|
}
|
||||||
|
|
||||||
if([indexes containsIndex:currentEntry.index]) {
|
if([indexes containsIndex:currentEntry.index]) {
|
||||||
// Safety check. The player doesn't like committing actions on a removed track
|
[self updateNextAfterDeleted:currentEntry withDeleteIndexes:indexes];
|
||||||
[playbackController stop:nil];
|
} else if(nextEntryAfterDeleted &&
|
||||||
|
[indexes containsIndex:nextEntryAfterDeleted.index]) {
|
||||||
|
[self updateNextAfterDeleted:nextEntryAfterDeleted withDeleteIndexes:indexes];
|
||||||
}
|
}
|
||||||
|
|
||||||
if(currentEntry.index >= 0 && [unarrangedIndexes containsIndex:currentEntry.index]) {
|
if(currentEntry.index >= 0 && [unarrangedIndexes containsIndex:currentEntry.index]) {
|
||||||
|
@ -828,9 +860,14 @@ static inline void dispatch_sync_reentrant(dispatch_queue_t queue, dispatch_bloc
|
||||||
return [self shuffledEntryAtIndex:(pe.shuffleIndex + 1)];
|
return [self shuffledEntryAtIndex:(pe.shuffleIndex + 1)];
|
||||||
} else {
|
} else {
|
||||||
NSInteger i;
|
NSInteger i;
|
||||||
if(pe.index < 0) // Was a current entry, now removed.
|
|
||||||
|
if(pe.deleted) // Was a current entry, now removed.
|
||||||
{
|
{
|
||||||
i = -pe.index - 1;
|
if(nextEntryAfterDeleted)
|
||||||
|
i = nextEntryAfterDeleted.index;
|
||||||
|
else
|
||||||
|
i = 0;
|
||||||
|
nextEntryAfterDeleted = nil;
|
||||||
} else {
|
} else {
|
||||||
i = pe.index + 1;
|
i = pe.index + 1;
|
||||||
}
|
}
|
||||||
|
@ -1038,7 +1075,7 @@ static inline void dispatch_sync_reentrant(dispatch_queue_t queue, dispatch_bloc
|
||||||
|
|
||||||
NSMutableIndexSet *refreshSet = [[NSMutableIndexSet alloc] init];
|
NSMutableIndexSet *refreshSet = [[NSMutableIndexSet alloc] init];
|
||||||
|
|
||||||
if(currentEntry != nil) [refreshSet addIndex:currentEntry.index];
|
if(currentEntry != nil && !currentEntry.deleted) [refreshSet addIndex:currentEntry.index];
|
||||||
if(pe != nil) [refreshSet addIndex:pe.index];
|
if(pe != nil) [refreshSet addIndex:pe.index];
|
||||||
|
|
||||||
// Refresh entire row to refresh tooltips
|
// Refresh entire row to refresh tooltips
|
||||||
|
|
|
@ -66,6 +66,8 @@
|
||||||
BOOL seekable;
|
BOOL seekable;
|
||||||
|
|
||||||
BOOL metadataLoaded;
|
BOOL metadataLoaded;
|
||||||
|
|
||||||
|
BOOL deleted;
|
||||||
}
|
}
|
||||||
|
|
||||||
+ (NSSet *)keyPathsForValuesAffectingDisplay;
|
+ (NSSet *)keyPathsForValuesAffectingDisplay;
|
||||||
|
@ -167,6 +169,8 @@
|
||||||
|
|
||||||
@property BOOL metadataLoaded;
|
@property BOOL metadataLoaded;
|
||||||
|
|
||||||
|
@property BOOL deleted;
|
||||||
|
|
||||||
- (void)setMetadata:(NSDictionary *)metadata;
|
- (void)setMetadata:(NSDictionary *)metadata;
|
||||||
|
|
||||||
@end
|
@end
|
||||||
|
|
|
@ -66,6 +66,8 @@
|
||||||
|
|
||||||
@synthesize metadataLoaded;
|
@synthesize metadataLoaded;
|
||||||
|
|
||||||
|
@synthesize deleted;
|
||||||
|
|
||||||
// The following read-only keys depend on the values of other properties
|
// The following read-only keys depend on the values of other properties
|
||||||
|
|
||||||
+ (NSSet *)keyPathsForValuesAffectingDisplay {
|
+ (NSSet *)keyPathsForValuesAffectingDisplay {
|
||||||
|
@ -139,6 +141,7 @@
|
||||||
self.replayGainTrackGain = 0;
|
self.replayGainTrackGain = 0;
|
||||||
self.replayGainTrackPeak = 0;
|
self.replayGainTrackPeak = 0;
|
||||||
self.volume = 1;
|
self.volume = 1;
|
||||||
|
self.deleted = NO;
|
||||||
}
|
}
|
||||||
return self;
|
return self;
|
||||||
}
|
}
|
||||||
|
@ -507,6 +510,8 @@
|
||||||
pe->seekable = seekable;
|
pe->seekable = seekable;
|
||||||
|
|
||||||
pe->metadataLoaded = metadataLoaded;
|
pe->metadataLoaded = metadataLoaded;
|
||||||
|
|
||||||
|
pe->deleted = deleted;
|
||||||
}
|
}
|
||||||
|
|
||||||
return pe;
|
return pe;
|
||||||
|
|
Loading…
Reference in New Issue