Added undo functionality in playlist, undo works for removals, moves and adds.

CQTexperiment
areff 2008-02-10 16:16:45 +00:00
parent 4bd896afb3
commit 9b05ce02d2
13 changed files with 2588 additions and 2301 deletions

View File

@ -236,9 +236,11 @@ increase/decrease as long as the user holds the left/right, plus/minus button */
[remote startListening:self]; [remote startListening:self];
} }
NSUndoManager *undoManager = [playlistController undoManager];
[undoManager disableUndoRegistration];
NSString *filename = @"~/Library/Application Support/Cog/Default.m3u"; NSString *filename = @"~/Library/Application Support/Cog/Default.m3u";
[playlistLoader addURL:[NSURL fileURLWithPath:[filename stringByExpandingTildeInPath]]]; [playlistLoader addURL:[NSURL fileURLWithPath:[filename stringByExpandingTildeInPath]]];
[undoManager enableUndoRegistration];
} }
- (void)applicationWillTerminate:(NSNotification *)aNotification - (void)applicationWillTerminate:(NSNotification *)aNotification

View File

@ -130,6 +130,7 @@
8EFFCD630AA093AF00C458A5 /* FileNode.m in Sources */ = {isa = PBXBuildFile; fileRef = 8EFFCD470AA093AF00C458A5 /* FileNode.m */; }; 8EFFCD630AA093AF00C458A5 /* FileNode.m in Sources */ = {isa = PBXBuildFile; fileRef = 8EFFCD470AA093AF00C458A5 /* FileNode.m */; };
8EFFCD650AA093AF00C458A5 /* FileOutlineView.m in Sources */ = {isa = PBXBuildFile; fileRef = 8EFFCD490AA093AF00C458A5 /* FileOutlineView.m */; }; 8EFFCD650AA093AF00C458A5 /* FileOutlineView.m in Sources */ = {isa = PBXBuildFile; fileRef = 8EFFCD490AA093AF00C458A5 /* FileOutlineView.m */; };
8EFFCD6F0AA093AF00C458A5 /* PathNode.m in Sources */ = {isa = PBXBuildFile; fileRef = 8EFFCD530AA093AF00C458A5 /* PathNode.m */; }; 8EFFCD6F0AA093AF00C458A5 /* PathNode.m in Sources */ = {isa = PBXBuildFile; fileRef = 8EFFCD530AA093AF00C458A5 /* PathNode.m */; };
B01946070D5F5467001A2FB8 /* UndoObject.m in Sources */ = {isa = PBXBuildFile; fileRef = B01946060D5F5467001A2FB8 /* UndoObject.m */; };
/* End PBXBuildFile section */ /* End PBXBuildFile section */
/* Begin PBXContainerItemProxy section */ /* Begin PBXContainerItemProxy section */
@ -632,6 +633,8 @@
8EFFCD490AA093AF00C458A5 /* FileOutlineView.m */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.c.objc; path = FileOutlineView.m; sourceTree = "<group>"; }; 8EFFCD490AA093AF00C458A5 /* FileOutlineView.m */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.c.objc; path = FileOutlineView.m; sourceTree = "<group>"; };
8EFFCD520AA093AF00C458A5 /* PathNode.h */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.c.h; path = PathNode.h; sourceTree = "<group>"; }; 8EFFCD520AA093AF00C458A5 /* PathNode.h */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.c.h; path = PathNode.h; sourceTree = "<group>"; };
8EFFCD530AA093AF00C458A5 /* PathNode.m */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.c.objc; path = PathNode.m; sourceTree = "<group>"; }; 8EFFCD530AA093AF00C458A5 /* PathNode.m */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.c.objc; path = PathNode.m; sourceTree = "<group>"; };
B01946050D5F5467001A2FB8 /* UndoObject.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = UndoObject.h; sourceTree = "<group>"; };
B01946060D5F5467001A2FB8 /* UndoObject.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = UndoObject.m; sourceTree = "<group>"; };
/* End PBXFileReference section */ /* End PBXFileReference section */
/* Begin PBXFrameworksBuildPhase section */ /* Begin PBXFrameworksBuildPhase section */
@ -1116,6 +1119,8 @@
8E75752A09F31D5A0080F1EE /* Playlist */ = { 8E75752A09F31D5A0080F1EE /* Playlist */ = {
isa = PBXGroup; isa = PBXGroup;
children = ( children = (
B01946050D5F5467001A2FB8 /* UndoObject.h */,
B01946060D5F5467001A2FB8 /* UndoObject.m */,
8E1296D80A2BA9CE00443124 /* PlaylistHeaderView.h */, 8E1296D80A2BA9CE00443124 /* PlaylistHeaderView.h */,
8E1296D90A2BA9CE00443124 /* PlaylistHeaderView.m */, 8E1296D90A2BA9CE00443124 /* PlaylistHeaderView.m */,
1755E1F60BA0D2B600CA3560 /* PlaylistLoader.h */, 1755E1F60BA0D2B600CA3560 /* PlaylistLoader.h */,
@ -1610,6 +1615,7 @@
178BAB9B0CD4E1B700B33D47 /* PopupButton.m in Sources */, 178BAB9B0CD4E1B700B33D47 /* PopupButton.m in Sources */,
17BBE5BC0CD95CFA00258F7A /* InvertedToolbarWindow.m in Sources */, 17BBE5BC0CD95CFA00258F7A /* InvertedToolbarWindow.m in Sources */,
569C52E20D5F347800BDBDC9 /* SpotlightWindowController.m in Sources */, 569C52E20D5F347800BDBDC9 /* SpotlightWindowController.m in Sources */,
B01946070D5F5467001A2FB8 /* UndoObject.m in Sources */,
); );
runOnlyForDeploymentPostprocessing = 0; runOnlyForDeploymentPostprocessing = 0;
}; };

File diff suppressed because it is too large Load Diff

Binary file not shown.

0
KnownIssues Normal file
View File

View File

@ -114,7 +114,6 @@ NSString *iTunesDropType = @"CorePasteboardFlavorType 0x6974756E";
} }
} }
- (NSIndexSet *)indexSetFromRows:(NSArray *)rows - (NSIndexSet *)indexSetFromRows:(NSArray *)rows
{ {
NSMutableIndexSet *indexSet = [NSMutableIndexSet indexSet]; NSMutableIndexSet *indexSet = [NSMutableIndexSet indexSet];
@ -127,7 +126,6 @@ NSString *iTunesDropType = @"CorePasteboardFlavorType 0x6974756E";
return indexSet; return indexSet;
} }
- (int)rowsAboveRow:(int)row inIndexSet:(NSIndexSet *)indexSet - (int)rowsAboveRow:(int)row inIndexSet:(NSIndexSet *)indexSet
{ {
unsigned currentIndex = [indexSet firstIndex]; unsigned currentIndex = [indexSet firstIndex];

View File

@ -7,6 +7,8 @@
// //
#import <Cocoa/Cocoa.h> #import <Cocoa/Cocoa.h>
#import <Foundation/NSUndoManager.h>
#import "UndoObject.h"
#import "DNDArrayController.h" #import "DNDArrayController.h"
@class PlaylistLoader; @class PlaylistLoader;
@ -19,6 +21,8 @@
NSMutableArray *shuffleList; NSMutableArray *shuffleList;
NSUndoManager *undoManager;
PlaylistEntry *currentEntry; PlaylistEntry *currentEntry;
BOOL shuffle; BOOL shuffle;
@ -38,6 +42,13 @@
- (void)setRepeat:(BOOL)r; - (void)setRepeat:(BOOL)r;
- (BOOL)repeat; - (BOOL)repeat;
/* Methods for undoing various actions */
- (NSUndoManager *)undoManager;
- (void)undoDelete:(NSMutableArray *)undoEntries;
- (void)undoMove:(NSMutableArray *) undoEntries;
- (void)doUndo:(id)sender;
- (void)doRedo:(id)sender;
- (IBAction)takeShuffleFromObject:(id)sender; - (IBAction)takeShuffleFromObject:(id)sender;
- (IBAction)takeRepeatFromObject:(id)sender; - (IBAction)takeRepeatFromObject:(id)sender;

View File

@ -10,12 +10,14 @@
#import "PlaylistController.h" #import "PlaylistController.h"
#import "PlaylistEntry.h" #import "PlaylistEntry.h"
#import "Shuffle.h" #import "Shuffle.h"
#import "UndoObject.h"
#import "CogAudio/AudioPlayer.h" #import "CogAudio/AudioPlayer.h"
@implementation PlaylistController @implementation PlaylistController
#define SHUFFLE_HISTORY_SIZE 100 #define SHUFFLE_HISTORY_SIZE 100
#define UNDO_STACK_LIMIT 25
- (id)initWithCoder:(NSCoder *)decoder - (id)initWithCoder:(NSCoder *)decoder
{ {
@ -24,6 +26,9 @@
if (self) if (self)
{ {
shuffleList = [[NSMutableArray alloc] init]; shuffleList = [[NSMutableArray alloc] init];
undoManager = [[NSUndoManager alloc] init];
[undoManager setLevelsOfUndo:UNDO_STACK_LIMIT];
} }
return self; return self;
@ -44,19 +49,53 @@
dropOperation:(NSTableViewDropOperation)op dropOperation:(NSTableViewDropOperation)op
{ {
[super tableView:tv acceptDrop:info row:row dropOperation:op]; [super tableView:tv acceptDrop:info row:row dropOperation:op];
UndoObject *undoEntry;
NSMutableArray *undoEntries = [[NSMutableArray alloc] init];
if ([info draggingSource] == tableView) if ([info draggingSource] == tableView)
{ {
//DNDArrayController handles moving...still need to update the indexes //DNDArrayController handles moving...still need to update the indexes
int i;
NSArray *rows = [NSKeyedUnarchiver unarchiveObjectWithData:[[info draggingPasteboard] dataForType: MovedRowsType]]; NSArray *rows = [NSKeyedUnarchiver unarchiveObjectWithData:[[info draggingPasteboard] dataForType: MovedRowsType]];
int firstIndex = [[self indexSetFromRows:rows] firstIndex]; NSIndexSet *indexes = [self indexSetFromRows:rows];
int firstIndex = [indexes firstIndex];
int indexesSize = [indexes count];
NSUInteger indexBuffer[indexesSize];
int i;
int adjustment;
int counter;
if (firstIndex > row) if (firstIndex > row)
i = row; i = row;
else else
i = firstIndex; i = firstIndex;
[indexes getIndexes:indexBuffer maxCount:indexesSize inIndexRange:nil];
if (row > firstIndex)
adjustment = 1;
else
adjustment = 0;
// create an UndoObject for each entry being moved, and store it away
// in the undoEntries array
for (counter = 0; counter < indexesSize; counter++)
{
undoEntry = [[UndoObject alloc] init];
[undoEntry setOrigin: row - adjustment];
[undoEntry setMovedTo: indexBuffer[counter]];
[undoEntries addObject: undoEntry];
}
[undoManager registerUndoWithTarget:self
selector:@selector(undoMove:)
object:undoEntries];
[self updateIndexesFromRow:i]; [self updateIndexesFromRow:i];
return YES; return YES;
@ -159,11 +198,144 @@
} }
} }
-(void)undoDelete:(NSMutableArray *)undoEntries
{
NSEnumerator *enumerator = [undoEntries objectEnumerator];
UndoObject *current;
while (current = [enumerator nextObject])
{
[playlistLoader
insertURLs: [NSArray arrayWithObject:[current path]]
atIndex:[current origin]
sort:YES];
// make sure to dealloc the undo object after reinserting it
[current dealloc];
}
[self updateIndexesFromRow: 0];
}
-(void)undoMove:(NSMutableArray *) undoEntries
{
NSArray *objects = [super arrangedObjects];
NSEnumerator *enumerator = [undoEntries objectEnumerator];
UndoObject *current;
id object;
int len = [undoEntries count];
int iterations = 0;
int playlistLocation;
// register an undo for the undo with the undoManager,
// so it knows what to do if a redo is requested
[undoManager registerUndoWithTarget:self
selector:@selector(undoMove:)
object:undoEntries];
while (current = [enumerator nextObject])
{
/* the exact opposite of an undo is required during a redo, hence
we have to check what we are dealing with and act accordingly */
// originally moved entry up the list
if (([current origin] > [current movedTo]))
{
if ([undoManager isUndoing]) // we are undoing
{
playlistLocation = ([current origin] - (len - 1)) + iterations++;
object = [objects objectAtIndex: playlistLocation];
[object retain];
[super insertObject:object atArrangedObjectIndex:[current movedTo]];
[super removeObjectAtArrangedObjectIndex:playlistLocation + 1];
}
else // we are redoing the undo
{
playlistLocation = [current movedTo] - iterations++;
object = [objects objectAtIndex: playlistLocation];
[object retain];
[super removeObjectAtArrangedObjectIndex:playlistLocation];
[super insertObject:object atArrangedObjectIndex:[current origin]];
}
}
// originally moved entry down the list
else
{
if ([undoManager isUndoing])
{
object = [objects objectAtIndex: [current origin]];
[object retain];
[super insertObject:object atArrangedObjectIndex:[current movedTo] + len--];
[super removeObjectAtArrangedObjectIndex:[current origin]];
}
else
{
object = [objects objectAtIndex: [current movedTo]];
[object retain];
[super removeObjectAtArrangedObjectIndex:[current movedTo]];
[super insertObject:object atArrangedObjectIndex:[current origin] + iterations++];
}
}
[object release];
}
[self updateIndexesFromRow: 0];
}
- (NSUndoManager *)undoManager
{
return undoManager;
}
- (void)doUndo:(id)sender
{
[undoManager undo];
}
- (void)doRedo:(id)sender
{
[undoManager redo];
}
- (void)removeObjectsAtArrangedObjectIndexes:(NSIndexSet *)indexes - (void)removeObjectsAtArrangedObjectIndexes:(NSIndexSet *)indexes
{ {
int i; // loop counter
int indexesSize = [indexes count];
NSUInteger indexBuffer[indexesSize];
UndoObject *undoEntry;
PlaylistEntry *pre;
NSMutableArray *undoEntries = [[NSMutableArray alloc] init];
NSLog(@"Removing indexes: %@", indexes); NSLog(@"Removing indexes: %@", indexes);
NSLog(@"Current index: %i", [[currentEntry index] intValue]); NSLog(@"Current index: %i", [[currentEntry index] intValue]);
// get indexes from IndexSet indexes and put them in indexBuffer
[indexes getIndexes:indexBuffer maxCount:indexesSize inIndexRange:nil];
// loop through the list of indexes, saving each entry away
// in an undo object
for (i = 0; i < indexesSize; i++)
{
// alllocate a new object for each undo entry
undoEntry = [[UndoObject alloc] init];
pre = [self entryAtIndex:indexBuffer[i]];
[undoEntry setOrigin:indexBuffer[i] andPath: [pre url]];
[undoEntries addObject:undoEntry];
}
// register the removals with the undoManager
[undoManager registerUndoWithTarget:self
selector:@selector(undoDelete:)
object:undoEntries];
if ([[currentEntry index] intValue] >= 0 && [indexes containsIndex:[[currentEntry index] intValue]]) if ([[currentEntry index] intValue] >= 0 && [indexes containsIndex:[[currentEntry index] intValue]])
{ {
[currentEntry setIndex:[NSNumber numberWithInt:-[[currentEntry index] intValue] - 1]]; [currentEntry setIndex:[NSNumber numberWithInt:-[[currentEntry index] intValue] - 1]];

View File

@ -23,7 +23,7 @@ typedef enum {
- (void)addURLs:(NSArray *)urls sort:(BOOL)sort; - (void)addURLs:(NSArray *)urls sort:(BOOL)sort;
- (void)addURL:(NSURL *)url; - (void)addURL:(NSURL *)url;
- (void)insertURLs:(NSArray *)urls atIndex:(int)index sort:(BOOL)sort; - (void)insertURLs:(NSArray *)urls atIndex:(int)index sort:(BOOL)sort;
- (void)undoAdd:(NSIndexSet *)undoEntries;
//save playlist, auto-determines type based on extension. Uses m3u if it cannot be determined. //save playlist, auto-determines type based on extension. Uses m3u if it cannot be determined.
- (BOOL)save:(NSString *)filename; - (BOOL)save:(NSString *)filename;
- (BOOL)save:(NSString *)filename asType:(PlaylistType)type; - (BOOL)save:(NSString *)filename asType:(PlaylistType)type;

View File

@ -252,6 +252,7 @@
[validURLs addObject:url]; [validURLs addObject:url];
} }
NSUndoManager *undoManager = [playlistController undoManager];
//Create actual entries //Create actual entries
int i; int i;
@ -272,6 +273,10 @@
NSIndexSet *is = [NSIndexSet indexSetWithIndexesInRange:NSMakeRange(index, [entries count])]; NSIndexSet *is = [NSIndexSet indexSetWithIndexesInRange:NSMakeRange(index, [entries count])];
[undoManager registerUndoWithTarget:self
selector:@selector(undoAdd:)
object:is];
[playlistController insertObjects:entries atArrangedObjectIndexes:is]; [playlistController insertObjects:entries atArrangedObjectIndexes:is];
//Select the first entry in the group that was just added //Select the first entry in the group that was just added
@ -283,6 +288,12 @@
return; return;
} }
-(void)undoAdd:(NSIndexSet *)undoEntries
{
[playlistController removeObjectsAtArrangedObjectIndexes:undoEntries];
}
- (void)readEntriesInfoThread:(NSArray *)entries - (void)readEntriesInfoThread:(NSArray *)entries
{ {
NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init]; NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];

View File

@ -175,7 +175,8 @@
NSString *characters = [e characters]; NSString *characters = [e characters];
unichar c; unichar c;
if ([characters length] != 1) { if ([characters length] != 1)
{
[super keyDown:e]; [super keyDown:e];
return; return;
@ -194,7 +195,9 @@
{ {
[playbackController play:self]; [playbackController play:self];
} }
else if (modifiers == 0 && c == 0x1b) { //Escape // Escape
else if (modifiers == 0 && c == 0x1b)
{
[playlistController clearFilterPredicate:self]; [playlistController clearFilterPredicate:self];
} }
else if (modifiers == NSControlKeyMask && c == 0xf703) // right arrow else if (modifiers == NSControlKeyMask && c == 0xf703) // right arrow

27
Playlist/UndoObject.h Normal file
View File

@ -0,0 +1,27 @@
//
// UndoObject.h
// Cog
//
// Created by Andre Reffhaug on 2/6/08.
// Copyright 2008 __MyCompanyName__. All rights reserved.
//
#import <Cocoa/Cocoa.h>
@interface UndoObject : NSObject {
int origin;
int movedTo;
NSURL *path;
}
-(void)setPath:(NSURL *) p;
-(void)setOrigin:(int) i;
-(void)setMovedTo:(int) i;
-(void)setOrigin:(int) i andPath:(NSURL *)path;
-(int)origin;
-(int)movedTo;
-(NSURL *)path;
@end

53
Playlist/UndoObject.m Normal file
View File

@ -0,0 +1,53 @@
//
// UndoObject.m
// Cog
//
// Created by Andre Reffhaug on 2/6/08.
// Copyright 2008 __MyCompanyName__. All rights reserved.
//
#import "UndoObject.h"
@implementation UndoObject
-(void)setPath:(NSURL *)p
{
[p retain];
[path release];
path = p;
}
-(void)setOrigin:(int)i
{
origin = i;
}
-(void)setMovedTo:(int)i
{
movedTo = i;
}
-(void)setOrigin:(int) i andPath:(NSURL *)p
{
origin = i;
[p retain];
[path release];
path = p;
}
-(int) origin
{
return origin;
}
-(int) movedTo
{
return movedTo;
}
-(NSURL *) path
{
return path;
}
@end