mirror of
https://github.com/cappuccino/cappuccino.git
synced 2026-09-12 05:31:29 +00:00
CPObjectController now works, as do bindings and CPControls.
This commit is contained in:
@@ -0,0 +1,428 @@
|
||||
|
||||
/*
|
||||
* CPArrayController.j
|
||||
* AppKit
|
||||
*
|
||||
* Adapted from Cocotron, by Johannes Fortmann
|
||||
*
|
||||
* Created by Ross Boucher.
|
||||
* Copyright 2009, 280 North, Inc.
|
||||
*
|
||||
* 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.1 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; if not, write to the Free Software
|
||||
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
|
||||
*/
|
||||
|
||||
@import <AppKit/CPObjectController.j>
|
||||
@import <AppKit/CPKeyValueBinding.j>
|
||||
|
||||
|
||||
@implementation CPArrayController : CPObjectController
|
||||
{
|
||||
BOOL _avoidsEmptySelection;
|
||||
BOOL _clearsFilterPredicateOnInsertion;
|
||||
BOOL _filterRestrictsInsertion;
|
||||
BOOL _preservesSelection;
|
||||
BOOL _selectsInsertedObjects;
|
||||
BOOL _alwaysUsesMultipleValuesMarker;
|
||||
|
||||
id _selectionIndexes;
|
||||
id _sortDescriptors;
|
||||
id _filterPredicate;
|
||||
id _arrangedObjects;
|
||||
}
|
||||
|
||||
+ (CPSet)keyPathsForValuesAffectingValueForContentArray
|
||||
{
|
||||
return [CPSet setWithObjects:"content"];
|
||||
}
|
||||
|
||||
+ (CPSet)keyPathsForValuesAffectingValueForSelection
|
||||
{
|
||||
return [CPSet setWithObjects:"content", "contentArray", "selectionIndexes"];
|
||||
}
|
||||
|
||||
+ (CPSet)keyPathsForValuesAffectingValueForSelectionIndex
|
||||
{
|
||||
return [CPSet setWithObjects:"content", "contentArray", "selectionIndexes", "selection"];
|
||||
}
|
||||
|
||||
+ (CPSet)keyPathsForValuesAffectingValueForSelectedObjects
|
||||
{
|
||||
return [CPSet setWithObjects:"content", "contentArray", "selectionIndexes", "selection"];
|
||||
}
|
||||
|
||||
+ (CPSet)keyPathsForValuesAffectingValueForCanRemove
|
||||
{
|
||||
return [CPSet setWithObjects:"selectionIndexes"];
|
||||
}
|
||||
|
||||
+ (CPSet)keyPathsForValuesAffectingValueForCanSelectNext
|
||||
{
|
||||
return [CPSet setWithObjects:"selectionIndexes"];
|
||||
}
|
||||
|
||||
+ (CPSet)keyPathsForValuesAffectingValueForCanSelectPrevious
|
||||
{
|
||||
return [CPSet setWithObjects:"selectionIndexes"];
|
||||
}
|
||||
|
||||
-(void)prepareContent
|
||||
{
|
||||
[self _setContentArray:[[self newObject]]];
|
||||
}
|
||||
|
||||
- (BOOL)preservesSelection
|
||||
{
|
||||
return _preservesSelection;
|
||||
}
|
||||
|
||||
- (void)setPreservesSelection:(BOOL)value
|
||||
{
|
||||
_preservesSelection = value;
|
||||
}
|
||||
|
||||
- (void)setContent:(id)value
|
||||
{
|
||||
if(![value isKindOfClass:[CPArray class]])
|
||||
value = [value];
|
||||
|
||||
var oldSelection = nil,
|
||||
oldSelectionIndexes = [[self selectionIndexes] copy];
|
||||
|
||||
if ([self preservesSelection])
|
||||
oldSelection = [self selectedObjects];
|
||||
|
||||
//FIXME: mutable copy?
|
||||
[super setContent:[value copy]];
|
||||
|
||||
if(_clearsFilterPredicateOnInsertion)
|
||||
[self setFilterPredicate:nil];
|
||||
|
||||
[self rearrangeObjects];
|
||||
|
||||
if (oldSelection)
|
||||
[self setSelectedObjects:oldSelection];
|
||||
else
|
||||
[self setSelectionIndexes:oldSelectionIndexes];
|
||||
}
|
||||
|
||||
- (void)_setContentArray:(id)value
|
||||
{
|
||||
[self setContent:value];
|
||||
}
|
||||
|
||||
- (id)contentArray
|
||||
{
|
||||
return [self content];
|
||||
}
|
||||
|
||||
- (CPArray)arrangeObjects:(CPArray)objects
|
||||
{
|
||||
var sortedObjects = objects;
|
||||
|
||||
if ([self filterPredicate])
|
||||
sortedObjects = [sortedObjects filteredArrayUsingPredicate:[self filterPredicate]];
|
||||
if ([self sortDescriptors])
|
||||
sortedObjects = [sortedObjects sortedArrayUsingDescriptors:[self sortDescriptors]];
|
||||
|
||||
return sortedObjects;
|
||||
}
|
||||
|
||||
- (void)rearrangeObjects
|
||||
{
|
||||
[self _setArrangedObjects:[self arrangeObjects:[self contentArray]]];
|
||||
}
|
||||
|
||||
- (void)_setArrangedObjects:(id)value
|
||||
{
|
||||
if (_arrangedObjects === value)
|
||||
return;
|
||||
|
||||
_arrangedObjects = [[_CPObservableArray alloc] initWithArray:value];
|
||||
}
|
||||
|
||||
- (id)arrangedObjects
|
||||
{
|
||||
return _arrangedObjects;
|
||||
}
|
||||
|
||||
|
||||
- (CPArray)sortDescriptors
|
||||
{
|
||||
return _sortDescriptors;
|
||||
}
|
||||
|
||||
- (void)setSortDescriptors:(CPArray)value
|
||||
{
|
||||
if (_sortDescriptors === value)
|
||||
return;
|
||||
|
||||
_sortDescriptors = [value copy];
|
||||
[self rearrangeObjects];
|
||||
}
|
||||
|
||||
- (CPPredicate)filterPredicate
|
||||
{
|
||||
return _filterPredicate;
|
||||
}
|
||||
|
||||
- (void)setFilterPredicate:(CPPredicate)value
|
||||
{
|
||||
if (_filterPredicate === value)
|
||||
return;
|
||||
|
||||
_filterPredicate = value;
|
||||
[self rearrangeObjects];
|
||||
}
|
||||
|
||||
- (BOOL)alwaysUsesMultipleValuesMarker
|
||||
{
|
||||
return _alwaysUsesMultipleValuesMarker;
|
||||
}
|
||||
|
||||
//Selection
|
||||
|
||||
- (unsigned)selectionIndex
|
||||
{
|
||||
return [_selectionIndexes firstIndex];
|
||||
}
|
||||
|
||||
- (BOOL)setSelectionIndex:(unsigned)index
|
||||
{
|
||||
return [self setSelectionIndexes:[CPIndexSet indexSetWithIndex:index]];
|
||||
}
|
||||
|
||||
- (CPIndexSet)selectionIndexes
|
||||
{
|
||||
return _selectionIndexes;
|
||||
}
|
||||
|
||||
- (BOOL)setSelectionIndexes:(CPIndexSet)indexes
|
||||
{
|
||||
if ([_selectionIndexes isEqual:indexes])
|
||||
return NO;
|
||||
|
||||
if(![indexes count] && _avoidsEmptySelection && [[self arrangedObjects] count])
|
||||
indexes = [CPIndexSet indexSetWithIndex:0];
|
||||
|
||||
[indexes removeIndexesInRange:CPMakeRange([[self arrangedObjects] count]+1, CPNotFound)];
|
||||
|
||||
[self willChangeValueForKey:@"selectionIndexes"];
|
||||
[self _selectionWillChange];
|
||||
|
||||
_selectionIndexes = [indexes copy];
|
||||
|
||||
[self _selectionDidChange];
|
||||
[self didChangeValueForKey:@"selectionIndexes"];
|
||||
|
||||
return YES;
|
||||
}
|
||||
|
||||
- (CPArray)selectedObjects
|
||||
{
|
||||
var objects = [_CPObservableArray arrayWithArray:[[self arrangedObjects] objectsAtIndexes:[self selectionIndexes]]];
|
||||
|
||||
return objects || [_CPObservableArray array];
|
||||
}
|
||||
|
||||
- (BOOL)setSelectedObjects:(CPArray)objects
|
||||
{
|
||||
var set = [CPIndexSet indexSet],
|
||||
count = [objects count],
|
||||
arrangedObjects = [self arrangedObjects];
|
||||
|
||||
for (var i=0; i<count; i++)
|
||||
{
|
||||
var index = [arrangedObjects indexOfObject:[objects objectAtIndex:i]];
|
||||
|
||||
if (index !== CPNotFound)
|
||||
[set addIndex:index];
|
||||
}
|
||||
|
||||
[self setSelectionIndexes:set];
|
||||
return YES;
|
||||
}
|
||||
|
||||
//Moving selection
|
||||
|
||||
-(BOOL)canSelectPrevious
|
||||
{
|
||||
return [[self selectionIndexes] firstIndex] > 0
|
||||
}
|
||||
|
||||
-(BOOL)canSelectNext
|
||||
{
|
||||
return [[self selectionIndexes] firstIndex] < [[self arrangedObjects] count] -1;
|
||||
}
|
||||
|
||||
-(void)selectNext:(id)sender
|
||||
{
|
||||
var index = [[self selectionIndexes] firstIndex] + 1 || 0;
|
||||
|
||||
if (index < [[self arrangedObjects] count])
|
||||
[self setSelectionIndexes:[CPIndexSet indexSetWithIndex:index]];
|
||||
}
|
||||
|
||||
-(void)selectPrevious:(id)sender
|
||||
{
|
||||
var index = [[self selectionIndexes] firstIndex] - 1 || [[self arrangedObjects] count] - 1;
|
||||
|
||||
if (index >= 0)
|
||||
[self setSelectionIndexes:[CPIndexSet indexSetWithIndex:index]];
|
||||
}
|
||||
|
||||
//Add/Remove
|
||||
|
||||
- (void)addObject:(id)object
|
||||
{
|
||||
if (![self canAdd])
|
||||
return;
|
||||
|
||||
[self willChangeValueForKey:@"content"];
|
||||
[_content addObject:object];
|
||||
[self didChangeValueForKey:@"content"];
|
||||
|
||||
if (_clearsFilterPredicateOnInsertion)
|
||||
[self setFilterPredicate:nil];
|
||||
|
||||
if ([_filterPredicate evaluateWithObject:object])
|
||||
{
|
||||
[self willChangeValueForKey:@"selectionIndexes"];
|
||||
var pos = [_arrangedObjects insertObject:object inArraySortedByDescriptors:_sortDescriptors];
|
||||
|
||||
[_selectionIndexes shiftIndexesStartingAtIndex:pos by:1];
|
||||
[self didChangeValueForKey:@"selectionIndexes"];
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
- (void)removeObject:(id)object
|
||||
{
|
||||
if (![self canRemove])
|
||||
return;
|
||||
|
||||
[self willChangeValueForKey:@"content"];
|
||||
[_content removeObject:object];
|
||||
[self didChangeValueForKey:@"content"];
|
||||
|
||||
if ([_filterPredicate evaluateWithObject:object])
|
||||
{
|
||||
[self willChangeValueForKey:@"selectionIndexes"];
|
||||
var pos = [_arrangedObjects indexOfObject:object];
|
||||
|
||||
[_selectionIndexes shiftIndexesStartingAtIndex:pos by:-1];
|
||||
[self didChangeValueForKey:@"selectionIndexes"];
|
||||
}
|
||||
}
|
||||
|
||||
-(void)add:(id)sender
|
||||
{
|
||||
if(![self canAdd])
|
||||
return;
|
||||
|
||||
[self insert:sender];
|
||||
}
|
||||
|
||||
- (void)insert:(id)sender
|
||||
{
|
||||
if(![self canInsert])
|
||||
return;
|
||||
|
||||
var newObject = [self automaticallyPreparesContent] ? [self newObject] : [self _defaultNewObject];
|
||||
|
||||
[self addObject:newObject];
|
||||
}
|
||||
|
||||
- (void)remove:(id)sender
|
||||
{
|
||||
[self removeObjects:[[self contentArray] objectsAtIndexes:[self selectionIndexes]]];
|
||||
}
|
||||
|
||||
- (void)removeObjectsAtArrangedObjectIndexes:(CPIndexSet)indexes
|
||||
{
|
||||
[self _removeObjects:[[self contentArray] objectsAtIndexes:indexes]];
|
||||
}
|
||||
|
||||
|
||||
- (void)addObjects:(CPArray)objects
|
||||
{
|
||||
if(![self canAdd])
|
||||
return;
|
||||
|
||||
var contentArray = [self contentArray],
|
||||
count = [objects count];
|
||||
|
||||
for (var i=0; i<count; i++)
|
||||
[contentArray addObject:[objects objectAtIndex:i]];
|
||||
|
||||
[self setContent:contentArray];
|
||||
}
|
||||
|
||||
- (void)removeObjects:(CPArray)objects
|
||||
{
|
||||
if(![self canRemove])
|
||||
return;
|
||||
|
||||
[self _removeObjects:objects];
|
||||
}
|
||||
|
||||
- (void)_removeObjects:(CPArray)objects
|
||||
{
|
||||
var contentArray = [self contentArray],
|
||||
count = [objects count];
|
||||
|
||||
for (var i=0; i<count; i++)
|
||||
[contentArray removeObject:[objects objectAtIndex:i]];
|
||||
|
||||
[self setContent:contentArray];
|
||||
}
|
||||
|
||||
- (BOOL)canInsert
|
||||
{
|
||||
return [self isEditable];
|
||||
}
|
||||
|
||||
@end
|
||||
|
||||
@implementation CPArrayController (CPCoding)
|
||||
|
||||
- (id)initWithCoder:(CPCoder)aCoder
|
||||
{
|
||||
self = [super initWithCoder:coder];
|
||||
|
||||
if (self)
|
||||
{
|
||||
_avoidsEmptySelection = [coder decodeBoolForKey:@"CPArrayControllerAvoidsEmptySelection"];
|
||||
_clearsFilterPredicateOnInsertion = [coder decodeBoolForKey:@"CPClearsFilterPredicateOnInsertion"];
|
||||
_filterRestrictsInsertion = [coder decodeBoolForKey:@"CPArrayControllerFilterRestrictsInsertion"];
|
||||
_preservesSelection = [coder decodeBoolForKey:@"CPArrayControllerPreservesSelection"];
|
||||
_selectsInsertedObjects = [coder decodeBoolForKey:@"CPArrayControllerSelectsInsertedObjects"];
|
||||
_alwaysUsesMultipleValuesMarker = [coder decodeBoolForKey:@"CPArrayControllerAlwaysUsesMultipleValuesMarker"];
|
||||
|
||||
if ([self automaticallyPreparesContent])
|
||||
[self prepareContent];
|
||||
else
|
||||
[self _setContentArray:[]];
|
||||
}
|
||||
|
||||
return self;
|
||||
}
|
||||
|
||||
- (void)awakeFromCib
|
||||
{
|
||||
[self _selectionWillChange];
|
||||
[self _selectionDidChange];
|
||||
}
|
||||
|
||||
@end
|
||||
+17
-1
@@ -121,7 +121,7 @@ var CPControlBlackColor = [CPColor blackColor];
|
||||
|
||||
+ (void)initialize
|
||||
{
|
||||
[self exposeBinding:"objectValue", "value"];
|
||||
[self exposeBinding:"value"];
|
||||
}
|
||||
|
||||
- (id)initWithFrame:(CGRect)aFrame
|
||||
@@ -146,6 +146,17 @@ var CPControlBlackColor = [CPColor blackColor];
|
||||
return self;
|
||||
}
|
||||
|
||||
- (void)bind:(CPString)binding toObject:(id)anObject withKeyPath:(CPString)keyPath options:(CPDictionary)options
|
||||
{
|
||||
if ([binding isEqual: CPValueBinding])
|
||||
{
|
||||
[self unbind: binding];
|
||||
[[CPKeyValueBinding alloc] initWithBinding: @"objectValue" name:CPValueBinding to:anObject keyPath:keyPath options:options from:self];
|
||||
}
|
||||
else
|
||||
[super bind:binding toObject:anObject withKeyPath:keyPath options:options];
|
||||
}
|
||||
|
||||
/*!
|
||||
Sets whether the receiver responds to mouse events.
|
||||
@param isEnabled whether the receiver will respond to mouse events
|
||||
@@ -438,6 +449,11 @@ var CPControlBlackColor = [CPColor blackColor];
|
||||
*/
|
||||
- (void)sendAction:(SEL)anAction to:(id)anObject
|
||||
{
|
||||
var theBinding = [CPKeyValueBinding getBinding:CPValueBinding forObject:self];
|
||||
|
||||
if (theBinding)
|
||||
[theBinding reverseSetValueFor:@"objectValue"];
|
||||
|
||||
[CPApp sendAction:anAction to:anObject from:self];
|
||||
}
|
||||
|
||||
|
||||
@@ -55,7 +55,7 @@ var CPBindingOperationAnd = 0,
|
||||
|
||||
+ (CPKeyValueBinding)getBinding:(CPString)aBinding forObject:(id)anObject
|
||||
{
|
||||
return [[bindingsMap objectForKey:anObject] objectForKey:aBinding];
|
||||
return [[bindingsMap objectForKey:[anObject hash]] objectForKey:aBinding];
|
||||
}
|
||||
|
||||
+ (CPDictionary)infoForBinding:(CPString)aBinding forObject:(id)anObject
|
||||
@@ -160,7 +160,7 @@ var CPBindingOperationAnd = 0,
|
||||
{
|
||||
if (!changes)
|
||||
return;
|
||||
CPLog.fatal("HERE");
|
||||
|
||||
var options = [_info objectForKey:CPOptionsKey],
|
||||
newValue = [changes objectForKey:CPKeyValueChangeNewKey];
|
||||
|
||||
@@ -176,7 +176,7 @@ var CPBindingOperationAnd = 0,
|
||||
valueTransformer,
|
||||
placeholder;
|
||||
|
||||
switch (aValue || @"")
|
||||
switch (aValue)
|
||||
{
|
||||
case CPMultipleValuesMarker: return [options objectForKey:CPMultipleValuesPlaceholderBindingOption] || @"Multiple Values";
|
||||
|
||||
@@ -187,7 +187,8 @@ var CPBindingOperationAnd = 0,
|
||||
|
||||
return [options objectForKey:CPNotApplicablePlaceholderBindingOption] || @"Not Applicable";
|
||||
|
||||
case @"": return [options objectForKey:CPNullPlaceholderBindingOption] || @"";
|
||||
case nil:
|
||||
case undefined: return [options objectForKey:CPNullPlaceholderBindingOption] || @"";
|
||||
}
|
||||
|
||||
var valueTransformerName = [options objectForKey:CPValueTransformerNameBindingOption],
|
||||
@@ -204,7 +205,7 @@ var CPBindingOperationAnd = 0,
|
||||
return aValue;
|
||||
}
|
||||
|
||||
- (id)reverseTransformValue: (id)value withOptions: (NSDictionary *)options
|
||||
- (id)reverseTransformValue:(id)aValue withOptions: (CPDictionary)options
|
||||
{
|
||||
var valueTransformerName = [options objectForKey:CPValueTransformerNameBindingOption],
|
||||
valueTransformer;
|
||||
|
||||
+28
-1
@@ -460,6 +460,12 @@
|
||||
@return the index of the object, or <code>CPNotFound</code> if it was not found.
|
||||
*/
|
||||
- (unsigned)indexOfObject:(id)anObject sortedByFunction:(Function)aFunction context:(id)aContext
|
||||
{
|
||||
var result = [self _indexOfObject:anObject sortedByFunction:aFunction context:aContext];
|
||||
return result >= 0 ? result : CPNotFound;
|
||||
}
|
||||
|
||||
- (unsigned)_indexOfObject:(id)anObject sortedByFunction:(Function)aFunction context:(id)aContext
|
||||
{
|
||||
if (!aFunction || anObject === undefined)
|
||||
return CPNotFound;
|
||||
@@ -483,7 +489,7 @@
|
||||
}
|
||||
}
|
||||
|
||||
return CPNotFound;
|
||||
return -mid-1;
|
||||
}
|
||||
|
||||
/*!
|
||||
@@ -510,6 +516,27 @@
|
||||
}];
|
||||
}
|
||||
|
||||
- (unsigned)insertObject:(id)anObject inArraySortedByDescriptors:(CPArray)descriptors
|
||||
{
|
||||
var index = [self _insertObject:anObject sortedByFunction:function(lhs, rhs)
|
||||
{
|
||||
var i = 0,
|
||||
count = [descriptors count],
|
||||
result = CPOrderedSame;
|
||||
|
||||
while (i < count)
|
||||
if((result = [descriptors[i++] compareObject:lhs withObject:rhs]) != CPOrderedSame)
|
||||
return result;
|
||||
|
||||
return result;
|
||||
} context:nil];
|
||||
|
||||
if (index < 0)
|
||||
index = -result-1;
|
||||
|
||||
[self insertObject:anObject atIndex:index];
|
||||
}
|
||||
|
||||
/*!
|
||||
Returns the last object in the array. If the array is empty, returns <code>nil</code>/
|
||||
*/
|
||||
|
||||
@@ -0,0 +1,176 @@
|
||||
/*
|
||||
* CPKeyValueBinding.j
|
||||
* Foundation
|
||||
*
|
||||
* Created by Ross Boucher 2/15/09
|
||||
* Copyright 280 North, Inc.
|
||||
*
|
||||
* Adapted from GNUStep
|
||||
* Released under the LGPL.
|
||||
*
|
||||
* 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.1 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; if not, write to the Free Software
|
||||
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
|
||||
*/
|
||||
|
||||
@import <Foundation/CPObject.j>
|
||||
@import <Foundation/CPDictionary.j>
|
||||
|
||||
|
||||
var transformerMap = [CPDictionary dictionary];
|
||||
|
||||
|
||||
@implementation CPValueTransformer : CPObject
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
+ (void)setValueTransformer:(CPValueTransformer)transformer forName:(CPString)name
|
||||
{
|
||||
[transformerMap setObject:transformer forKey:name];
|
||||
}
|
||||
|
||||
+ (CPValueTransformer)valueTransformerForName:(CPString)name
|
||||
{
|
||||
return [transformerMap objectForKey:name];
|
||||
}
|
||||
|
||||
+ (CPArray)valueTransformerNames
|
||||
{
|
||||
return [transformerMap allKeys];
|
||||
}
|
||||
|
||||
+ (BOOL)allowsReverseTransformation
|
||||
{
|
||||
return NO;
|
||||
}
|
||||
|
||||
+ (Class)transformedValueClass
|
||||
{
|
||||
return [CPObject class];
|
||||
}
|
||||
|
||||
- (id)reverseTransformedValue:(id)value
|
||||
{
|
||||
if ([[self class] allowsReverseTransformation])
|
||||
{
|
||||
[CPException raise:CPInvalidArgumentException reason:(self+" is not reversible.")];
|
||||
}
|
||||
|
||||
return [self transformedValue:value];
|
||||
}
|
||||
|
||||
- (id)transformedValue:(id)value
|
||||
{
|
||||
return nil;
|
||||
}
|
||||
|
||||
@end
|
||||
|
||||
// builtin transformers
|
||||
|
||||
@implementation CPNegateBooleanTransformer
|
||||
|
||||
+ (BOOL)allowsReverseTransformation
|
||||
{
|
||||
return YES;
|
||||
}
|
||||
|
||||
+ (Class)transformedValueClass
|
||||
{
|
||||
return [CPNumber class];
|
||||
}
|
||||
|
||||
- (id)reverseTransformedValue:(id)value
|
||||
{
|
||||
return ![value boolValue];
|
||||
}
|
||||
|
||||
- (id)transformedValue:(id)value
|
||||
{
|
||||
return ![value boolValue];
|
||||
}
|
||||
|
||||
@end
|
||||
|
||||
@implementation CPIsNilTransformer
|
||||
|
||||
+ (BOOL)allowsReverseTransformation
|
||||
{
|
||||
return NO;
|
||||
}
|
||||
|
||||
+ (Class)transformedValueClass
|
||||
{
|
||||
return [CPNumber class];
|
||||
}
|
||||
|
||||
- (id)transformedValue:(id)value
|
||||
{
|
||||
return value === nil || value === undefined;
|
||||
}
|
||||
|
||||
@end
|
||||
|
||||
@implementation CPIsNotNilTransformer
|
||||
|
||||
+ (BOOL)allowsReverseTransformation
|
||||
{
|
||||
return NO;
|
||||
}
|
||||
|
||||
+ (Class)transformedValueClass
|
||||
{
|
||||
return [CPNumber class];
|
||||
}
|
||||
|
||||
- (id)transformedValue:(id)value
|
||||
{
|
||||
return value !== nil && value !== undefined;
|
||||
}
|
||||
|
||||
@end
|
||||
|
||||
@implementation CPUnarchiveFromDataTransformer
|
||||
|
||||
+ (BOOL)allowsReverseTransformation
|
||||
{
|
||||
return YES;
|
||||
}
|
||||
|
||||
+ (Class)transformedValueClass
|
||||
{
|
||||
return [CPData class];
|
||||
}
|
||||
|
||||
- (id)reverseTransformedValue:(id)value
|
||||
{
|
||||
return [CPKeyedArchiver archivedDataWithRootObject:value];
|
||||
}
|
||||
|
||||
- (id)transformedValue:(id)value
|
||||
{
|
||||
return [CPKeyedUnarchiver unarchiveObjectWithData:value];
|
||||
}
|
||||
|
||||
@end
|
||||
|
||||
CPNegateBooleanTransformerName = @"CPNegateBooleanTransformerName";
|
||||
CPIsNilTransformerName = @"CPIsNilTransformerName";
|
||||
CPIsNotNilTransformerName = @"CPIsNotNilTransformerName";
|
||||
CPUnarchiveFromDataTransformerName = @"CPUnarchiveFromDataTransformerName";
|
||||
|
||||
[CPValueTransformer setValueTransformer:[CPNegateBooleanTransformer new] forName:CPNegateBooleanTransformerName];
|
||||
[CPValueTransformer setValueTransformer:[CPIsNilTransformer new] forName:CPIsNilTransformerName];
|
||||
[CPValueTransformer setValueTransformer:[CPIsNotNilTransformer new] forName:CPIsNotNilTransformerName];
|
||||
[CPValueTransformer setValueTransformer:[CPUnarchiveFromDataTransformer new] forName:CPUnarchiveFromDataTransformerName];
|
||||
@@ -40,13 +40,16 @@ CPLogRegister(CPLogConsole);
|
||||
|
||||
slider = [[CPSlider alloc] initWithFrame:CGRectMake(CGRectGetMinX(textFrame), CGRectGetMaxY(textFrame)+10, CGRectGetWidth(textFrame), 12)];
|
||||
|
||||
[slider setContinuous:NO];
|
||||
[slider setContinuous:YES];
|
||||
|
||||
[contentView addSubview:slider];
|
||||
|
||||
var button = [[CPButton alloc] initWithFrame:CGRectMake(CGRectGetMinX(textFrame)+25, CGRectGetMaxY(textFrame)+40, 100, 18)];
|
||||
|
||||
[button setTitle:@"mute"];
|
||||
|
||||
[button setTarget:self];
|
||||
[button setAction:@selector(muteTrack:)];
|
||||
|
||||
[contentView addSubview:button];
|
||||
|
||||
@@ -60,13 +63,10 @@ CPLogRegister(CPLogConsole);
|
||||
var controller = [[CPObjectController alloc] init];
|
||||
[controller bind:@"contentObject" toObject:self withKeyPath:@"track" options:nil];
|
||||
|
||||
[textField bind:@"objectValue" toObject:controller withKeyPath:@"selection.volume" options:nil];
|
||||
[slider bind:@"objectValue" toObject:controller withKeyPath:@"selection.volume" options:nil];
|
||||
[textField bind:CPValueBinding toObject:controller withKeyPath:@"selection.volume" options:nil];
|
||||
[slider bind:CPValueBinding toObject:controller withKeyPath:@"selection.volume" options:nil];
|
||||
|
||||
[theWindow orderFront:self];
|
||||
|
||||
// Uncomment the following line to turn on the standard menu bar.
|
||||
//[CPMenu setMenuBarVisible:YES];
|
||||
}
|
||||
|
||||
- (IBAction)muteTrack:(id)sender
|
||||
@@ -74,6 +74,7 @@ CPLogRegister(CPLogConsole);
|
||||
[track setVolume:0.0];
|
||||
}
|
||||
|
||||
|
||||
@end
|
||||
|
||||
|
||||
|
||||
Reference in New Issue
Block a user