libs-gui/Source/NSArrayController.m
2024-07-16 18:03:30 -04:00

670 lines
15 KiB
Objective-C

/** <title>NSArrayController</title>
<abstract>Controller class for arrays</abstract>
Copyright <copy>(C) 2006, 2020 Free Software Foundation, Inc.</copy>
Author: Fred Kiefer <fredkiefer@gmx.de>
Date: June 2006
This file is part of the GNUstep GUI Library.
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public
License as published by the Free Software Foundation; either
version 2 of the License, or (at your option) any later version.
This library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public
License along with this library; see the file COPYING.LIB.
If not, see <http://www.gnu.org/licenses/> or write to the
Free Software Foundation, 51 Franklin Street, Fifth Floor,
Boston, MA 02110-1301, USA.
*/
#import <Foundation/NSArchiver.h>
#import <Foundation/NSArray.h>
#import <Foundation/NSDictionary.h>
#import <Foundation/NSIndexSet.h>
#import <Foundation/NSKeyValueObserving.h>
#import <Foundation/NSPredicate.h>
#import <Foundation/NSSortDescriptor.h>
#import <Foundation/NSString.h>
#import "AppKit/NSArrayController.h"
#import "AppKit/NSKeyValueBinding.h"
#import "GSBindingHelpers.h"
#import "GSFastEnumeration.h"
@implementation GSObservableArray
- (id) initWithArray: (NSArray *)array
{
self = [super init];
if (self)
{
ASSIGN(_array, array);
}
return self;
}
- (void) dealloc
{
RELEASE(_array);
[super dealloc];
}
- (NSUInteger) count
{
return [_array count];
}
- (NSUInteger) indexOfObject: (id)anObject
{
return [_array indexOfObject: anObject];
}
- (id) objectAtIndex: (NSUInteger)index
{
return [_array objectAtIndex: index];
}
- (NSArray *) objectsAtIndexes: (NSIndexSet *)indexes
{
NSArray *result = [_array objectsAtIndexes: indexes];
return AUTORELEASE([[GSObservableArray alloc]
initWithArray: result]);
}
- (id) valueForKey: (NSString*)key
{
id result = [_array valueForKey: key];
if ([result isKindOfClass: [NSArray class]])
{
// FIXME: Using the correct memory management here
// Leads to an issue inside of KVO. For now we leak the
// object until this gets fixed.
//return AUTORELEASE([[GSObservableArray alloc]
return ([[GSObservableArray alloc]
initWithArray: result]);
}
return result;
}
- (NSArray*) arrayByAddingObject: (id)anObject
{
NSArray * result = [_array arrayByAddingObject: anObject];
return AUTORELEASE([[GSObservableArray alloc]
initWithArray: result]);
}
- (NSArray*) arrayByAddingObjectsFromArray: (NSArray*)anotherArray
{
NSArray * result = [_array arrayByAddingObjectsFromArray: anotherArray];
return AUTORELEASE([[GSObservableArray alloc]
initWithArray: result]);
}
- (void) addObserver: (NSObject*)anObserver
forKeyPath: (NSString*)aPath
options: (NSKeyValueObservingOptions)options
context: (void*)aContext
{
NSIndexSet *indexes = [NSIndexSet indexSetWithIndexesInRange: NSMakeRange(0, [self count])];
[self addObserver: anObserver
toObjectsAtIndexes: indexes
forKeyPath: aPath
options: options
context: aContext];
}
- (void) removeObserver: (NSObject*)anObserver forKeyPath: (NSString*)aPath
{
NSIndexSet *indexes = [NSIndexSet indexSetWithIndexesInRange: NSMakeRange(0, [self count])];
[self removeObserver: anObserver
fromObjectsAtIndexes: indexes
forKeyPath: aPath];
}
@end
@implementation NSArrayController
+ (void) initialize
{
if (self == [NSArrayController class])
{
[self exposeBinding: NSContentArrayBinding];
[self exposeBinding: NSSelectionIndexesBinding];
[self setKeys: [NSArray arrayWithObjects: NSContentBinding, NSContentObjectBinding, nil]
triggerChangeNotificationsForDependentKey: @"arrangedObjects"];
}
}
- (id) initWithContent: (id)content
{
if ((self = [super initWithContent: content]) != nil)
{
[self setSelectsInsertedObjects: YES];
}
return self;
}
- (id) init
{
NSMutableArray *new = [[NSMutableArray alloc] init];
self = [self initWithContent: new];
RELEASE(new);
return self;
}
- (void) dealloc
{
DESTROY(_arranged_objects);
DESTROY(_selection_indexes);
DESTROY(_sort_descriptors);
DESTROY(_filter_predicate);
[super dealloc];
}
- (void) addObject: (id)obj
{
[self willChangeValueForKey: NSContentBinding];
[_content addObject: obj];
if ([self automaticallyRearrangesObjects])
{
[self rearrangeObjects];
}
else
{
DESTROY(_arranged_objects);
}
if ([self selectsInsertedObjects])
{
[self addSelectedObjects: [NSArray arrayWithObject: obj]];
}
[self didChangeValueForKey: NSContentBinding];
}
- (void) addObjects: (NSArray*)obj
{
[self willChangeValueForKey: NSContentBinding];
[_content addObjectsFromArray: obj];
if ([self automaticallyRearrangesObjects])
{
[self rearrangeObjects];
}
else
{
DESTROY(_arranged_objects);
}
if ([self selectsInsertedObjects])
{
[self addSelectedObjects: obj];
}
[self didChangeValueForKey: NSContentBinding];
}
- (void) removeObject: (id)obj
{
[self willChangeValueForKey: NSContentBinding];
[_content removeObject: obj];
[self removeSelectedObjects: [NSArray arrayWithObject: obj]];
if ([self automaticallyRearrangesObjects])
{
[self rearrangeObjects];
}
else
{
DESTROY(_arranged_objects);
}
[self didChangeValueForKey: NSContentBinding];
}
- (void) removeObjects: (NSArray*)obj
{
[self willChangeValueForKey: NSContentBinding];
[_content removeObjectsInArray: obj];
[self removeSelectedObjects: obj];
if ([self automaticallyRearrangesObjects])
{
[self rearrangeObjects];
}
else
{
DESTROY(_arranged_objects);
}
[self didChangeValueForKey: NSContentBinding];
}
- (BOOL) canInsert
{
return YES;
}
- (void) setContent: (id)content
{
[super setContent: content];
[self rearrangeObjects];
}
- (void) insert: (id)sender
{
id new = [self newObject];
[self addObject: new];
RELEASE(new);
}
- (NSIndexSet*) _indexSetForObjects: (NSArray*)objects
{
NSMutableIndexSet *tmp = [NSMutableIndexSet new];
id<NSFastEnumeration> enumerator = objects;
FOR_IN (id, obj, enumerator)
{
NSUInteger index = [_arranged_objects indexOfObject: obj];
if (NSNotFound != index)
{
[tmp addIndex: index];
}
}
END_FOR_IN(enumerator)
return AUTORELEASE(tmp);
}
- (BOOL) addSelectedObjects: (NSArray*)obj
{
return [self addSelectionIndexes: [self _indexSetForObjects: obj]];
}
- (BOOL) addSelectionIndexes: (NSIndexSet*)idx
{
NSMutableIndexSet *tmp = AUTORELEASE([_selection_indexes mutableCopy]);
[tmp addIndexes: idx];
return [self setSelectionIndexes: tmp];
}
- (BOOL) setSelectedObjects: (NSArray*)obj
{
return [self setSelectionIndexes: [self _indexSetForObjects: obj]];
}
- (BOOL) setSelectionIndex: (NSUInteger)idx
{
return [self setSelectionIndexes:
[NSIndexSet indexSetWithIndex: idx]];
}
- (BOOL) setSelectionIndexes: (NSIndexSet*)idx
{
if ([_selection_indexes isEqual: idx])
{
return NO;
}
else
{
ASSIGNCOPY(_selection_indexes, idx);
return YES;
}
}
- (BOOL) removeSelectedObjects: (NSArray*)obj
{
return [self removeSelectionIndexes: [self _indexSetForObjects: obj]];
}
- (BOOL) removeSelectionIndexes: (NSIndexSet*)idx
{
NSMutableIndexSet *tmp = AUTORELEASE([_selection_indexes mutableCopy]);
[tmp removeIndexes: idx];
return [self setSelectionIndexes: tmp];
}
- (BOOL) canSelectNext
{
NSUInteger cur = [self selectionIndex];
if ((cur == NSNotFound) || (cur + 1 == [_content count]))
{
return NO;
}
else
{
return YES;
}
}
- (BOOL) canSelectPrevious
{
NSUInteger cur = [self selectionIndex];
if ((cur == NSNotFound) || (cur == 0))
{
return NO;
}
else
{
return YES;
}
}
- (void) selectNext: (id)sender
{
NSUInteger cur = [self selectionIndex];
[self setSelectionIndexes:
[NSIndexSet indexSetWithIndex: cur + 1]];
}
- (void) selectPrevious: (id)sender
{
NSUInteger cur = [self selectionIndex];
[self setSelectionIndexes:
[NSIndexSet indexSetWithIndex: cur - 1]];
}
- (NSArray*) selectedObjects
{
// We make the selection work on the arranged objects
return [[self arrangedObjects] objectsAtIndexes: _selection_indexes];
}
- (NSUInteger) selectionIndex
{
return [_selection_indexes firstIndex];
}
- (NSIndexSet*) selectionIndexes
{
return AUTORELEASE([_selection_indexes copy]);
}
- (BOOL) avoidsEmptySelection
{
return _acflags.avoids_empty_selection;
}
- (void) setAvoidsEmptySelection: (BOOL)flag
{
_acflags.avoids_empty_selection = flag;
}
- (BOOL) preservesSelection
{
return _acflags.preserves_selection;
}
- (void) setPreservesSelection: (BOOL)flag
{
_acflags.preserves_selection = flag;
}
- (BOOL) alwaysUsesMultipleValuesMarker
{
return _acflags.always_uses_multiple_values_marker;
}
- (void) setAlwaysUsesMultipleValuesMarker: (BOOL)flag
{
_acflags.always_uses_multiple_values_marker = flag;
}
- (BOOL) clearsFilterPredicateOnInsertion
{
return _acflags.clears_filter_predicate_on_insertion;
}
- (void) setClearsFilterPredicateOnInsertion: (BOOL)flag
{
_acflags.clears_filter_predicate_on_insertion = flag;
}
- (BOOL) automaticallyRearrangesObjects
{
return _acflags.automatically_rearranges_objects;
}
- (void) setAutomaticallyRearrangesObjects: (BOOL)flag
{
_acflags.automatically_rearranges_objects = flag;
}
- (BOOL) selectsInsertedObjects
{
return _acflags.selects_inserted_objects;
}
- (void) setSelectsInsertedObjects: (BOOL)flag
{
_acflags.selects_inserted_objects = flag;
}
- (NSArray*) arrangeObjects: (NSArray*)obj
{
NSArray *temp = obj;
if (_filter_predicate != nil)
{
temp = [obj filteredArrayUsingPredicate: _filter_predicate];
}
return [temp sortedArrayUsingDescriptors: _sort_descriptors];
}
- (id) arrangedObjects
{
if (_arranged_objects == nil)
{
[self rearrangeObjects];
}
return _arranged_objects;
}
- (void) rearrangeObjects
{
[self willChangeValueForKey: @"arrangedObjects"];
DESTROY(_arranged_objects);
_arranged_objects = [[GSObservableArray alloc]
initWithArray: [self arrangeObjects: _content]];
[self didChangeValueForKey: @"arrangedObjects"];
}
- (void) setSortDescriptors: (NSArray*)desc
{
ASSIGNCOPY(_sort_descriptors, desc);
}
- (NSArray*) sortDescriptors
{
return AUTORELEASE([_sort_descriptors copy]);
}
- (void) setFilterPredicate: (NSPredicate*)filterPredicate
{
ASSIGN(_filter_predicate, filterPredicate);
}
- (NSPredicate*) filterPredicate
{
return _filter_predicate;
}
- (void) insertObject: (id)obj
atArrangedObjectIndex: (NSUInteger)idx
{
// FIXME
[self addObject: obj];
}
- (void) insertObjects: (NSArray*)obj
atArrangedObjectIndexes: (NSIndexSet*)idx
{
// FIXME
[self addObjects: obj];
}
- (void) removeObjectAtArrangedObjectIndex: (NSUInteger)idx
{
[self removeObject: [_arranged_objects objectAtIndex: idx]];
}
- (void) removeObjectsAtArrangedObjectIndexes: (NSIndexSet*)idx
{
[self removeObjects: [_arranged_objects objectsAtIndexes: idx]];
}
- (void) bind: (NSString *)binding
toObject: (id)anObject
withKeyPath: (NSString *)keyPath
options: (NSDictionary *)options
{
if ([binding isEqual: NSContentArrayBinding])
{
GSKeyValueBinding *kvb;
[self unbind: binding];
kvb = [[GSKeyValueBinding alloc] initWithBinding: @"content"
withName: binding
toObject: anObject
withKeyPath: keyPath
options: options
fromObject: self];
// The binding will be retained in the binding table
RELEASE(kvb);
}
else
{
[super bind: binding
toObject: anObject
withKeyPath: keyPath
options: options];
}
}
- (Class) valueClassForBinding: (NSString *)binding
{
if ([binding isEqual: NSContentArrayBinding])
{
return [NSArray class];
}
else
{
return [super valueClassForBinding: binding];
}
}
- (void) encodeWithCoder: (NSCoder *)coder
{
[super encodeWithCoder: coder];
if ([coder allowsKeyedCoding])
{
[coder encodeBool: [self avoidsEmptySelection]
forKey: @"NSAvoidsEmptySelection"];
[coder encodeBool: [self preservesSelection]
forKey: @"NSPreservesSelection"];
[coder encodeBool: [self selectsInsertedObjects]
forKey: @"NSSelectsInsertedObjects"];
[coder encodeBool: [self clearsFilterPredicateOnInsertion]
forKey: @"NSClearsFilterPredicateOnInsertion"];
[coder encodeBool: [self automaticallyRearrangesObjects]
forKey: @"NSAutomaticallyRearrangesObjects"];
}
else
{
BOOL f;
f = _acflags.avoids_empty_selection;
[coder encodeValueOfObjCType: @encode(BOOL)
at: &f];
f = _acflags.preserves_selection;
[coder encodeValueOfObjCType: @encode(BOOL)
at: &f];
f = _acflags.selects_inserted_objects;
[coder encodeValueOfObjCType: @encode(BOOL)
at: &f];
f = _acflags.clears_filter_predicate_on_insertion;
[coder encodeValueOfObjCType: @encode(BOOL)
at: &f];
f = _acflags.automatically_rearranges_objects;
[coder encodeValueOfObjCType: @encode(BOOL)
at: &f];
}
}
- (id) initWithCoder: (NSCoder *)coder
{
if ((self = [super initWithCoder: coder]) == nil)
{
return nil;
}
if ([coder allowsKeyedCoding])
{
if ([coder containsValueForKey: @"NSAvoidsEmptySelection"])
{
[self setAvoidsEmptySelection:
[coder decodeBoolForKey: @"NSAvoidsEmptySelection"]];
}
if ([coder containsValueForKey: @"NSPreservesSelection"])
{
[self setPreservesSelection:
[coder decodeBoolForKey: @"NSPreservesSelection"]];
}
if ([coder containsValueForKey: @"NSSelectsInsertedObjects"])
{
[self setSelectsInsertedObjects:
[coder decodeBoolForKey: @"NSSelectsInsertedObjects"]];
}
if ([coder containsValueForKey: @"NSClearsFilterPredicateOnInsertion"])
{
[self setClearsFilterPredicateOnInsertion:
[coder decodeBoolForKey: @"NSClearsFilterPredicateOnInsertion"]];
}
if ([coder containsValueForKey: @"NSAutomaticallyRearrangesObjects"])
{
[self setAutomaticallyRearrangesObjects:
[coder decodeBoolForKey: @"NSAutomaticallyRearrangesObjects"]];
}
}
else
{
BOOL f;
[coder decodeValueOfObjCType: @encode(BOOL)
at: &f];
_acflags.avoids_empty_selection = f;
[coder decodeValueOfObjCType: @encode(BOOL)
at: &f];
_acflags.preserves_selection = f;
[coder decodeValueOfObjCType: @encode(BOOL)
at: &f];
_acflags.selects_inserted_objects = f;
[coder decodeValueOfObjCType: @encode(BOOL)
at: &f];
_acflags.clears_filter_predicate_on_insertion = f;
[coder decodeValueOfObjCType: @encode(BOOL)
at: &f];
_acflags.automatically_rearranges_objects = f;
}
return self;
}
@end