Revision: 4353
http://skim-app.svn.sourceforge.net/skim-app/?rev=4353&view=rev
Author: hofman
Date: 2008-08-05 21:25:18 +0000 (Tue, 05 Aug 2008)
Log Message:
-----------
Implement undo for bookmarks using KVO.
Modified Paths:
--------------
trunk/SKBookmark.h
trunk/SKBookmark.m
trunk/SKBookmarkController.h
trunk/SKBookmarkController.m
Modified: trunk/SKBookmark.h
===================================================================
--- trunk/SKBookmark.h 2008-08-04 21:19:17 UTC (rev 4352)
+++ trunk/SKBookmark.h 2008-08-05 21:25:18 UTC (rev 4353)
@@ -38,9 +38,6 @@
#import <Cocoa/Cocoa.h>
-extern NSString *SKBookmarkChangedNotification;
-extern NSString *SKBookmarkWillBeRemovedNotification;
-
enum {
SKBookmarkTypeBookmark,
SKBookmarkTypeFolder,
@@ -49,7 +46,6 @@
@interface SKBookmark : NSObject <NSCopying> {
SKBookmark *parent;
- NSUndoManager *undoManager;
}
- (id)initWithPath:(NSString *)aPath pageIndex:(unsigned)aPageIndex
label:(NSString *)aLabel;
@@ -72,16 +68,14 @@
- (NSNumber *)pageNumber;
- (NSArray *)children;
-- (void)insertChild:(SKBookmark *)child atIndex:(unsigned int)index;
-- (void)addChild:(SKBookmark *)child;
-- (void)removeChild:(SKBookmark *)child;
+- (unsigned int)countOfChildren;
+- (SKBookmark *)objectInChildrenAtIndex:(unsigned int)anIndex;
+- (void)insertObject:(SKBookmark *)child inChildrenAtIndex:(unsigned
int)anIndex;
+- (void)removeObjectFromChildrenAtIndex:(unsigned int)anIndex;
- (SKBookmark *)parent;
- (void)setParent:(SKBookmark *)newParent;
-- (NSUndoManager *)undoManager;
-- (void)setUndoManager:(NSUndoManager *)newUndoManager;
-
- (BOOL)isDescendantOf:(SKBookmark *)bookmark;
- (BOOL)isDescendantOfArray:(NSArray *)bookmarks;
Modified: trunk/SKBookmark.m
===================================================================
--- trunk/SKBookmark.m 2008-08-04 21:19:17 UTC (rev 4352)
+++ trunk/SKBookmark.m 2008-08-05 21:25:18 UTC (rev 4353)
@@ -41,9 +41,6 @@
#import "NSImage_SKExtensions.h"
#import "NSObject_SKExtensions.h"
-NSString *SKBookmarkChangedNotification = @"SKBookmarkChangedNotification";
-NSString *SKBookmarkWillBeRemovedNotification =
@"SKBookmarkWillBeRemovedNotification";
-
static NSString *SKBookmarkTypeBookmarkString = @"bookmark";
static NSString *SKBookmarkTypeFolderString = @"folder";
static NSString *SKBookmarkTypeSeparatorString = @"separator";
@@ -143,7 +140,6 @@
}
- (void)dealloc {
- [undoManager release];
if (self != defaultPlaceholderBookmark)
[super dealloc];
}
@@ -162,9 +158,10 @@
- (NSNumber *)pageNumber { return nil; }
- (NSArray *)children { return nil; }
-- (void)insertChild:(SKBookmark *)child atIndex:(unsigned int)anIndex {}
-- (void)addChild:(SKBookmark *)child {}
-- (void)removeChild:(SKBookmark *)child {}
+- (unsigned int)countOfChildren { return 0; }
+- (SKBookmark *)objectInChildrenAtIndex:(unsigned int)anIndex { return nil; }
+- (void)insertObject:(SKBookmark *)child inChildrenAtIndex:(unsigned
int)anIndex {}
+- (void)removeObjectFromChildrenAtIndex:(unsigned int)anIndex {}
- (SKBookmark *)parent {
return parent;
@@ -195,17 +192,6 @@
return NO;
}
-- (NSUndoManager *)undoManager {
- return undoManager ? undoManager : [parent undoManager];
-}
-
-- (void)setUndoManager:(NSUndoManager *)newUndoManager {
- if (undoManager != newUndoManager) {
- [undoManager release];
- undoManager = [newUndoManager retain];
- }
-}
-
@end
#pragma mark -
@@ -232,7 +218,6 @@
}
- (void)dealloc {
- [[self undoManager] removeAllActionsWithTarget:self];
[alias release];
[aliasData release];
[label release];
@@ -307,10 +292,8 @@
- (void)setLabel:(NSString *)newLabel {
if (label != newLabel) {
- [(SKBookmark *)[[self undoManager] prepareWithInvocationTarget:self]
setLabel:label];
[label release];
label = [newLabel retain];
- [[NSNotificationCenter defaultCenter]
postNotificationName:SKBookmarkChangedNotification object:self];
}
}
@@ -334,7 +317,6 @@
}
- (void)dealloc {
- [[self undoManager] removeAllActionsWithTarget:self];
[label release];
[children release];
[super dealloc];
@@ -362,36 +344,33 @@
- (void)setLabel:(NSString *)newLabel {
if (label != newLabel) {
- [(SKBookmark *)[[self undoManager] prepareWithInvocationTarget:self]
setLabel:label];
[label release];
label = [newLabel retain];
- [[NSNotificationCenter defaultCenter]
postNotificationName:SKBookmarkChangedNotification object:self];
}
}
- (NSArray *)children {
- return children;
+ return [[children copy] autorelease];
}
-- (void)insertChild:(SKBookmark *)child atIndex:(unsigned int)anIndex {
- [(SKBookmark *)[[self undoManager] prepareWithInvocationTarget:self]
removeChild:child];
- [children insertObject:child atIndex:anIndex];
- [child setParent:self];
- [[NSNotificationCenter defaultCenter]
postNotificationName:SKBookmarkChangedNotification object:self];
+- (unsigned int)countOfChildren {
+ return [children count];
}
-- (void)addChild:(SKBookmark *)child {
- [self insertChild:child atIndex:[children count]];
+- (SKBookmark *)objectInChildrenAtIndex:(unsigned int)anIndex {
+ return [children objectAtIndex:anIndex];
}
-- (void)removeChild:(SKBookmark *)child {
- [(SKBookmark *)[[self undoManager] prepareWithInvocationTarget:self]
insertChild:child atIndex:[[self children] indexOfObject:child]];
- [[NSNotificationCenter defaultCenter]
postNotificationName:SKBookmarkWillBeRemovedNotification object:self];
- [child setParent:nil];
- [children removeObject:child];
- [[NSNotificationCenter defaultCenter]
postNotificationName:SKBookmarkChangedNotification object:self];
+- (void)insertObject:(SKBookmark *)child inChildrenAtIndex:(unsigned
int)anIndex {
+ [children insertObject:child atIndex:anIndex];
+ [child setParent:self];
}
+- (void)removeObjectFromChildrenAtIndex:(unsigned int)anIndex {
+ [[children objectAtIndex:anIndex] setParent:nil];
+ [children removeObjectAtIndex:anIndex];
+}
+
@end
#pragma mark -
Modified: trunk/SKBookmarkController.h
===================================================================
--- trunk/SKBookmarkController.h 2008-08-04 21:19:17 UTC (rev 4352)
+++ trunk/SKBookmarkController.h 2008-08-05 21:25:18 UTC (rev 4353)
@@ -55,10 +55,6 @@
- (SKBookmark *)bookmarkRoot;
- (void)addBookmarkForPath:(NSString *)path pageIndex:(unsigned)pageIndex
label:(NSString *)label toFolder:(SKBookmark *)folder;
-- (void)handleApplicationWillTerminateNotification:(NSNotification
*)notification;
-- (void)handleBookmarkChangedNotification:(NSNotification *)notification;
-- (void)handleBookmarkWillBeRemovedNotification:(NSNotification *)notification;
-- (NSString *)bookmarksFilePath;
- (void)openBookmarks:(NSArray *)items;
- (IBAction)doubleClickBookmark:(id)sender;
@@ -74,6 +70,4 @@
- (NSUndoManager *)undoManager;
-- (void)setupToolbar;
-
@end
Modified: trunk/SKBookmarkController.m
===================================================================
--- trunk/SKBookmarkController.m 2008-08-04 21:19:17 UTC (rev 4352)
+++ trunk/SKBookmarkController.m 2008-08-05 21:25:18 UTC (rev 4353)
@@ -71,7 +71,21 @@
static NSString *SKRecentDocumentAliasDataKey = @"_BDAlias";
static NSString *SKRecentDocumentSnapshotsKey = @"snapshots";
+static NSString *SKBookmarkChildrenKey = @"children";
+static NSString *SKBookmarkLabelKey = @"label";
+static NSString *SKBookmarkPropertiesObservationContext =
@"SKBookmarkPropertiesObservationContext";
+
+
[EMAIL PROTECTED] SKBookmarkController (SKPrivate)
+- (void)setupToolbar;
+- (NSString *)bookmarksFilePath;
+- (void)handleApplicationWillTerminateNotification:(NSNotification
*)notification;
+- (void)endEditing;
+- (void)startObservingBookmarks:(NSArray *)newBookmarks;
+- (void)stopObservingBookmarks:(NSArray *)oldBookmarks;
[EMAIL PROTECTED]
+
@implementation SKBookmarkController
static unsigned int maxRecentDocumentsCount = 0;
@@ -127,25 +141,18 @@
}
bookmarkRoot = [[SKBookmark alloc] initFolderWithChildren:bookmarks
label:nil];
- [bookmarkRoot setUndoManager:[self undoManager]];
+ [self startObservingBookmarks:[NSArray arrayWithObject:bookmarkRoot]];
[[NSNotificationCenter defaultCenter] addObserver:self
selector:@selector(handleApplicationWillTerminateNotification:)
name:NSApplicationWillTerminateNotification
object:NSApp];
- [[NSNotificationCenter defaultCenter] addObserver:self
-
selector:@selector(handleBookmarkChangedNotification:)
-
name:SKBookmarkChangedNotification
- object:nil];
- [[NSNotificationCenter defaultCenter] addObserver:self
-
selector:@selector(handleBookmarkWillBeRemovedNotification:)
-
name:SKBookmarkWillBeRemovedNotification
- object:nil];
}
return sharedBookmarkController;
}
- (void)dealloc {
+ [self stopObservingBookmarks:[NSArray arrayWithObject:bookmarkRoot]];
[[NSNotificationCenter defaultCenter] removeObserver:self];
[bookmarkRoot release];
[recentDocuments release];
@@ -191,7 +198,7 @@
if ([bookmark bookmarkType] == SKBookmarkTypeBookmark) {
message = [bookmark path];
} else if ([bookmark bookmarkType] == SKBookmarkTypeFolder) {
- int count = [[bookmark children] count];
+ int count = [bookmark countOfChildren];
message = count == 1 ? NSLocalizedString(@"1 item", @"Bookmark
folder description") : [NSString stringWithFormat:NSLocalizedString(@"%i
items", @"Bookmark folder description"), count];
}
}
@@ -220,9 +227,10 @@
}
- (void)addBookmarkForPath:(NSString *)path pageIndex:(unsigned)pageIndex
label:(NSString *)label toFolder:(SKBookmark *)folder {
+ if (folder == nil) folder = bookmarkRoot;
SKBookmark *bookmark = [[SKBookmark alloc] initWithPath:path
pageIndex:pageIndex label:label];
if (bookmark) {
- [(folder ? folder : bookmarkRoot) addChild:bookmark];
+ [folder insertObject:bookmark inChildrenAtIndex:[folder
countOfChildren]];
[bookmark release];
}
}
@@ -354,19 +362,19 @@
SKBookmark *folder = [[[SKBookmark alloc]
initFolderWithLabel:NSLocalizedString(@"Folder", @"default folder name")]
autorelease];
int rowIndex = [[outlineView selectedRowIndexes] lastIndex];
SKBookmark *item = bookmarkRoot;
- unsigned int idx = [[bookmarkRoot children] count];
+ unsigned int idx = [bookmarkRoot countOfChildren];
if (rowIndex != NSNotFound) {
SKBookmark *selectedItem = [outlineView itemAtRow:rowIndex];
if ([outlineView isItemExpanded:selectedItem]) {
item = selectedItem;
- idx = [[item children] count];
+ idx = [item countOfChildren];
} else {
item = [selectedItem parent];
idx = [[item children] indexOfObject:selectedItem] + 1;
}
}
- [item insertChild:folder atIndex:idx];
+ [item insertObject:folder inChildrenAtIndex:idx];
int row = [outlineView rowForItem:folder];
[outlineView selectRowIndexes:[NSIndexSet indexSetWithIndex:row]
byExtendingSelection:NO];
@@ -377,19 +385,19 @@
SKBookmark *separator = [[[SKBookmark alloc] initSeparator] autorelease];
int rowIndex = [[outlineView selectedRowIndexes] lastIndex];
SKBookmark *item = bookmarkRoot;
- unsigned int idx = [[bookmarkRoot children] count];
+ unsigned int idx = [bookmarkRoot countOfChildren];
if (rowIndex != NSNotFound) {
SKBookmark *selectedItem = [outlineView itemAtRow:rowIndex];
if ([outlineView isItemExpanded:selectedItem]) {
item = selectedItem;
- idx = [[item children] count];
+ idx = [item countOfChildren];
} else {
item = [selectedItem parent];
idx = [[item children] indexOfObject:selectedItem] + 1;
}
}
- [item insertChild:separator atIndex:idx];
+ [item insertObject:separator inChildrenAtIndex:idx];
int row = [outlineView rowForItem:separator];
[outlineView selectRowIndexes:[NSIndexSet indexSetWithIndex:row]
byExtendingSelection:NO];
@@ -416,6 +424,96 @@
return [self undoManager];
}
+- (void)startObservingBookmarks:(NSArray *)newBookmarks {
+ NSEnumerator *bmEnum = [newBookmarks objectEnumerator];
+ SKBookmark *bm;
+ while (bm = [bmEnum nextObject]) {
+ if ([bm bookmarkType] != SKBookmarkTypeSeparator) {
+ [bm addObserver:self forKeyPath:SKBookmarkLabelKey
options:(NSKeyValueObservingOptionNew | NSKeyValueObservingOptionOld)
context:SKBookmarkPropertiesObservationContext];
+ if ([bm bookmarkType] == SKBookmarkTypeFolder) {
+ [bm addObserver:self forKeyPath:SKBookmarkChildrenKey
options:(NSKeyValueObservingOptionNew | NSKeyValueObservingOptionOld)
context:SKBookmarkPropertiesObservationContext];
+ [self startObservingBookmarks:[bm children]];
+ }
+ }
+ }
+}
+
+- (void)stopObservingBookmarks:(NSArray *)oldBookmarks {
+ NSEnumerator *bmEnum = [oldBookmarks objectEnumerator];
+ SKBookmark *bm;
+ while (bm = [bmEnum nextObject]) {
+ if ([bm bookmarkType] != SKBookmarkTypeSeparator) {
+ [bm removeObserver:self forKeyPath:SKBookmarkLabelKey];
+ if ([bm bookmarkType] == SKBookmarkTypeFolder) {
+ [bm removeObserver:self forKeyPath:SKBookmarkChildrenKey];
+ [self stopObservingBookmarks:[bm children]];
+ }
+ }
+ }
+}
+
+- (void)insertObjects:(NSArray *)newChildren inChildrenOfBookmark:(SKBookmark
*)bookmark atIndexes:(NSIndexSet *)indexes {
+ [[bookmark mutableArrayValueForKey:SKBookmarkChildrenKey]
insertObjects:newChildren atIndexes:indexes];
+}
+
+- (void)removeObjectsFromChildrenOfBookmark:(SKBookmark *)bookmark
atIndexes:(NSIndexSet *)indexes {
+ [[bookmark mutableArrayValueForKey:SKBookmarkChildrenKey]
removeObjectsAtIndexes:indexes];
+}
+
+#pragma mark KVO
+
+- (void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object
change:(NSDictionary *)change context:(void *)context {
+ if (context == SKBookmarkPropertiesObservationContext) {
+ SKBookmark *bookmark = (SKBookmark *)object;
+ id newValue = [change objectForKey:NSKeyValueChangeNewKey];
+ id oldValue = [change objectForKey:NSKeyValueChangeOldKey];
+ NSIndexSet *indexes = [change objectForKey:NSKeyValueChangeIndexesKey];
+
+ if ([newValue isEqual:[NSNull null]]) newValue = nil;
+ if ([oldValue isEqual:[NSNull null]]) oldValue = nil;
+
+ switch ([[change objectForKey:NSKeyValueChangeKindKey]
unsignedIntValue]) {
+ case NSKeyValueChangeSetting:
+ if ([keyPath isEqualToString:SKBookmarkChildrenKey]) {
+ NSMutableArray *old = [NSMutableArray
arrayWithArray:oldValue];
+ NSMutableArray *new = [NSMutableArray
arrayWithArray:newValue];
+ [old removeObjectsInArray:newValue];
+ [new removeObjectsInArray:oldValue];
+ [self stopObservingBookmarks:old];
+ [self startObservingBookmarks:new];
+ [[[self undoManager] prepareWithInvocationTarget:bookmark]
setChildren:oldValue];
+ } else if ([keyPath isEqualToString:SKBookmarkLabelKey]) {
+ [[[self undoManager] prepareWithInvocationTarget:bookmark]
setLabel:oldValue];
+ }
+ break;
+ case NSKeyValueChangeInsertion:
+ if ([keyPath isEqualToString:SKBookmarkChildrenKey]) {
+ [self startObservingBookmarks:newValue];
+ [[[self undoManager] prepareWithInvocationTarget:self]
removeObjectsFromChildrenOfBookmark:bookmark atIndexes:indexes];
+ }
+ break;
+ case NSKeyValueChangeRemoval:
+ if ([keyPath isEqualToString:SKBookmarkChildrenKey]) {
+ [self stopObservingBookmarks:oldValue];
+ [[[self undoManager] prepareWithInvocationTarget:self]
insertObjects:oldValue inChildrenOfBookmark:bookmark atIndexes:indexes];
+ }
+ break;
+ case NSKeyValueChangeReplacement:
+ if ([keyPath isEqualToString:SKBookmarkChildrenKey]) {
+ [self stopObservingBookmarks:oldValue];
+ [self startObservingBookmarks:newValue];
+ [[[self undoManager] prepareWithInvocationTarget:self]
removeObjectsFromChildrenOfBookmark:bookmark atIndexes:indexes];
+ [[[self undoManager] prepareWithInvocationTarget:self]
insertObjects:oldValue inChildrenOfBookmark:bookmark atIndexes:indexes];
+ }
+ break;
+ }
+
+ [outlineView reloadData];
+ } else {
+ [super observeValueForKeyPath:keyPath ofObject:object change:change
context:context];
+ }
+}
+
#pragma mark Notification handlers
- (void)handleApplicationWillTerminateNotification:(NSNotification
*)notification {
@@ -433,20 +531,16 @@
}
}
-- (void)handleBookmarkWillBeRemovedNotification:(NSNotification *)notification
{
+- (void)endEditing {
if ([outlineView editedRow] && [[self window]
makeFirstResponder:outlineView] == NO)
[[self window] endEditingFor:nil];
}
-- (void)handleBookmarkChangedNotification:(NSNotification *)notification {
- [outlineView reloadData];
-}
-
#pragma mark NSOutlineView datasource methods
- (int)outlineView:(NSOutlineView *)ov numberOfChildrenOfItem:(id)item {
if (item == nil) item = bookmarkRoot;
- return [[item children] count];
+ return [item countOfChildren];
}
- (BOOL)outlineView:(NSOutlineView *)ov isItemExpandable:(id)item {
@@ -455,7 +549,7 @@
- (id)outlineView:(NSOutlineView *)ov child:(int)anIndex ofItem:(id)item {
if (item == nil) item = bookmarkRoot;
- return [[item children] objectAtIndex:anIndex];
+ return [item objectInChildrenAtIndex:anIndex];
}
- (id)outlineView:(NSOutlineView *)ov objectValueForTableColumn:(NSTableColumn
*)tableColumn byItem:(id)item {
@@ -464,7 +558,7 @@
return [NSDictionary dictionaryWithObjectsAndKeys:[item label],
SKTextWithIconCellStringKey, [item icon], SKTextWithIconCellImageKey, nil];
} else if ([tcID isEqualToString:@"file"]) {
if ([item bookmarkType] == SKBookmarkTypeFolder) {
- int count = [[item children] count];
+ int count = [item countOfChildren];
return count == 1 ? NSLocalizedString(@"1 item", @"Bookmark folder
description") : [NSString stringWithFormat:NSLocalizedString(@"%i items",
@"Bookmark folder description"), count];
} else {
return [item path];
@@ -503,7 +597,7 @@
} else if (item) {
[ov setDropItem:(SKBookmark *)[item parent] == bookmarkRoot ?
nil : [item parent] dropChildIndex:[[[item parent] children]
indexOfObject:item] + 1];
} else {
- [ov setDropItem:nil dropChildIndex:[[bookmarkRoot children]
count]];
+ [ov setDropItem:nil dropChildIndex:[bookmarkRoot
countOfChildren]];
}
}
return [item isDescendantOfArray:[self draggedBookmarks]] ?
NSDragOperationNone : NSDragOperationMove;
@@ -521,16 +615,18 @@
if (item == nil) item = bookmarkRoot;
+ [self endEditing];
while (bookmark = [bmEnum nextObject]) {
- int bookmarkIndex = [[[bookmark parent] children]
indexOfObject:bookmark];
- if (item == [bookmark parent]) {
+ SKBookmark *parent = [bookmark parent];
+ int bookmarkIndex = [[parent children] indexOfObject:bookmark];
+ if (item == parent) {
if (anIndex > bookmarkIndex)
anIndex--;
if (anIndex == bookmarkIndex)
continue;
}
- [[bookmark parent] removeChild:bookmark];
- [(SKBookmark *)item insertChild:bookmark atIndex:anIndex++];
+ [parent removeObjectFromChildrenAtIndex:bookmarkIndex];
+ [(SKBookmark *)item insertObject:bookmark
inChildrenAtIndex:anIndex++];
}
return YES;
}
@@ -576,8 +672,13 @@
- (void)outlineView:(NSOutlineView *)ov deleteItems:(NSArray *)items {
NSEnumerator *itemEnum = [[self minimumCoverForBookmarks:items]
reverseObjectEnumerator];
SKBookmark *item;
- while (item = [itemEnum nextObject])
- [[item parent] removeChild:item];
+ [self endEditing];
+ while (item = [itemEnum nextObject]) {
+ SKBookmark *parent = [item parent];
+ unsigned int itemIndex = [[parent children] indexOfObject:item];
+ if (itemIndex != NSNotFound)
+ [parent removeObjectFromChildrenAtIndex:itemIndex];
+ }
}
- (BOOL)outlineView:(NSOutlineView *)ov canDeleteItems:(NSArray *)items {
This was sent by the SourceForge.net collaborative development platform, the
world's largest Open Source development site.
-------------------------------------------------------------------------
This SF.Net email is sponsored by the Moblin Your Move Developer's challenge
Build the coolest Linux based applications with Moblin SDK & win great prizes
Grand prize is a trip for two to an Open Source event anywhere in the world
http://moblin-contest.org/redirect.php?banner_id=100&url=/
_______________________________________________
Skim-app-commit mailing list
[email protected]
https://lists.sourceforge.net/lists/listinfo/skim-app-commit