Johannes Fortmann's KVC/KVO/KVB patch:

- KVC now works for methods prefixed with '_'
- KVC works properly on NSDictionary, NSArray
- KVO on NSArray throws; addObserver:toObjectsAtIndexes implemented
- NSArrayController arrangedObjects property; probably many bugs,
incomplete
- NSArrayController selection property; dito. set and get work; merges
values to NSMultipleValuesMarker etc.
- NSBinder now has support for a replacement key path. e.g. on a color
well, you bind against "value", but the corresponding key is named
"color". The method -(NSString*)_replacementKeyPathForBinding:
(NSString*)binding should return "color" in this case if "value" is
passed in.
- helper methods to get all used bindings; moved
_binderClassForBinding: to class scope
- _NSTableColumnBinder as supporting class for bindings of the form
column -> arrangedObjects.name
- _NSTableViewContentBinder as supporting class for bindings of the
form tableView.content -> arrangedObjects
- changes for NSTableView to use NSIndexSet for row selection
- changes for NSTableView to utilize the support classes instead of
its data source if applicable
- NSNull copyWithZone implemented
This commit is contained in:
Christopher Lloyd 2007-05-11 12:49:23 +00:00
parent bedc7810fe
commit ccf3b51e72
27 changed files with 784 additions and 85 deletions

View File

@ -572,6 +572,8 @@
FE2A11550B45C65E006C03DE /* NSTokenField.m in Sources */ = {isa = PBXBuildFile; fileRef = FE2A11530B45C65E006C03DE /* NSTokenField.m */; };
FE2A115C0B45C66A006C03DE /* NSTokenFieldCell.h in Headers */ = {isa = PBXBuildFile; fileRef = FE2A115A0B45C66A006C03DE /* NSTokenFieldCell.h */; settings = {ATTRIBUTES = (Public, ); }; };
FE2A115D0B45C66A006C03DE /* NSTokenFieldCell.m in Sources */ = {isa = PBXBuildFile; fileRef = FE2A115B0B45C66A006C03DE /* NSTokenFieldCell.m */; };
FE352F6A0BF3998E00223FAF /* NSArrayControllerSelectionProxy.h in Headers */ = {isa = PBXBuildFile; fileRef = FE352F680BF3998E00223FAF /* NSArrayControllerSelectionProxy.h */; };
FE352F6B0BF3998E00223FAF /* NSArrayControllerSelectionProxy.m in Sources */ = {isa = PBXBuildFile; fileRef = FE352F690BF3998E00223FAF /* NSArrayControllerSelectionProxy.m */; };
FE4434040BB9833B00411464 /* NSGraphicsStyle_uxtheme.h in Headers */ = {isa = PBXBuildFile; fileRef = FE4434020BB9833B00411464 /* NSGraphicsStyle_uxtheme.h */; };
FE4434050BB9833B00411464 /* NSGraphicsStyle_uxtheme.m in Sources */ = {isa = PBXBuildFile; fileRef = FE4434030BB9833B00411464 /* NSGraphicsStyle_uxtheme.m */; };
FE4BDC0B0BCD40C600E19685 /* NSFontDescriptor.h in Headers */ = {isa = PBXBuildFile; fileRef = FE4BDC090BCD40C600E19685 /* NSFontDescriptor.h */; settings = {ATTRIBUTES = (Public, ); }; };
@ -657,8 +659,8 @@
FE8BAF280BC9EC680007FACA /* NSKVOBinder.m in Sources */ = {isa = PBXBuildFile; fileRef = FE8BAF200BC9EC680007FACA /* NSKVOBinder.m */; };
FE8BAF290BC9EC680007FACA /* NSObject+BindingSupport.h in Headers */ = {isa = PBXBuildFile; fileRef = FE8BAF210BC9EC680007FACA /* NSObject+BindingSupport.h */; settings = {ATTRIBUTES = (Public, ); }; };
FE8BAF2A0BC9EC680007FACA /* NSObject+BindingSupport.m in Sources */ = {isa = PBXBuildFile; fileRef = FE8BAF220BC9EC680007FACA /* NSObject+BindingSupport.m */; };
FE8BAF2B0BC9EC680007FACA /* NSTableViewBinder.h in Headers */ = {isa = PBXBuildFile; fileRef = FE8BAF230BC9EC680007FACA /* NSTableViewBinder.h */; };
FE8BAF2C0BC9EC680007FACA /* NSTableViewBinder.m in Sources */ = {isa = PBXBuildFile; fileRef = FE8BAF240BC9EC680007FACA /* NSTableViewBinder.m */; };
FEC5FF7D0BF49D5400682A70 /* NSTableColumnBinder.h in Headers */ = {isa = PBXBuildFile; fileRef = FEC5FF7B0BF49D5400682A70 /* NSTableColumnBinder.h */; };
FEC5FF7E0BF49D5400682A70 /* NSTableColumnBinder.m in Sources */ = {isa = PBXBuildFile; fileRef = FEC5FF7C0BF49D5400682A70 /* NSTableColumnBinder.m */; };
FED166D60BE502AD00BF9889 /* NSSegment.h in Headers */ = {isa = PBXBuildFile; fileRef = FED166D40BE502AD00BF9889 /* NSSegment.h */; };
FED166D70BE502AD00BF9889 /* NSSegment.m in Sources */ = {isa = PBXBuildFile; fileRef = FED166D50BE502AD00BF9889 /* NSSegment.m */; };
FED167AA0BE57EB300BF9889 /* NSTextBlock.h in Headers */ = {isa = PBXBuildFile; fileRef = FED167A40BE57EB300BF9889 /* NSTextBlock.h */; settings = {ATTRIBUTES = (Public, ); }; };
@ -1289,6 +1291,8 @@
FE2A11530B45C65E006C03DE /* NSTokenField.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = NSTokenField.m; sourceTree = "<group>"; };
FE2A115A0B45C66A006C03DE /* NSTokenFieldCell.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = NSTokenFieldCell.h; sourceTree = "<group>"; };
FE2A115B0B45C66A006C03DE /* NSTokenFieldCell.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = NSTokenFieldCell.m; sourceTree = "<group>"; };
FE352F680BF3998E00223FAF /* NSArrayControllerSelectionProxy.h */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.c.h; path = NSArrayControllerSelectionProxy.h; sourceTree = "<group>"; };
FE352F690BF3998E00223FAF /* NSArrayControllerSelectionProxy.m */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.c.objc; path = NSArrayControllerSelectionProxy.m; sourceTree = "<group>"; };
FE4434020BB9833B00411464 /* NSGraphicsStyle_uxtheme.h */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.c.h; path = NSGraphicsStyle_uxtheme.h; sourceTree = "<group>"; };
FE4434030BB9833B00411464 /* NSGraphicsStyle_uxtheme.m */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.c.objc; path = NSGraphicsStyle_uxtheme.m; sourceTree = "<group>"; };
FE4BDC090BCD40C600E19685 /* NSFontDescriptor.h */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.c.h; path = NSFontDescriptor.h; sourceTree = "<group>"; };
@ -1374,8 +1378,8 @@
FE8BAF200BC9EC680007FACA /* NSKVOBinder.m */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.c.objc; path = NSKVOBinder.m; sourceTree = "<group>"; };
FE8BAF210BC9EC680007FACA /* NSObject+BindingSupport.h */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.c.h; path = "NSObject+BindingSupport.h"; sourceTree = "<group>"; };
FE8BAF220BC9EC680007FACA /* NSObject+BindingSupport.m */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.c.objc; path = "NSObject+BindingSupport.m"; sourceTree = "<group>"; };
FE8BAF230BC9EC680007FACA /* NSTableViewBinder.h */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.c.h; path = NSTableViewBinder.h; sourceTree = "<group>"; };
FE8BAF240BC9EC680007FACA /* NSTableViewBinder.m */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.c.objc; path = NSTableViewBinder.m; sourceTree = "<group>"; };
FEC5FF7B0BF49D5400682A70 /* NSTableColumnBinder.h */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.c.h; path = NSTableColumnBinder.h; sourceTree = "<group>"; };
FEC5FF7C0BF49D5400682A70 /* NSTableColumnBinder.m */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.c.objc; path = NSTableColumnBinder.m; sourceTree = "<group>"; };
FED166D40BE502AD00BF9889 /* NSSegment.h */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.c.h; path = NSSegment.h; sourceTree = "<group>"; };
FED166D50BE502AD00BF9889 /* NSSegment.m */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.c.objc; path = NSSegment.m; sourceTree = "<group>"; };
FED167A40BE57EB300BF9889 /* NSTextBlock.h */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.c.h; path = NSTextBlock.h; sourceTree = "<group>"; };
@ -2289,6 +2293,8 @@
FE2A10250B44A239006C03DE /* NSController */ = {
isa = PBXGroup;
children = (
FE352F680BF3998E00223FAF /* NSArrayControllerSelectionProxy.h */,
FE352F690BF3998E00223FAF /* NSArrayControllerSelectionProxy.m */,
FE2A10280B44A26B006C03DE /* NSArrayController.h */,
FE2A10290B44A26B006C03DE /* NSArrayController.m */,
FE2A10580B459984006C03DE /* NSController.h */,
@ -2336,14 +2342,14 @@
FE8BAF1A0BC9EC0B0007FACA /* NSKeyValueBinding */ = {
isa = PBXGroup;
children = (
FEC5FF7B0BF49D5400682A70 /* NSTableColumnBinder.h */,
FEC5FF7C0BF49D5400682A70 /* NSTableColumnBinder.m */,
FE8BAF1D0BC9EC680007FACA /* NSBinder.h */,
FE8BAF1E0BC9EC680007FACA /* NSBinder.m */,
FE8BAF1F0BC9EC680007FACA /* NSKVOBinder.h */,
FE8BAF200BC9EC680007FACA /* NSKVOBinder.m */,
FE8BAF210BC9EC680007FACA /* NSObject+BindingSupport.h */,
FE8BAF220BC9EC680007FACA /* NSObject+BindingSupport.m */,
FE8BAF230BC9EC680007FACA /* NSTableViewBinder.h */,
FE8BAF240BC9EC680007FACA /* NSTableViewBinder.m */,
);
path = NSKeyValueBinding;
sourceTree = "<group>";
@ -2689,7 +2695,6 @@
FE8BAF250BC9EC680007FACA /* NSBinder.h in Headers */,
FE8BAF270BC9EC680007FACA /* NSKVOBinder.h in Headers */,
FE8BAF290BC9EC680007FACA /* NSObject+BindingSupport.h in Headers */,
FE8BAF2B0BC9EC680007FACA /* NSTableViewBinder.h in Headers */,
FED310E10BCC027200552615 /* NSAnimation.h in Headers */,
FED310E30BCC027200552615 /* NSViewAnimation.h in Headers */,
FE4BDC0B0BCD40C600E19685 /* NSFontDescriptor.h in Headers */,
@ -2698,6 +2703,8 @@
FED167AC0BE57EB300BF9889 /* NSTextTable.h in Headers */,
FED167AE0BE57EB300BF9889 /* NSTextTableBlock.h in Headers */,
FE27AC150BE9038000136B43 /* NSTextInput.h in Headers */,
FE352F6A0BF3998E00223FAF /* NSArrayControllerSelectionProxy.h in Headers */,
FEC5FF7D0BF49D5400682A70 /* NSTableColumnBinder.h in Headers */,
);
runOnlyForDeploymentPostprocessing = 0;
};
@ -3137,7 +3144,6 @@
FE8BAF260BC9EC680007FACA /* NSBinder.m in Sources */,
FE8BAF280BC9EC680007FACA /* NSKVOBinder.m in Sources */,
FE8BAF2A0BC9EC680007FACA /* NSObject+BindingSupport.m in Sources */,
FE8BAF2C0BC9EC680007FACA /* NSTableViewBinder.m in Sources */,
FED310E20BCC027200552615 /* NSAnimation.m in Sources */,
FED310E40BCC027200552615 /* NSViewAnimation.m in Sources */,
FE4BDC0C0BCD40C600E19685 /* NSFontDescriptor.m in Sources */,
@ -3145,6 +3151,8 @@
FED167AB0BE57EB300BF9889 /* NSTextBlock.m in Sources */,
FED167AD0BE57EB300BF9889 /* NSTextTable.m in Sources */,
FED167AF0BE57EB300BF9889 /* NSTextTableBlock.m in Sources */,
FE352F6B0BF3998E00223FAF /* NSArrayControllerSelectionProxy.m in Sources */,
FEC5FF7E0BF49D5400682A70 /* NSTableColumnBinder.m in Sources */,
);
runOnlyForDeploymentPostprocessing = 0;
};

View File

@ -536,6 +536,7 @@ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLI
[self setObjectValue:[NSNumber numberWithDouble:value]];
}
-(void)setAttributedStringValue:(NSAttributedString *)value {
value=[value copy];
[_objectValue release];

View File

@ -20,6 +20,19 @@ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLI
@implementation NSColorWell
+(void)initialize
{
[self setKeys:[NSArray arrayWithObjects:@"color", @"something", nil]
triggerChangeNotificationsForDependentKey:@"value"];
}
-(id)_replacementKeyPathForBinding:(id)binding
{
if([binding isEqual:@"value"])
return @"color";
return binding;
}
// private
NSString *_NSColorWellDidBecomeExclusiveNotification=@"_NSColorWellDidBecomeExclusiveNotification";
@ -127,6 +140,9 @@ NSString *_NSColorWellDidBecomeExclusiveNotification=@"_NSColorWellDidBecomeExcl
}
-(void)setColor:(NSColor *)color {
if(![color isKindOfClass:[NSColor class]])
return [self setColor:[NSColor blackColor]];
color=[color retain];
[_color release];
_color=color;
@ -254,4 +270,5 @@ NSString *_NSColorWellDidBecomeExclusiveNotification=@"_NSColorWellDidBecomeExcl
return YES;
}
@end

View File

@ -289,9 +289,14 @@ static NSMutableDictionary *cellClassDictionary = nil;
-(void)updateCell:(NSCell *)cell {
if (_cell == cell)
{
[self willChangeValueForKey:@"objectValue"];
[self didChangeValueForKey:@"objectValue"];
[self setNeedsDisplay:YES];
}
}
-(void)updateCellInside:(NSCell *)cell {
if (_cell == cell)
[self setNeedsDisplay:YES];

View File

@ -1,4 +1,5 @@
/* Copyright (c) 2006-2007 Christopher J. W. Lloyd
Copyright (c) 2007 Johannes Fortmann
Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
@ -11,7 +12,22 @@ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLI
@class NSPredicate,NSIndexSet;
@interface NSArrayController : NSObjectController {
struct
{
long avoidsEmptySelection:1;
long clearsFilterPredicateOnInsertion:1;
long editable:1;
long filterRestrictsInsertion:1;
long preservesSelection:1;
long selectsInsertedObjects:1;
long alwaysUsesMultipleValuesMarker:1;
} flags;
id contentArray;
id selectionIndexes;
id sortDescriptors;
id filterPredicate;
id _selection;
id arrangedObjects;
}
#if 0

View File

@ -1,4 +1,4 @@
/* Copyright (c) 2006-2007 Christopher J. W. Lloyd
/* Copyright (c) 2007 Johannes Fortmann
Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
@ -6,7 +6,183 @@ The above copyright notice and this permission notice shall be included in all c
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
#import <AppKit/NSArrayController.h>
#import <Foundation/NSArray.h>
#import <Foundation/NSString.h>
#import "NSArrayControllerSelectionProxy.h"
#import <Foundation/NSIndexSet.h>
#import <Foundation/NSException.h>
@implementation NSArrayController
+(void)initialize
{
[self setKeys:[NSArray arrayWithObjects:@"contentArray", @"selectionIndexes", nil]
triggerChangeNotificationsForDependentKey:@"selection"];
}
-(id)initWithCoder:(id)coder
{
self=[super init];
if(self)
{
flags.avoidsEmptySelection = [coder decodeBoolForKey:@"NSAvoidsEmptySelection"];
flags.clearsFilterPredicateOnInsertion = [coder decodeBoolForKey:@"NSClearsFilterPredicateOnInsertion"];
flags.editable = [coder decodeBoolForKey:@"NSEditable"];
flags.filterRestrictsInsertion = [coder decodeBoolForKey:@"NSFilterRestrictsInsertion"];
flags.preservesSelection = [coder decodeBoolForKey:@"NSPreservesSelection"];
flags.selectsInsertedObjects = [coder decodeBoolForKey:@"NSSelectsInsertedObjects"];
flags.alwaysUsesMultipleValuesMarker = [coder decodeBoolForKey:@"NSAlwaysUsesMultipleValuesMarker"];
id declaredKeys=[coder decodeObjectForKey:@"NSDeclaredKeys"];
}
// FIX: cocotron nib loading seems not to retain top-level objects. _dirtiest_ possible hack: retain
return [self retain];
}
-(void)dealloc
{
[_selection release];
[contentArray release];
[selectionIndexes release];
[super dealloc];
}
-(id)awakeFromNib
{
[self willChangeValueForKey:@"selection"];
_selection=[[NSArrayControllerSelectionProxy alloc] initWithArrayController:self];
[self didChangeValueForKey:@"selection"];
}
- (id)contentArray {
return [[contentArray retain] autorelease];
}
- (void)setContentArray:(id)value {
if (contentArray != value) {
[contentArray release];
contentArray = [value copy];
if([self filterPredicate])
value=[value filteredArrayUsingPredicate:[self filterPredicate]];
if([self sortDescriptors])
value=[value sortedArrayUsingDescriptors:[self sortDescriptors]];
[self setArrangedObjects:value];
}
}
- (void)setArrangedObjects:(id)value {
if (arrangedObjects != value)
{
[arrangedObjects release];
arrangedObjects = [[NSArray alloc] initWithArray:value];
}
}
-(id)arrangedObjects
{
return arrangedObjects;
}
-(id)selection
{
return _selection;
}
- (id)selectionIndexes {
return [[selectionIndexes retain] autorelease];
}
- (void)setSelectionIndexes:(id)value {
if(!value && flags.avoidsEmptySelection && [[self arrangedObjects] count])
value=[NSIndexSet indexSetWithIndex:0];
if (selectionIndexes != value) {
[selectionIndexes release];
selectionIndexes = [value copy];
NSLog(@"selectionIndexes changed to %@", value);
[self willChangeValueForKey:@"selection"];
[_selection release];
_selection = nil;
_selection=[[NSArrayControllerSelectionProxy alloc] initWithArrayController:self];
[self didChangeValueForKey:@"selection"];
}
}
- (id)sortDescriptors {
return [[sortDescriptors retain] autorelease];
}
- (void)setSortDescriptors:(id)value {
if (sortDescriptors != value) {
[sortDescriptors release];
sortDescriptors = [value copy];
}
}
- (id)filterPredicate {
return [[filterPredicate retain] autorelease];
}
- (void)setFilterPredicate:(id)value {
if (filterPredicate != value) {
[filterPredicate release];
filterPredicate = [value copy];
}
}
-(BOOL)alwaysUsesMultipleValuesMarker
{
return flags.alwaysUsesMultipleValuesMarker;
}
-(id)selectedObjects
{
id idxs=[self selectionIndexes];
if(idxs)
return [[self arrangedObjects] objectsAtIndexes:idxs];
return nil;
}
-(void)add:(id)sender
{
}
-(void)remove:(id)sender
{
}
-(void)selectNext:(id)sender
{
id idxs=[[[self selectionIndexes] mutableCopy] autorelease];
if(!idxs)
return [self setSelectionIndexes:[NSIndexSet indexSetWithIndex:0]];
[idxs shiftIndexesStartingAtIndex:0 by:1];
if([idxs lastIndex]<[[self arrangedObjects] count])
[self setSelectionIndexes:idxs];
}
-(void)selectPrevious:(id)sender
{
id idxs=[[[self selectionIndexes] mutableCopy] autorelease];
if(!idxs)
return [self setSelectionIndexes:[NSIndexSet indexSetWithIndex:0]];
if([idxs firstIndex]>0)
{
[idxs shiftIndexesStartingAtIndex:0 by:-1];
[self setSelectionIndexes:idxs];
}
}
@end

View File

@ -0,0 +1,19 @@
/* Copyright (c) 2007 Johannes Fortmann
Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
#import <Foundation/NSObject.h>
#import <Foundation/NSMutableDictionary.h>
@interface NSArrayControllerSelectionProxy : NSObject
{
id controller;
NSMutableDictionary *values;
}
-(id)initWithArrayController:(id)cont;
@end

View File

@ -0,0 +1,94 @@
/* Copyright (c) 2007 Johannes Fortmann
Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
#import "NSArrayControllerSelectionProxy.h"
#import "NSController.h"
#import <Foundation/NSString.h>
@implementation NSArrayControllerSelectionProxy
-(id)initWithArrayController:(id)cont
{
if(self=[super init])
{
values=[NSMutableDictionary new];
controller = [cont retain];
}
return self;
}
-(void)dealloc
{
[values release];
[controller release];
[super dealloc];
}
-(id)valueForKey:(id)key
{
id val=[values objectForKey:key];
if(val)
return val;
id allValues=[[controller selectedObjects] valueForKeyPath:key];
switch([allValues count])
{
case 0:
val=NSNoSelectionMarker;
break;
case 1:
val=[allValues lastObject];
break;
default:
{
if([controller alwaysUsesMultipleValuesMarker])
{
val=NSMultipleValuesMarker;
}
else
{
val=[allValues objectAtIndex:0];
id en=[allValues objectEnumerator];
id obj;
while((obj=[en nextObject]) && val!=NSMultipleValuesMarker)
{
if(![val isEqual:obj])
val=NSMultipleValuesMarker;
}
}
break;
}
}
[values setValue:val forKey:key];
return val;
}
-(int)count
{
return [values count];
}
-(id)keyEnumerator
{
return [values keyEnumerator];
}
-(void)setValue:(id)value forKey:(id)key
{
NSLog(@"setValue %@ forKey %@", value, key);
[[controller selectedObjects] setValue:value forKey:key];
}
-(id)description
{
return [NSString stringWithFormat:
@"%@ <0x%x>",
[self className],
self];
}
@end

View File

@ -1,4 +1,4 @@
/* Copyright (c) 2006-2007 Christopher J. W. Lloyd
/* Copyright (c) 2007 Johannes Fortmann
Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
@ -6,6 +6,10 @@ The above copyright notice and this permission notice shall be included in all c
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
#import <Foundation/NSObject.h>
#import <AppKit/AppKitExport.h>
APPKIT_EXPORT NSString *NSNoSelectionMarker;
APPKIT_EXPORT NSString *NSMultipleValuesMarker;
@interface NSController : NSObject <NSCoding> {

View File

@ -1,4 +1,4 @@
/* Copyright (c) 2006-2007 Christopher J. W. Lloyd
/* Copyright (c) 2007 Johannes Fortmann
Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
@ -8,6 +8,9 @@ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLI
#import <AppKit/NSController.h>
#import <Foundation/NSRaise.h>
NSString *NSNoSelectionMarker=@"NSNoSelectionMarker";
NSString *NSMultipleValuesMarker=@"NSMultipleValuesMarker";
@implementation NSController
-initWithCoder:(NSCoder *)coder {

View File

@ -12,8 +12,13 @@ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLI
id source;
id destination;
id keyPath;
id bindingPath;
id binding;
id options;
}
- (id)options;
- (void)setOptions:(id)value;
- (id)source;
- (void)setSource:(id)value;

View File

@ -18,6 +18,7 @@ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLI
if (source != value)
{
source = value;
[self setBindingPath:[source _replacementKeyPathForBinding:binding]];
}
}
@ -53,6 +54,29 @@ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLI
if (binding != value) {
[binding release];
binding = [value copy];
[self setBindingPath:[source _replacementKeyPathForBinding:binding]];
}
}
- (id)options {
return [[options retain] autorelease];
}
- (void)setOptions:(id)value {
if (options != value) {
[options release];
options = [value copy];
}
}
- (id)bindingPath {
return [[bindingPath retain] autorelease];
}
- (void)setBindingPath:(id)value {
if (bindingPath != value) {
[bindingPath release];
bindingPath = [value copy];
}
}
@ -64,9 +88,13 @@ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLI
[self stopObservingChanges];
[keyPath release];
[binding release];
[options release];
[bindingPath release];
[super dealloc];
}
-(void)bind
{
}

View File

@ -10,7 +10,7 @@ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLI
@interface _NSKVOBinder : _NSBinder
{
id bindingKeyPath;
}
@end

View File

@ -15,8 +15,10 @@ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLI
@implementation _NSKVOBinder
-(void)startObservingChanges
{
NSLog(@"binding between %@.%@ alias %@ and %@.%@ (%@)", [source className], binding, bindingPath, [destination className], keyPath, self);
[source addObserver:self
forKeyPath:binding
forKeyPath:bindingPath
options:NSKeyValueObservingOptionNew
context:nil];
[destination addObserver:self
@ -28,7 +30,7 @@ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLI
-(void)stopObservingChanges
{
NS_DURING
[source removeObserver:self forKeyPath:binding];
[source removeObserver:self forKeyPath:bindingPath];
[destination removeObserver:self forKeyPath:keyPath];
NS_HANDLER
NS_ENDHANDLER
@ -36,32 +38,39 @@ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLI
-(void)syncUp
{
NS_DURING
id value=[destination valueForKeyPath:keyPath];
if(value)
[source setValue:value forKeyPath:binding];
[source setValue:value forKeyPath:bindingPath];
else
{
value=[source valueForKeyPath:binding];
value=[source valueForKeyPath:bindingPath];
if(value)
[destination setValue:value forKeyPath:keyPath];
}
NS_HANDLER
NS_ENDHANDLER
}
- (void)observeValueForKeyPath:(NSString *)kp ofObject:(id)object change:(NSDictionary *)change context:(void *)context
{
[self stopObservingChanges];
NSLog(@"bind event from %@.%@ to %@.%@ (%@)", [source className], binding, [destination className], keyPath, self);
if(object==source)
{
NSLog(@"bind event from %@.%@ alias %@ to %@.%@ (%@)", [source className], binding, bindingPath, [destination className], keyPath, self);
[destination setValue:[change valueForKey:NSKeyValueChangeNewKey]
forKey:keyPath];
forKeyPath:keyPath];
}
else if(object==destination)
{
NSLog(@"bind event from %@.%@ to %@.%@ alias %@ (%@)", [destination className], keyPath, [source className], binding, bindingPath, self);
[source setValue:[change valueForKey:NSKeyValueChangeNewKey]
forKey:binding];
forKeyPath:bindingPath];
}
[self startObservingChanges];

View File

@ -10,5 +10,14 @@ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLI
@class NSDictionary;
@interface NSObject (BindingSupport)
-(void)bind:(id)binding toObject:(id)destination withKeyPath:(NSString*)keyPath options:(NSDictionary*)options;
-(void)bind:(id)binding toObject:(NSString*)destination withKeyPath:(NSString*)keyPath options:(NSDictionary*)options;
-(void)infoForBinding:(NSString*)binding;
-(void)unbind:(NSString*)binding;
@end
@interface NSObject (InternalBindingSupport)
+(Class)_binderClassForBinding:(id)binding;
-(void)_cleanupBinders;
-(NSArray*)_allUsedBinders;
@end

View File

@ -5,17 +5,18 @@ Permission is hereby granted, free of charge, to any person obtaining a copy of
The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
#import <AppKit/NSObject+BindingSupport.h>
#import "NSKVOBinder.h"
#import <Foundation/NSDictionary.h>
#import <Foundation/NSValue.h>
#import <Foundation/NSString.h>
#import <AppKit/NSObject+BindingSupport.h>
NSMutableDictionary *bindersForObjects=nil;
@implementation NSObject (BindingSupport)
-(Class)_binderClassForBinding:(id)binding
+(Class)_binderClassForBinding:(id)binding
{
//return nil;
//return [_NSBinder class];
return [_NSKVOBinder class];
}
@ -37,21 +38,28 @@ NSMutableDictionary *bindersForObjects=nil;
if(!binder && create)
{
binder = [[[self _binderClassForBinding:binding] new] autorelease];
binder = [[[isa _binderClassForBinding:binding] new] autorelease];
[ownBinders setObject:binder forKey:binding];
}
return binder;
}
-(id)_replacementKeyPathForBinding:(id)binding
{
if([binding isEqual:@"value"])
return @"objectValue";
return binding;
}
-(void)_cleanupBinders
{
[bindersForObjects removeObjectForKey:[NSValue valueWithNonretainedObject:self]];
}
-(void)bind:(id)binding toObject:(id)destination withKeyPath:(NSString*)keyPath options:(NSDictionary*)options
-(void)bind:(NSString*)binding toObject:(id)destination withKeyPath:(NSString*)keyPath options:(NSDictionary*)options
{
if(![self _binderClassForBinding:binding])
if(![isa _binderClassForBinding:binding])
return;
id binder=[self _binderForBinding:binding create:NO];
@ -65,7 +73,31 @@ NSMutableDictionary *bindersForObjects=nil;
[binder setDestination:destination];
[binder setKeyPath:keyPath];
[binder setBinding:binding];
[binder setOptions:options];
[binder bind];
}
-(void)unbind:(NSString*)binding
{
id key = [NSValue valueWithNonretainedObject:self];
id ownBinders = [bindersForObjects objectForKey:key];
id binder=[ownBinders objectForKey:binding];
[binder unbind];
[ownBinders removeObjectForKey:binding];
}
-(void)infoForBinding:(NSString*)binding
{
return [[self _binderForBinding:binding create:NO] options];
}
-(NSArray*)_allUsedBinders
{
id key = [NSValue valueWithNonretainedObject:self];
id ownBinders = [bindersForObjects objectForKey:key];
return [ownBinders allValues];
}
@end

View File

@ -9,9 +9,16 @@ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLI
@class NSArray;
@interface _NSTableViewBinder : _NSBinder
@interface _NSTableColumnBinder : _NSBinder
{
NSArray* rowValues;
id pathToArray;
}
-(void)applyToCell:(id)cell inRow:(int)row;
-(void)updateRowValues;
@end
@interface _NSTableViewContentBinder : _NSBinder
@end

View File

@ -5,15 +5,16 @@ Permission is hereby granted, free of charge, to any person obtaining a copy of
The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
#import "NSTableViewBinder.h"
#import "NSTableColumnBinder.h"
#import <Foundation/NSDictionary.h>
#import <Foundation/NSException.h>
#import <Foundation/NSKeyValueObserving.h>
#import <Foundation/NSKeyValueCoding.h>
#import <Foundation/NSArray.h>
#import <Foundation/NSString.h>
@implementation _NSTableViewBinder
@implementation _NSTableColumnBinder
- (NSArray *)rowValues
{
return [[rowValues retain] autorelease];
@ -24,11 +25,17 @@ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLI
if (rowValues != value)
{
[rowValues release];
rowValues = [value copy];
rowValues = [value retain];
}
}
-(void)applyToCell:(id)cell inRow:(int)row
{
[cell setValue:[rowValues objectAtIndex:row] forKey:bindingPath];
}
-(void)dealloc
{
@ -38,29 +45,46 @@ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLI
-(void)startObservingChanges
{
[destination addObserver:self forKeyPath:keyPath options:7 context:nil];
//NSLog(@"%@: observing path %@ on %@", [self className], keyPath, destination);
NS_DURING
//[destination addObserver:self forKeyPath:keyPath options:0 context:destination];
NS_HANDLER
NS_ENDHANDLER
}
-(void)stopObservingChanges
{
NS_DURING
[destination removeObserver:self forKeyPath:keyPath];
// [destination removeObserver:self forKeyPath:keyPath];
NS_HANDLER
NS_ENDHANDLER
NS_ENDHANDLER
}
-(void)syncUp
{
[self setRowValues:[destination valueForKeyPath:keyPath]];
[self updateRowValues];
}
- (void)observeValueForKeyPath:(NSString *)kp ofObject:(id)object change:(NSDictionary *)change context:(void *)context
{
[self stopObservingChanges];
if(object==destination)
if(object==source)
{
[self setRowValues:[change valueForKey:NSKeyValueChangeNewKey]];
NSLog(@"bind event from %@.%@ alias %@ to %@.%@ (%@)", [source className], binding, bindingPath, [destination className], keyPath, self);
}
else if(context==destination)
{
NSLog(@"bind event from %@.%@ to %@.%@ alias %@ (%@)", [destination className], keyPath, [source className], binding, bindingPath, self);
[self updateRowValues];
if([source respondsToSelector:@selector(reloadData)])
[source reloadData];
if([source respondsToSelector:@selector(tableView)])
[[source tableView] reloadData];
}
[self startObservingChanges];
@ -68,8 +92,8 @@ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLI
-(void)bind
{
[self startObservingChanges];
[self syncUp];
[self startObservingChanges];
}
-(void)unbind
@ -87,4 +111,71 @@ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLI
return [rowValues objectAtIndex:row];
}
-(id)description
{
return [NSString stringWithFormat:@"%@ %@", [super description], [self rowValues]];
}
-(void)updateRowValues
{
[self setRowValues:[destination valueForKeyPath:keyPath]];
}
@end
@class NSTableView;
@implementation _NSTableViewContentBinder
-(void)startObservingChanges
{
NSParameterAssert([source isKindOfClass:[NSTableView class]]);
[destination addObserver:self
forKeyPath:keyPath
options:NSKeyValueObservingOptionNew
context:nil];
}
-(void)stopObservingChanges
{
NS_DURING
[destination removeObserver:self forKeyPath:keyPath];
NS_HANDLER
NS_ENDHANDLER
}
-(void)syncUp
{
[source performSelector:@selector(reloadData)
withObject:nil
afterDelay:1.0];
[source reloadData];
}
- (void)observeValueForKeyPath:(NSString *)kp ofObject:(id)object change:(NSDictionary *)change context:(void *)context
{
[self stopObservingChanges];
if(object==destination)
{
[source _boundValuesChanged];
}
[self startObservingChanges];
}
-(void)bind
{
[self syncUp];
[self startObservingChanges];
}
-(void)unbind
{
[self stopObservingChanges];
}
@end

View File

@ -1,4 +1,5 @@
/* Copyright (c) 2006-2007 Christopher J. W. Lloyd
Copyright (c) 2007 Johannes Fortmann
Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
@ -10,6 +11,9 @@ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLI
#import <AppKit/AppKit.h>
#import <AppKit/NSNibKeyedUnarchiver.h>
#import "NSKeyValueBinding/NSTableColumnBinder.h"
#import "NSKeyValueBinding/NSKVOBinder.h"
@implementation NSTableColumn
-(void)encodeWithCoder:(NSCoder *)coder {
@ -145,4 +149,55 @@ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLI
return [self dataCell];
}
-(int)_rowCountFromBindings
{
id binder=[self _binderForBinding:@"value" create:NO];
if(!binder)
return -1;
return [binder count];
}
-(void)_boundValuesChanged
{
id binders=[self _allUsedBinders];
int count=[binders count];
int i;
for(i=0; i<count; i++)
{
id binder=[binders objectAtIndex:i];
if([binder isKindOfClass:[_NSTableColumnBinder class]])
{
[binder updateRowValues];
}
}
}
-(void)prepareCell:(id)cell inRow:(int)row
{
id binders=[self _allUsedBinders];
int count=[binders count];
int i;
for(i=0; i<count; i++)
{
id binder=[binders objectAtIndex:i];
if([binder isKindOfClass:[_NSTableColumnBinder class]])
{
[binder applyToCell:cell inRow:row];
}
}
}
+(Class)_binderClassForBinding:(id)binding
{
if([binding isEqual:@"headerTitle"] ||
[binding isEqual:@"maxWidth"] ||
[binding isEqual:@"minWidth"] ||
[binding isEqual:@"width"])
return [_NSKVOBinder class];
return [_NSTableColumnBinder class];
}
@end

View File

@ -41,8 +41,8 @@ APPKIT_EXPORT NSString *NSTableViewColumnDidResizeNotification;
// temp ivars
int _numberOfRows;
NSMutableArray *_selectedRows;
NSMutableArray *_selectedColumns;
NSIndexSet *_selectedRowIndexes;
int _clickedColumn, _clickedRow;
int _editedColumn, _editedRow;
id _editingCell;
@ -148,6 +148,8 @@ APPKIT_EXPORT NSString *NSTableViewColumnDidResizeNotification;
-(void)drawRow:(int)row clipRect:(NSRect)rect;
-(void)drawGridInClipRect:(NSRect)rect;
- (NSIndexSet *)selectedRowIndexes;
- (void)setSelectedRowIndexes:(NSIndexSet *)value;
@end
@interface NSObject(NSTableView_dataSource)

View File

@ -10,6 +10,8 @@ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLI
#import <AppKit/AppKit.h>
#import <AppKit/NSTableCornerView.h>
#import <AppKit/NSNibKeyedUnarchiver.h>
#import "NSKeyValueBinding/NSTableColumnBinder.h"
#import "NSKeyValueBinding/NSKVOBinder.h"
NSString *NSTableViewSelectionIsChangingNotification=@"NSTableViewSelectionIsChangingNotification";
NSString *NSTableViewSelectionDidChangeNotification=@"NSTableViewSelectionDidChangeNotification";
@ -33,6 +35,27 @@ NSString *NSTableViewColumnDidResizeNotification=@"NSTableViewColumnDidResizeNot
@implementation NSTableView
-(id)_replacementKeyPathForBinding:(id)binding
{
if([binding isEqual:@"selectionIndexes"])
return @"selectedRowIndexes";
return binding;
}
+(Class)_binderClassForBinding:(id)binding
{
if([binding isEqual:@"content"])
return [_NSTableViewContentBinder class];
return [_NSKVOBinder class];
}
-(void)_boundValuesChanged
{
[_tableColumns makeObjectsPerformSelector:@selector(_boundValuesChanged)];
[self reloadData];
}
-(void)encodeWithCoder:(NSCoder *)coder {
NSUnimplementedMethod();
}
@ -59,7 +82,7 @@ NSString *NSTableViewColumnDidResizeNotification=@"NSTableViewColumnDidResizeNot
_allowsEmptySelection=(flags&0x10000000)?YES:NO;
_allowsColumnSelection=(flags&0x04000000)?YES:NO;
_intercellSpacing = NSMakeSize(3.0,2.0);
_selectedRows = [[NSMutableArray alloc] init];
_selectedRowIndexes = [[NSIndexSet alloc] init];
_selectedColumns = [[NSMutableArray alloc] init];
_editedColumn = -1;
_editedRow = -1;
@ -76,7 +99,7 @@ NSString *NSTableViewColumnDidResizeNotification=@"NSTableViewColumnDidResizeNot
// Returns the height of each row in the receiver. The default row height is 16.0.
_rowHeight = 16.0;
_intercellSpacing = NSMakeSize(3.0,2.0);
_selectedRows = [[NSMutableArray alloc] init];
_selectedRowIndexes = [[NSIndexSet alloc] init];
_selectedColumns = [[NSMutableArray alloc] init];
_editedColumn = -1;
_editedRow = -1;
@ -106,7 +129,7 @@ NSString *NSTableViewColumnDidResizeNotification=@"NSTableViewColumnDidResizeNot
[_tableColumns release];
[_backgroundColor release];
[_gridColor release];
[_selectedRows release];
[NSIndexSet release];
[_selectedColumns release];
[super dealloc];
}
@ -194,6 +217,12 @@ NSString *NSTableViewColumnDidResizeNotification=@"NSTableViewColumnDidResizeNot
}
-(int)numberOfRows {
int val;
if((val = [[_tableColumns lastObject] _rowCountFromBindings]) >= 0)
{
return val;
}
return [_dataSource numberOfRowsInTableView:self];
}
@ -543,11 +572,24 @@ NSString *NSTableViewColumnDidResizeNotification=@"NSTableViewColumnDidResizeNot
return _clickedColumn;
}
- (NSIndexSet *)selectedRowIndexes {
return [[_selectedRowIndexes retain] autorelease];
}
- (void)setSelectedRowIndexes:(NSIndexSet *)value {
if (_selectedRowIndexes != value) {
[_selectedRowIndexes release];
_selectedRowIndexes = [value copy];
[self setNeedsDisplay:YES];
}
}
-(int)selectedRow {
if([_selectedRows count]==0)
if([_selectedRowIndexes count]==0)
return -1;
return [[_selectedRows objectAtIndex:0] intValue];
return [_selectedRowIndexes firstIndex];
}
-(int)selectedColumn {
@ -558,7 +600,7 @@ NSString *NSTableViewColumnDidResizeNotification=@"NSTableViewColumnDidResizeNot
}
-(int)numberOfSelectedRows {
return [_selectedRows count];
return [_selectedRowIndexes count];
}
-(int)numberOfSelectedColumns {
@ -566,7 +608,7 @@ NSString *NSTableViewColumnDidResizeNotification=@"NSTableViewColumnDidResizeNot
}
-(BOOL)isRowSelected:(int)row {
return [_selectedRows containsObject:[NSNumber numberWithInt:row]];
return [_selectedRowIndexes containsIndex:row];
}
-(BOOL)isColumnSelected:(int)col {
@ -578,22 +620,28 @@ NSString *NSTableViewColumnDidResizeNotification=@"NSTableViewColumnDidResizeNot
}
-(NSEnumerator *)selectedRowEnumerator {
return [_selectedRows objectEnumerator];
NSUnimplementedMethod();
}
-(void)selectRow:(int)row byExtendingSelection:(BOOL)extend {
NSLog(@"selectRow:%i extend:%i", row, extend);
// selecting a row deselects all columns
[_selectedColumns removeAllObjects];
if (extend == NO)
[_selectedRows removeAllObjects];
NSMutableIndexSet* selectedRowIndexes=[[[self selectedRowIndexes] mutableCopy] autorelease];
if (![_selectedRows containsObject:[NSNumber numberWithInt:row]]) {
if (extend == NO)
[selectedRowIndexes removeAllIndexes];
if (![selectedRowIndexes containsIndex:row]) {
if ([self delegateShouldSelectRow:row])
[_selectedRows addObject:[NSNumber numberWithInt:row]];
[selectedRowIndexes addIndex:row];
}
[self setNeedsDisplay:YES];
[self setSelectedRowIndexes:selectedRowIndexes];
[_headerView setNeedsDisplay:YES];
}
@ -601,7 +649,7 @@ NSString *NSTableViewColumnDidResizeNotification=@"NSTableViewColumnDidResizeNot
NSTableColumn *tableColumn = [_tableColumns objectAtIndex:column];
// selecting a column deselects all rows
[_selectedRows removeAllObjects];
[self setSelectedRowIndexes:[NSIndexSet indexSet]];
if (extend == NO)
[_selectedColumns removeAllObjects];
@ -616,9 +664,12 @@ NSString *NSTableViewColumnDidResizeNotification=@"NSTableViewColumnDidResizeNot
}
-(void)deselectRow:(int)row {
if ([_selectedRows containsObject:[NSNumber numberWithInt:row]]) {
[_selectedRows removeObject:[NSNumber numberWithInt:row]];
[self setNeedsDisplay:YES];
NSMutableIndexSet* selectedRowIndexes=[self selectedRowIndexes];
if ([selectedRowIndexes containsIndex:row]) {
selectedRowIndexes=[[[self selectedRowIndexes] mutableCopy] autorelease];
[selectedRowIndexes removeIndex:row];
[self setSelectedRowIndexes:selectedRowIndexes];
}
}
@ -641,7 +692,7 @@ NSString *NSTableViewColumnDidResizeNotification=@"NSTableViewColumnDidResizeNot
}
-(void)deselectAll:sender {
[_selectedRows removeAllObjects];
[self setSelectedRowIndexes:[NSIndexSet indexSet]];
[_selectedColumns removeAllObjects];
[self setNeedsDisplay:YES];
@ -737,6 +788,7 @@ NSString *NSTableViewColumnDidResizeNotification=@"NSTableViewColumnDidResizeNot
[self drawHighlightedSelectionForColumn:column row:row inRect:[self frameOfCellAtColumn:column row:row]];
}
-(void)drawRow:(int)row clipRect:(NSRect)clipRect {
// draw only visible columns.
NSRange visibleColumns = [self columnsInRect:clipRect];
@ -752,15 +804,17 @@ NSString *NSTableViewColumnDidResizeNotification=@"NSTableViewColumnDidResizeNot
NSRect cellRect = NSInsetRect([self frameOfCellAtColumn:drawThisColumn row:row], _intercellSpacing.width/2, _intercellSpacing.height/2);
if (!(row == _editedRow && drawThisColumn == _editedColumn)) {
[dataCell setObjectValue:[_dataSource tableView:self objectValueForTableColumn:column row:row]];
[dataCell setObjectValue:[self dataSourceObjectValueForTableColumn:column row:row]];
if ([dataCell type] == NSTextCellType) {
if ([self isRowSelected:row] || [self isColumnSelected:drawThisColumn])
[dataCell setTextColor:[NSColor selectedTextColor]];
else
[dataCell setTextColor:[NSColor textColor]];
[dataCell setTextColor:[NSColor textColor]];
}
[column prepareCell:dataCell inRow:row];
if ([_delegate respondsToSelector:@selector(tableView:willDisplayCell:forTableColumn:row:)])
[_delegate tableView:self willDisplayCell:dataCell forTableColumn:column row:row];
@ -1108,7 +1162,7 @@ NSString *NSTableViewColumnDidResizeNotification=@"NSTableViewColumnDidResizeNot
for (i = 0; i < _numberOfRows; ++i) {
NSCell *dataCell = [column dataCellForRow:i];
[dataCell setObjectValue:[_dataSource tableView:self objectValueForTableColumn:column row:i]];
[dataCell setObjectValue:[self dataSourceObjectValueForTableColumn:column row:i]];
width = [[dataCell attributedStringValue] size].width;
if (width > minWidth)
minWidth = width;

View File

@ -37,7 +37,7 @@ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLI
-(void)establishConnection
{
NSLog(@"binding between %@.%@ and %@.%@", [_source className], _binding, [_destination className], _keyPath);
//NSLog(@"binding between %@.%@ and %@.%@", [_source className], _binding, [_destination className], _keyPath);
[_source bind:_binding
toObject:_destination

View File

@ -35,7 +35,7 @@ static inline void setObjectForKey(NSMutableDictionary_mapTable *self,id object,
if (key==nil)
NSRaiseException(NSInvalidArgumentException,self,@selector(setObject:forKey:),@"Attempt to insert object with nil key");
else if(object==nil)
NSRaiseException(NSInvalidArgumentException,self,@selector(setObject:forKey:),@"Attempt to insert nil object");
NSRaiseException(NSInvalidArgumentException,self,@selector(setObject:forKey:),@"Attempt to insert nil object for key %@", key);
key=[key copy];

View File

@ -51,7 +51,7 @@ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLI
id obj;
while(obj=[en nextObject])
{
id val=[obj valueForKeyPath:key];
id val=[obj valueForKey:key];
if(!val)
val=[NSNull null];
[array addObject:val];
@ -151,9 +151,7 @@ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLI
{
return [NSNumber numberWithInt:[self count]];
}
@end
@implementation NSMutableArray (NSKeyValueCoding)
-(void)setValue:(id)value forKey:(NSString*)key
{
id en=[self objectEnumerator];
@ -176,3 +174,40 @@ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLI
@end
@implementation NSArray (KVO)
- (void)addObserver:(NSObject *)observer toObjectsAtIndexes:(NSIndexSet *)indexes forKeyPath:(NSString *)keyPath options:(NSKeyValueObservingOptions)options context:(void *)context
{
unsigned idx=[indexes firstIndex];
while(idx!=NSNotFound)
{
[[self objectAtIndex:idx] addObserver:observer
forKeyPath:keyPath
options:options
context:context];
idx=[indexes indexGreaterThanIndex:idx];
}
}
- (void)removeObserver:(NSObject *)observer fromObjectsAtIndexes:(NSIndexSet *)indexes forKeyPath:(NSString *)keyPath
{
unsigned idx=[indexes firstIndex];
while(idx!=NSNotFound)
{
[[self objectAtIndex:idx] removeObserver:observer
forKeyPath:keyPath];
idx=[indexes indexGreaterThanIndex:idx];
}
}
-(void)addObserver:(id)observer forKeyPath:(NSString*)keyPath options:(NSKeyValueObservingOptions)options context:(void*)context;
{
NSRaiseException(NSInvalidArgumentException,self,_cmd,@"not supported for key path %@ (observer was %@)", keyPath, observer);
}
-(void)removeObserver:(id)observer forKeyPath:(NSString*)keyPath;
{
NSRaiseException(NSInvalidArgumentException,self,_cmd,@"not supported for key path %@ (observer was %@)", keyPath, observer);
}
@end

View File

@ -12,6 +12,11 @@ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLI
{
return [self objectForKey:key];
}
-(void)setValue:(id)value forKey:(NSString*)key
{
[NSException raise:NSInvalidArgumentException format:@"%@ called on immutable dictionary %@", NSStringFromSelector(_cmd), self];
}
@end
@ -19,7 +24,10 @@ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLI
-(void)setValue:(id)value forKey:(NSString*)key
{
[self willChangeValueForKey:key];
[self setObject:value forKey:key];
if(value)
[self setObject:value forKey:key];
else
[self removeObjectForKey:key];
[self didChangeValueForKey:key];
}
@end

View File

@ -36,7 +36,7 @@ static BOOL CreateClassDefinition( const char * name, const char * superclassNam
#pragma mark -
#pragma mark KVO implementation
NSMutableDictionary *observationInfo=nil;
NSMutableDictionary *observationInfos=nil;
NSLock *kvoLock=nil;
@interface NSObject (KVOSettersForwardReferencs)
@ -47,26 +47,26 @@ NSLock *kvoLock=nil;
-(void*)observationInfo
{
return [[observationInfo objectForKey:[NSValue valueWithPointer:self]] pointerValue];
return [[observationInfos objectForKey:[NSValue valueWithPointer:self]] pointerValue];
}
-(void)setObservationInfo:(void*)info
{
if(!observationInfo)
observationInfo=[NSMutableDictionary new];
[observationInfo setObject:[NSValue valueWithPointer:info] forKey:[NSValue valueWithPointer:self]];
if(!observationInfos)
observationInfos=[NSMutableDictionary new];
[observationInfos setObject:[NSValue valueWithPointer:info] forKey:[NSValue valueWithPointer:self]];
}
+(void*)observationInfo
{
return [[observationInfo objectForKey:[NSValue valueWithPointer:self]] pointerValue];
return [[observationInfos objectForKey:[NSValue valueWithPointer:self]] pointerValue];
}
+(void)setObservationInfo:(void*)info
{
if(!observationInfo)
observationInfo=[NSMutableDictionary new];
[observationInfo setObject:[NSValue valueWithPointer:info] forKey:[NSValue valueWithPointer:self]];
if(!observationInfos)
observationInfos=[NSMutableDictionary new];
[observationInfos setObject:[NSValue valueWithPointer:info] forKey:[NSValue valueWithPointer:self]];
}
-(void)addObserver:(id)observer forKeyPath:(NSString*)keyPath options:(NSKeyValueObservingOptions)options context:(void*)context realKeyPath:(id)realKeyPath;
@ -159,6 +159,11 @@ NSLock *kvoLock=nil;
{
[dict removeObjectForKey:firstPart];
}
if(![dict count])
{
[dict release];
[self setObservationInfo:nil];
}
return;
}
}
@ -266,14 +271,16 @@ NSLock *kvoLock=nil;
{
id oldValue=[info oldValue];
if(oldValue)
[dict setObject:[info oldValue]
[dict setObject:oldValue
forKey:NSKeyValueChangeOldKey];
[info setOldValue:nil];
}
if(info->options & NSKeyValueObservingOptionNew)
{
[dict setObject:[self valueForKeyPath:keyPath]
forKey:NSKeyValueChangeNewKey];
id newValue=[self valueForKeyPath:keyPath];
if(newValue)
[dict setObject:newValue
forKey:NSKeyValueChangeNewKey];
}
[dict setObject:NSKeyValueChangeKindKey
forKey:NSKeyValueChangeKindKey];
@ -352,13 +359,23 @@ NSLock *kvoLock=nil;
// extracts key from selector called, calls original function
#define CHANGE_DECLARATION(type) CHANGE_DEFINE(type) \
{ \
NSString* sel=NSStringFromSelector(_cmd); \
NSString* key=[sel _KVC_setterKeyNameFromSelectorName]; \
const char* origName = sel_getName(_cmd); \
int selLen=strlen(origName); \
char *sel=alloca(selLen+1); \
strcpy(sel, origName); \
sel[selLen-1]='\0'; \
if(sel[0]=='_') \
sel+=4; \
else \
sel+=3; \
sel[0]=tolower(sel[0]); \
NSString *key=[[NSString alloc] initWithCString:sel]; \
[self willChangeValueForKey:key]; \
typedef id (*sender)(id obj, SEL selector, type value); \
sender implementation=(sender)[[self superclass] instanceMethodForSelector:_cmd]; \
*implementation(self, _cmd, value); \
[self didChangeValueForKey:key]; \
[key release]; \
}
@ -509,7 +526,7 @@ CHANGE_DECLARATION(SEL)
SEL kvoSelector=nil;
// current method is a setter?
if([methodName hasPrefix:@"set"] &&
if(([methodName hasPrefix:@"set"] || [methodName hasPrefix:@"_set"]) &&
[[self methodSignatureForSelector:method->method_name] numberOfArguments]==3 &&
[[self class] automaticallyNotifiesObserversForKey:[methodName _KVC_setterKeyNameFromSelectorName]])
{
@ -542,7 +559,6 @@ CHANGE_DECLARATION(SEL)
// if(kvoSelector==0)
// NSLog(@"type %s not defined in %s:%i (selector %s on class %@)", firstParameterType, __FILE__, __LINE__, SELNAME(method->method_name), [self className]);
}
// there's a suitable selector for us
if(kvoSelector!=0)

View File

@ -26,6 +26,11 @@ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLI
return shared;
}
-(id)copyWithZone:(NSZone*)zone
{
return self;
}
-init {
return self;
}