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

@ -1,21 +1,23 @@
// //
// PlaylistController.m // PlaylistController.m
// Cog // Cog
// //
// Created by Vincent Spader on 3/18/05. // Created by Vincent Spader on 3/18/05.
// Copyright 2005 Vincent Spader All rights reserved. // Copyright 2005 Vincent Spader All rights reserved.
// //
#import "PlaylistLoader.h" #import "PlaylistLoader.h"
#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]]; NSIndexSet *indexes = [self indexSetFromRows:rows];
int firstIndex = [[self indexSetFromRows:rows] firstIndex]; 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

@ -133,8 +133,8 @@
{ {
//Find which row is under the cursor //Find which row is under the cursor
[[self window] makeFirstResponder:self]; [[self window] makeFirstResponder:self];
NSPoint menuPoint = [self convertPoint:[event locationInWindow] fromView:nil]; NSPoint menuPoint = [self convertPoint:[event locationInWindow] fromView:nil];
int row = [self rowAtPoint:menuPoint]; int row = [self rowAtPoint:menuPoint];
/* Update the table selection before showing menu /* Update the table selection before showing menu
Preserves the selection if the row under the mouse is selected (to allow for Preserves the selection if the row under the mouse is selected (to allow for
@ -171,11 +171,12 @@
- (void)keyDown:(NSEvent *)e - (void)keyDown:(NSEvent *)e
{ {
unsigned int modifiers = [e modifierFlags] & (NSCommandKeyMask | NSShiftKeyMask | NSControlKeyMask | NSAlternateKeyMask); unsigned int modifiers = [e modifierFlags] & (NSCommandKeyMask | NSShiftKeyMask | NSControlKeyMask | NSAlternateKeyMask);
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