From 2320adf46b5fc0f6077da8c6203f4519ba0de23d Mon Sep 17 00:00:00 2001 From: fredkiefer Date: Mon, 12 Mar 2012 12:24:17 +0000 Subject: [PATCH] * Headers/AppKit/NSKeyValueBinding.h, * Source/externs.m: Additional binding names. * Source/GSBindingHelpers.h, * Source/NSKeyValueBinding.m: Additional helper methods. * Source/NSObjectController.m: Use helper methods. * Source/NSArrayController.m: Signal when the arranged objects change. * Source/NSTableColumn.m, * Source/NSTableView.m: First attempt at support for content binding. git-svn-id: svn+ssh://svn.gna.org/svn/gnustep/libs/gui/trunk@34923 72102866-910b-0410-8b05-ffd578937521 --- ChangeLog | 11 +++ Headers/AppKit/NSKeyValueBinding.h | 3 + Source/GSBindingHelpers.h | 3 + Source/NSArrayController.m | 35 +++++-- Source/NSKeyValueBinding.m | 56 ++++++----- Source/NSObjectController.m | 53 +++++------ Source/NSTableColumn.m | 55 +++++++++-- Source/NSTableView.m | 146 +++++++++++++++++++++++++---- Source/externs.m | 3 + 9 files changed, 282 insertions(+), 83 deletions(-) diff --git a/ChangeLog b/ChangeLog index b77134658..ba25024a6 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,3 +1,14 @@ +2012-03-12 Fred Kiefer + + * Headers/AppKit/NSKeyValueBinding.h, + * Source/externs.m: Additional binding names. + * Source/GSBindingHelpers.h, + * Source/NSKeyValueBinding.m: Additional helper methods. + * Source/NSObjectController.m: Use helper methods. + * Source/NSArrayController.m: Signal when the arranged objects change. + * Source/NSTableColumn.m, + * Source/NSTableView.m: First attempt at support for content binding. + 2012-03-09 Eric Wasylishen * Source/NSTableView.m: diff --git a/Headers/AppKit/NSKeyValueBinding.h b/Headers/AppKit/NSKeyValueBinding.h index 88245c6bc..a3aa597a3 100644 --- a/Headers/AppKit/NSKeyValueBinding.h +++ b/Headers/AppKit/NSKeyValueBinding.h @@ -103,6 +103,8 @@ APPKIT_EXPORT id NSNotApplicableMarker; // Binding name constants APPKIT_EXPORT NSString *NSAlignmentBinding; +APPKIT_EXPORT NSString *NSContentArrayBinding; +APPKIT_EXPORT NSString *NSContentBinding; APPKIT_EXPORT NSString *NSContentObjectBinding; APPKIT_EXPORT NSString *NSEditableBinding; APPKIT_EXPORT NSString *NSEnabledBinding; @@ -113,6 +115,7 @@ APPKIT_EXPORT NSString *NSHiddenBinding; APPKIT_EXPORT NSString *NSSelectedIndexBinding; APPKIT_EXPORT NSString *NSSelectedObjectBinding; APPKIT_EXPORT NSString *NSSelectedTagBinding; +APPKIT_EXPORT NSString *NSSelectionIndexesBinding; APPKIT_EXPORT NSString *NSTextColorBinding; APPKIT_EXPORT NSString *NSTitleBinding; APPKIT_EXPORT NSString *NSToolTipBinding; diff --git a/Source/GSBindingHelpers.h b/Source/GSBindingHelpers.h index 878f4eedd..5fdbc66f2 100644 --- a/Source/GSBindingHelpers.h +++ b/Source/GSBindingHelpers.h @@ -59,7 +59,10 @@ options: (NSDictionary *)options fromObject: (id)source; - (void) setValueFor: (NSString *)binding; +- (void) reverseSetValue: (id)value; - (void) reverseSetValueFor: (NSString *)binding; +- (id) destinationValue; +- (id) sourceValueFor: (NSString *)binding; /* Transforms the value with a value transformer, if specified and available, * and takes care of any placeholders diff --git a/Source/NSArrayController.m b/Source/NSArrayController.m index 7ad7a8750..1679e1374 100644 --- a/Source/NSArrayController.m +++ b/Source/NSArrayController.m @@ -31,6 +31,7 @@ #import #import #import +#import #import #import #import @@ -46,7 +47,7 @@ { if (self == [NSArrayController class]) { - [self exposeBinding: @"contentArray"]; + [self exposeBinding: NSContentArrayBinding]; } } @@ -54,7 +55,7 @@ { if ((self = [super initWithContent: content]) != nil) { - _arranged_objects = [content copy]; + [self rearrangeObjects]; [self setSelectsInsertedObjects: YES]; } @@ -124,6 +125,12 @@ return YES; } +- (void) setContent: (id)content +{ + [super setContent: content]; + [self rearrangeObjects]; +} + - (void) insert: (id)sender { id new = [self newObject]; @@ -334,7 +341,9 @@ - (void) rearrangeObjects { + [self willChangeValueForKey: @"arrangedObjects"]; ASSIGN(_arranged_objects, [self arrangeObjects: _content]); + [self didChangeValueForKey: @"arrangedObjects"]; } - (void) setSortDescriptors: (NSArray*)desc @@ -360,15 +369,15 @@ - (void) insertObject: (id)obj atArrangedObjectIndex: (NSUInteger)idx { - // TODO - return; + // FIXME + [self addObject: obj]; } - (void) insertObjects: (NSArray*)obj atArrangedObjectIndexes: (NSIndexSet*)idx { - // TODO - return; + // FIXME + [self addObjects: obj]; } - (void) removeObjectAtArrangedObjectIndex: (NSUInteger)idx @@ -386,7 +395,7 @@ atArrangedObjectIndexes: (NSIndexSet*)idx withKeyPath: (NSString *)keyPath options: (NSDictionary *)options { - if ([binding isEqual: @"contentArray"]) + if ([binding isEqual: NSContentArrayBinding]) { GSKeyValueBinding *kvb; @@ -409,6 +418,18 @@ atArrangedObjectIndexes: (NSIndexSet*)idx } } +- (Class) valueClassForBinding: (NSString *)binding +{ + if ([binding isEqual: NSContentArrayBinding]) + { + return [NSArray class]; + } + else + { + return [super valueClassForBinding: binding]; + } +} + - (void) encodeWithCoder: (NSCoder *)coder { [super encodeWithCoder: coder]; diff --git a/Source/NSKeyValueBinding.m b/Source/NSKeyValueBinding.m index 383726479..b0e7e07d3 100644 --- a/Source/NSKeyValueBinding.m +++ b/Source/NSKeyValueBinding.m @@ -48,7 +48,7 @@ + (void) exposeBinding: (NSString *)binding { - [GSKeyValueBinding exposeBinding: binding forClass: [self class]]; + [GSKeyValueBinding exposeBinding: binding forClass: [self class]]; } - (NSArray *) exposedBindings @@ -76,10 +76,10 @@ return [NSString class]; } -- (void)bind: (NSString *)binding - toObject: (id)anObject - withKeyPath: (NSString *)keyPath - options: (NSDictionary *)options +- (void) bind: (NSString *)binding + toObject: (id)anObject + withKeyPath: (NSString *)keyPath + options: (NSDictionary *)options { if ((anObject == nil) || (keyPath == nil)) @@ -319,7 +319,7 @@ void GSBindingInvokeAction(NSString *targetKey, NSString *argumentKey, [super dealloc]; } -- (void) setValueFor: (NSString *)binding +- (id) destinationValue { id newValue; id dest; @@ -329,26 +329,38 @@ void GSBindingInvokeAction(NSString *targetKey, NSString *argumentKey, dest = [info objectForKey: NSObservedObjectKey]; keyPath = [info objectForKey: NSObservedKeyPathKey]; options = [info objectForKey: NSOptionsKey]; - newValue = [dest valueForKeyPath: keyPath]; - newValue = [self transformValue: newValue withOptions: options]; - [src setValue: newValue forKey: binding]; + return [self transformValue: newValue withOptions: options]; +} + +- (id) sourceValueFor: (NSString *)binding +{ + id newValue; + NSDictionary *options; + + options = [info objectForKey: NSOptionsKey]; + newValue = [src valueForKeyPath: binding]; + return [self reverseTransformValue: newValue withOptions: options]; +} + +- (void) setValueFor: (NSString *)binding +{ + [src setValue: [self destinationValue] forKey: binding]; +} + +- (void) reverseSetValue: (id)value +{ + NSString *keyPath; + id dest; + + keyPath = [info objectForKey: NSObservedKeyPathKey]; + dest = [info objectForKey: NSObservedObjectKey]; + [dest setValue: value forKeyPath: keyPath]; } - (void) reverseSetValueFor: (NSString *)binding { - id newValue; - id dest; - NSString *keyPath; - NSDictionary *options; - - dest = [info objectForKey: NSObservedObjectKey]; - keyPath = [info objectForKey: NSObservedKeyPathKey]; - options = [info objectForKey: NSOptionsKey]; - - newValue = [src valueForKeyPath: binding]; - newValue = [self reverseTransformValue: newValue withOptions: options]; - [dest setValue: newValue forKeyPath: keyPath]; + [self reverseSetValue: [self sourceValueFor: binding]]; } - (void) observeValueForKeyPath: (NSString *)keyPath @@ -508,7 +520,7 @@ void GSBindingInvokeAction(NSString *targetKey, NSString *argumentKey, if (!objectTable) return; - [bindingLock lock]; + [bindingLock lock]; bindings = (NSDictionary *)NSMapGet(objectTable, (void *)src); if (!bindings) return; diff --git a/Source/NSObjectController.m b/Source/NSObjectController.m index 8caf6811d..2c099f9fb 100644 --- a/Source/NSObjectController.m +++ b/Source/NSObjectController.m @@ -201,19 +201,22 @@ - (void) setContent: (id)content { - NSMutableArray *selection; + if (content != _content) + { + NSMutableArray *selection; - ASSIGN(_content, content); - if (content) - { - selection = [[NSMutableArray alloc] initWithObjects: &content count: 1]; + ASSIGN(_content, content); + if (content) + { + selection = [[NSMutableArray alloc] initWithObjects: &content count: 1]; + } + else + { + selection = [[NSMutableArray alloc] init]; + } + ASSIGN(_selection, selection); + RELEASE(selection); } - else - { - selection = [[NSMutableArray alloc] init]; - } - ASSIGN(_selection, selection); - RELEASE(selection); } - (void)bind: (NSString *)binding @@ -221,7 +224,7 @@ withKeyPath: (NSString *)keyPath options: (NSDictionary *)options { - if ([binding isEqual:NSContentObjectBinding]) + if ([binding isEqual: NSContentObjectBinding]) { GSKeyValueBinding *kvb; @@ -290,16 +293,13 @@ - (void) addObject: (id)obj { - NSDictionary * bindingInfo = [self infoForBinding: NSContentObjectBinding]; + GSKeyValueBinding *theBinding; [self setContent: obj]; - if (bindingInfo) - { - // Change the relationship of the object that our content is bound to. - id masterObject = [bindingInfo objectForKey: NSObservedObjectKey]; - NSString * keyPath = [bindingInfo objectForKey: NSObservedKeyPathKey]; - [masterObject setValue: obj forKeyPath: keyPath]; - } + theBinding = [GSKeyValueBinding getBinding: NSContentObjectBinding + forObject: self]; + if (theBinding != nil) + [theBinding reverseSetValueFor: @"content"]; } - (void) remove: (id)sender @@ -314,16 +314,13 @@ { if (obj == [self content]) { - NSDictionary * bindingInfo = [self infoForBinding: NSContentObjectBinding]; + GSKeyValueBinding *theBinding; [self setContent: nil]; - if (bindingInfo) - { - // Change the relationship of the object that our content is bound to. - id masterObject = [bindingInfo objectForKey: NSObservedObjectKey]; - NSString * keyPath = [bindingInfo objectForKey: NSObservedKeyPathKey]; - [masterObject setValue: nil forKeyPath: keyPath]; - } + theBinding = [GSKeyValueBinding getBinding: NSContentObjectBinding + forObject: self]; + if (theBinding != nil) + [theBinding reverseSetValueFor: @"content"]; } } diff --git a/Source/NSTableColumn.m b/Source/NSTableColumn.m index 4f05cdedc..38a59e9cf 100644 --- a/Source/NSTableColumn.m +++ b/Source/NSTableColumn.m @@ -63,12 +63,15 @@ */ #import +#import #import #import #import +#import "AppKit/NSKeyValueBinding.h" #import "AppKit/NSTableHeaderCell.h" #import "AppKit/NSTableColumn.h" #import "AppKit/NSTableView.h" +#import "GSBindingHelpers.h" /**

@@ -85,7 +88,11 @@ + (void) initialize { if (self == [NSTableColumn class]) - [self setVersion: 3]; + { + [self setVersion: 3]; + [self exposeBinding: NSValueBinding]; + [self exposeBinding: NSEnabledBinding]; + } } /* @@ -127,11 +134,14 @@ - (void) dealloc { - RELEASE (_headerCell); - RELEASE (_headerToolTip); - RELEASE (_dataCell); - RELEASE (_sortDescriptorPrototype); - TEST_RELEASE (_identifier); + // Remove all key value bindings for this view. + [GSKeyValueBinding unbindAllForObject: self]; + + RELEASE(_headerCell); + RELEASE(_headerToolTip); + RELEASE(_dataCell); + RELEASE(_sortDescriptorPrototype); + TEST_RELEASE(_identifier); [super dealloc]; } @@ -599,4 +609,37 @@ to YES. */ return self; } +- (void) setValue: (id)anObject forKey: (NSString*)aKey +{ + if ([aKey isEqual: NSValueBinding]) + { + // FIXME + // Reload data + } + else if ([aKey isEqual: NSEnabledBinding]) + { + // FIXME + } + else + { + [super setValue: anObject forKey: aKey]; + } +} + +- (id) valueForKey: (NSString*)aKey +{ + if ([aKey isEqual: NSValueBinding]) + { + return nil; + } + else if ([aKey isEqual: NSEnabledBinding]) + { + // FIXME + return [NSNumber numberWithBool: YES]; + } + else + { + return [super valueForKey: aKey]; + } +} @end diff --git a/Source/NSTableView.m b/Source/NSTableView.m index 8895e5fab..3abcc5a8f 100644 --- a/Source/NSTableView.m +++ b/Source/NSTableView.m @@ -37,6 +37,7 @@ #import #import #import +#import #import #import #import @@ -52,6 +53,7 @@ #import "AppKit/NSEvent.h" #import "AppKit/NSImage.h" #import "AppKit/NSGraphics.h" +#import "AppKit/NSKeyValueBinding.h" #import "AppKit/NSScroller.h" #import "AppKit/NSScrollView.h" #import "AppKit/NSTableColumn.h" @@ -65,6 +67,7 @@ #import "AppKit/NSDragging.h" #import "AppKit/NSCustomImageRep.h" #import "GNUstepGUI/GSTheme.h" +#import "GSBindingHelpers.h" #include static NSNotificationCenter *nc = nil; @@ -1992,6 +1995,9 @@ static void computeNewSelection { [self setVersion: currentVersion]; nc = [NSNotificationCenter defaultCenter]; + // FIXME + [self exposeBinding: NSContentBinding]; + [self exposeBinding: NSSelectionIndexesBinding]; } } @@ -2296,22 +2302,31 @@ static void computeNewSelection const SEL sel_a = @selector (numberOfRowsInTableView:); const SEL sel_b = @selector (tableView:objectValueForTableColumn:row:); const SEL sel_c = @selector(tableView:setObjectValue:forTableColumn:row:); - if (anObject && [anObject respondsToSelector: sel_a] == NO) - { - [NSException - raise: NSInternalInconsistencyException - format: @"Data Source doesn't respond to numberOfRowsInTableView:"]; + GSKeyValueBinding *theBinding; + + // If we have content binding the data source is used only + // like a delegate + theBinding = [GSKeyValueBinding getBinding: NSContentBinding + forObject: self]; + if (theBinding == nil) + { + if (anObject && [anObject respondsToSelector: sel_a] == NO) + { + [NSException + raise: NSInternalInconsistencyException + format: @"Data Source doesn't respond to numberOfRowsInTableView:"]; + } + + if (anObject && [anObject respondsToSelector: sel_b] == NO) + { + /* This method isn't required. + [NSException raise: NSInternalInconsistencyException + format: @"Data Source doesn't respond to " + @"tableView:objectValueForTableColumn:row:"]; + */ + } } - - if (anObject && [anObject respondsToSelector: sel_b] == NO) - { -/* This method isn't required. - [NSException raise: NSInternalInconsistencyException - format: @"Data Source doesn't respond to " - @"tableView:objectValueForTableColumn:row:"]; -*/ - } - + _dataSource_editable = [anObject respondsToSelector: sel_c]; /* We do *not* retain the dataSource, it's like a delegate */ @@ -2731,7 +2746,7 @@ byExtendingSelection: (BOOL)flag * This is not just a speed up, it prevents us from sending * a NSTableViewSelectionDidChangeNotification. * This behaviour is required by the specifications */ - if ([_selectedColumns isEqualToIndexSet: indexes]) + if ([_selectedColumns isEqual: indexes]) { if (!empty) { @@ -2814,7 +2829,7 @@ byExtendingSelection: (BOOL)flag * This is not just a speed up, it prevents us from sending * a NSTableViewSelectionDidChangeNotification. * This behaviour is required by the specifications */ - if ([_selectedRows isEqualToIndexSet: indexes]) + if ([_selectedRows isEqual: indexes]) { if (!empty) { @@ -3892,7 +3907,7 @@ if (currentRow >= 0 && currentRow < _numberOfRows) \ if (startedPeriodicEvents == YES) [NSEvent stopPeriodicEvents]; - if (![_selectedRows isEqualToIndexSet: oldSelectedRows]) + if (![_selectedRows isEqual: oldSelectedRows]) { [self _postSelectionDidChangeNotification]; } @@ -6576,8 +6591,16 @@ For a more detailed explanation, -setSortDescriptors:. */ row: (int) index { id result = nil; + GSKeyValueBinding *theBinding; - if ([_dataSource respondsToSelector: + theBinding = [GSKeyValueBinding getBinding: NSValueBinding + forObject: tb]; + if (theBinding != nil) + { + return [(NSArray *)[theBinding sourceValueFor: NSValueBinding] + objectAtIndex: index]; + } + else if ([_dataSource respondsToSelector: @selector(tableView:objectValueForTableColumn:row:)]) { result = [_dataSource tableView: self @@ -6608,7 +6631,26 @@ For a more detailed explanation, -setSortDescriptors:. */ */ - (int) _numRows { - return [_dataSource numberOfRowsInTableView:self]; + GSKeyValueBinding *theBinding; + + // If we have content binding the data source is used only + // like a delegate + theBinding = [GSKeyValueBinding getBinding: NSContentBinding + forObject: self]; + if (theBinding != nil) + { + return [(NSArray *)[theBinding sourceValueFor: NSContentBinding] count]; + } + else if ([_dataSource respondsToSelector: + @selector(numberOfRowsInTableView:)]) + { + return [_dataSource numberOfRowsInTableView:self]; + } + else + { + // FIXME + return 0; + } } - (BOOL) _isDraggingSource @@ -6766,4 +6808,68 @@ For a more detailed explanation, -setSortDescriptors:. */ _selectedColumn = -1; } +- (void) setValue: (id)anObject forKey: (NSString*)aKey +{ + if ([aKey isEqual: NSContentBinding]) + { + // Reload data + [self reloadData]; + NSLog(@"Setting TV content to %@", anObject); + } + else if ([aKey isEqual: NSSelectionIndexesBinding]) + { + if (_selectingColumns) + { + if (nil == anObject) + { + [self _unselectAllColumns]; + } + else + { + return [self selectColumnIndexes: anObject + byExtendingSelection: NO]; + } + } + else + { + if (nil == anObject) + { + [self _unselectAllRows]; + } + else + { + return [self selectRowIndexes: anObject + byExtendingSelection: NO]; + } + } + } + else + { + [super setValue: anObject forKey: aKey]; + } +} + +- (id) valueForKey: (NSString*)aKey +{ + if ([aKey isEqual: NSContentBinding]) + { + return nil; + } + else if ([aKey isEqual: NSSelectionIndexesBinding]) + { + if (_selectingColumns) + { + return [self selectedColumnIndexes]; + } + else + { + return [self selectedRowIndexes]; + } + } + else + { + return [super valueForKey: aKey]; + } +} + @end diff --git a/Source/externs.m b/Source/externs.m index 8f026f33d..c08643c67 100644 --- a/Source/externs.m +++ b/Source/externs.m @@ -676,6 +676,8 @@ NSString *NSValueTransformerNameBindingOption = @"NSValueTransformerName"; NSString *NSValueTransformerBindingOption = @"NSValueTransformer"; NSString *NSAlignmentBinding = @"alignment"; +NSString *NSContentArrayBinding = @"contentArray"; +NSString *NSContentBinding = @"content"; NSString *NSContentObjectBinding = @"contentObject"; NSString *NSEditableBinding = @"editable"; NSString *NSEnabledBinding = @"enabled"; @@ -686,6 +688,7 @@ NSString *NSHiddenBinding = @"hidden"; NSString *NSSelectedIndexBinding = @"selectedIndex"; NSString *NSSelectedObjectBinding = @"selectedObject"; NSString *NSSelectedTagBinding = @"selectedTag"; +NSString *NSSelectionIndexesBinding = @"selectionIndexes"; NSString *NSTextColorBinding = @"textColor"; NSString *NSTitleBinding = @"title"; NSString *NSToolTipBinding = @"toolTip";