Compare commits

..
83 Commits
Author SHA1 Message Date
Aparajita Fishman 8fa12b874e Framework capp template 2011-07-17 11:29:52 +03:00
Aparajita Fishman a9edc868f1 Support for postinstall scripts 2011-07-17 11:29:43 +03:00
Aparajita Fishman 7e465689e4 Whitespace cleanup 2011-07-09 17:06:54 -07:00
Aparajita Fishman 1cab9463ad CPFormatter support, bug fixes...
- Test application.

CPControl
---------
- Moved _formatter declaration up next to value, it was in the Target-Action Support section before.

- sendAction:to: should return a BOOL.

- Support for CPFormatter -editingStringForObjectValue.

- If a nil string is passed to -setStringValue in Cocoa, it raises an invalid parameter assertion and does nothing.

- Per Cocoa, if setting a string value fails, it tries again with an empty string.

- Removed a few extra blank lines.

- Changed NULL to nil.

CPTextField
-----------
- The string value is now cached in _stringValue to avoid constant calls to the formatter.

- There were several places where references to _inputElement were not wrapped with #if PLATFORM(DOM).

- When resigning first responder, if there is a formatter and formatting fails, reject the resignation and keep focus. Also, if the delegate responds to control:didFailToFormatString:errorDescription:, invoke it.

- _willBecomeFirstResponderByClick was not being set to NO when resigning. As a result, if you clicked in a field, tabbed to another field, and tabbed back to the original field, it would not select the text because it thought there was a mouse click.

- Don't send _sendStringValue: in keyUp: if the value has not changed. Previously it would do that for non-printing keys like navigation keys.

- insertNewLine: validates the string the same way resigning does.

- insertNewLine: would not send textDidEndEditing: if the field had no action, but it should.

- insertNewLine: should select all text (per Cocoa).

- insertNewLineIgnoringFieldEditor: and insertTabIgnoringFieldEditor: were not replacing the selection, Cocoa docs say they should.

- Per Cocoa, when setting an object value and there is a formatter, the value is first formatted as a string. If that fails, an object value for an empty string is attempted.

CPFormatter
-----------
- Fixed up the docs to match the method signatures.

- Indicated with "Ref" in argument types if the argument is pass by reference.

- isPartialStringValid:newEditingString:errorDescription: has a default implementation in Cocoa that nils the return values and returns YES.

- Fixed the name of isPartialStringValid:proposedSelectedRange:originalString:originalSelectedRange:errorDescription:

- isPartialStringValid:proposedSelectedRange:originalString:originalSelectedRange:errorDescription: has a default implementation in Cocoa that invokes isPartialStringValid:newEditingString:errorDescription:
2011-07-09 17:03:12 -07:00
Aparajita Fishman 3974a483d1 Merge remote branch 'refs/remotes/280north/master' 2011-07-09 15:03:57 -07:00
Aparajita Fishman ecf8f7b143 Have to set _themeClass when class changes 2011-07-08 19:03:30 -07:00
Klaas Pieter Annema 4719578f10 fix 0 is a valid highlighted index 2011-07-07 20:40:22 +02:00
Klaas Pieter Annema fe370c7066 Merge branch 'master' of github.com:280north/cappuccino 2011-07-07 18:37:24 +02:00
Klaas Pieter Annema 63d208bf20 never return a separator as the highlighted item 2011-07-07 18:33:00 +02:00
Alexander LjungbergandKlaas Pieter Annema 5158540208 Fixed: initialise CPNumberFormatter maximumFractionalDigits to 3. 2011-07-07 16:04:12 +02:00
Alexander LjungbergandKlaas Pieter Annema a69f7dc81a Test CPTextField with a formatter. 2011-07-07 16:04:11 +02:00
Alexander LjungbergandKlaas Pieter Annema e848e161d4 CPNumberFormatter maximum fractional digits support. Optimise by only creating CPDecimalNumberHandler when needed and caching the result. 2011-07-07 16:04:11 +02:00
Alexander LjungbergandKlaas Pieter Annema 3db61216e1 CPFormatter support for CPControl which actually works. 2011-07-07 16:04:11 +02:00
Alexander LjungbergandKlaas Pieter Annema 63b00186f4 Fixed: level indicator warning and critical level ranges should be inclusive. 2011-07-07 16:04:11 +02:00
Alexander LjungbergandKlaas Pieter Annema 110d4c478d Fixed: pressing the up arrow key in a table view would cause a crash if the previous selection was non continuous. 2011-07-07 16:04:11 +02:00
Alexander LjungbergandKlaas Pieter Annema 49c29ded92 Nib2cib support for formatters. 2011-07-07 16:04:11 +02:00
Alexander LjungbergandKlaas Pieter Annema 06670f2214 CPFormatter support for CPControl. 2011-07-07 16:04:11 +02:00
Alexander LjungbergandKlaas Pieter Annema 134bfec615 Temporary numberFromString:. 2011-07-07 16:04:11 +02:00
Alexander LjungbergandKlaas Pieter Annema 279326ffe1 CPNumberFormatter rounding mode support. 2011-07-07 16:04:10 +02:00
Alexander LjungbergandKlaas Pieter Annema ecb04e983e Simple CPNumberFormatter stringFromNumber for CPNumberFormatterDecimalStyle. 2011-07-07 16:04:10 +02:00
Klaas Pieter Annema 73b4c33f05 track if the mouse was released after menu open
Make sure the menu isn't closed by the mouse up that is directly followed by the mouse down that opened the menu.
2011-07-07 16:04:10 +02:00
Klaas Pieter Annema ad30f4dcbb call delegate when submenu open and close 2011-07-07 16:04:10 +02:00
Klaas Pieter Annema 0318587f80 Set the title of a menu item's submenu
Correspond to Cocoa's behavior by automatically having CPMenuItem set the title of it's submenu
2011-07-07 16:04:10 +02:00
Klaas Pieter Annema 2b6fddf26c move menu delegate logic to generic methods 2011-07-07 16:04:04 +02:00
Aparajita Fishman f57755ce46 Merge remote branch 'refs/remotes/280north/master' 2011-07-06 12:01:47 -07:00
Alexander Ljungberg da61da392b Fixed: initialise CPNumberFormatter maximumFractionalDigits to 3. 2011-07-06 14:08:44 -04:00
Alexander Ljungberg 984b46ba07 Test CPTextField with a formatter. 2011-07-06 13:36:41 -04:00
Alexander Ljungberg 1abce77b3a Merge branch 'master' of github.com:280north/cappuccino 2011-07-06 13:32:46 -04:00
Aparajita Fishman eaa51835ef Exit with error if conversion fails. 2011-07-05 16:08:22 -07:00
Aparajita Fishman 9038b7cfa9 Merge remote branch 'refs/remotes/280north/master'
Conflicts:
	Tools/nib2cib/main.j
2011-07-05 16:04:20 -07:00
Francisco Ryan Tolmasky I 420606d8e8 Tests for forward invocation.
Reviewed by @tolmasky.
2011-07-02 13:33:53 -07:00
Francisco Ryan Tolmasky I 80c8fd9794 Fix bundletask to include .js files.
Reviewed by @tolmasky.
2011-07-02 08:38:10 -07:00
Klaas Pieter Annema 0af89853d4 Merge pull request #1311 from aparajita/registerThemeValues-tweak
Added missing type in +registerThemeValues signature
2011-07-01 08:41:29 -07:00
Randall Luecke ff13de9f75 Merge branch 'master' of git://github.com/280north/cappuccino 2011-07-01 00:05:19 -07:00
Randall Luecke 47f40eb04b Added left/right arrow key expand and collapse... just for you @tolmasky. 2011-07-01 00:04:09 -07:00
release 8dd174d1e3 Changed version number to 1.0.0.006 2011-06-30 03:06:48 -05:00
Aparajita Fishman b9b0eb79d8 Make sure CPPlatformString +sizeOfString and +metricsOfFont work with CommonJS, so that OJUnitTest will work. 2011-06-29 10:40:14 -07:00
Aparajita Fishman a5832aa66b Theme tweaks.
- Don't need the CPTheme hack any more, I'm giving up on auxiliary themes for now.
- Added CPView -registerThemeValues: and -registerThemeValues:inherit:
2011-06-29 09:37:37 -07:00
Aparajita Fishman 63710d5fae Added missing type in method signature 2011-06-29 09:08:12 -07:00
Aparajita Fishman 9472f76ad5 Added missing type in method signature 2011-06-29 09:06:22 -07:00
Klaas Pieter Annema a2ef701d18 Merge branch 'master' of github.com:280north/cappuccino 2011-06-29 16:05:26 +02:00
Klaas Pieter Annema 55e07536d9 don't start dragging if there are no rows 2011-06-29 16:04:46 +02:00
Randall Luecke fc81483371 Fix typeo in CPToolbar.j CPToolbarItems to not respect maxSize. Closes #1305. 2011-06-25 00:45:07 -07:00
release 4da5669ecb Changed version number to 1.0.0.006-SNAPSHOT 2011-06-24 05:37:02 -05:00
release c232680600 Changed version number to 1.0.0.005 2011-06-24 05:36:56 -05:00
Klaas Pieter Annema e552b022ba Merge branch 'pull-request-1302' 2011-06-24 12:21:02 +02:00
release bd25b6fb48 Changed version number to 1.0.0.005-SNAPSHOT 2011-06-24 04:36:49 -05:00
release 834d878a4d Changed version number to 1.0.0.004 2011-06-24 04:36:38 -05:00
release 4cb5ccbc3a Changed version number to 1.0.0.004-SNAPSHOT 2011-06-24 03:05:58 -05:00
release 929cd78bae Changed version number to 1.0.0.003 2011-06-24 03:05:51 -05:00
Antoine Mercadal 6c34bec51e do not return 0 if any conversion fail (return 2 instead) 2011-06-19 19:32:42 +02:00
Ross Boucher cbabb544ab bootstrap should have a +x bit. 2011-06-15 12:54:11 -07:00
hmalphettes f5961b9e06 remove the web-fragment.xml. it is not used and could be harmful. 2011-06-15 14:42:01 +08:00
Pascal Belloncle 7fb17e9f41 added Gemfile 2011-06-14 14:55:18 -03:00
Alexander Ljungberg 42220e057b CPNumberFormatter maximum fractional digits support. Optimise by only creating CPDecimalNumberHandler when needed and caching the result. 2011-06-13 23:19:06 -07:00
Alexander Ljungberg 91509e7d36 CPFormatter support for CPControl which actually works. 2011-06-13 18:39:17 -07:00
hmalphettes dd70bdc698 use '/' for development. use the BundleShape to be able to be published by the latest version of tycho eventually 2011-06-13 17:14:35 +08:00
release 59d619d190 Changed version number to 1.0.0.003-SNAPSHOT 2011-06-13 03:31:35 -05:00
release dd160d6bed Changed version number to 1.0.0.002 2011-06-13 03:31:30 -05:00
hmalphettes ae5a2c4164 CARBON-183 and remove harful servlet-3.0 web-fragment.xml. Remove the dependency on buildr4osgi 2011-06-13 16:15:15 +08:00
hmalphettes a939bed8e0 CORE-153 pad the buildnumber 2011-06-13 15:51:15 +08:00
release c686a4384b Changed version number to 1.0.0.2-SNAPSHOT 2011-06-11 13:19:43 -05:00
Alexander Ljungberg 4421b55613 Fixed: level indicator warning and critical level ranges should be inclusive. 2011-06-11 04:43:16 -07:00
Alexander Ljungberg 75d9929483 Fixed: pressing the up arrow key in a table view would cause a crash if the previous selection was non continuous. 2011-06-11 04:21:49 -07:00
Alexander Ljungberg 7c78c2ef5d Nib2cib support for formatters. 2011-06-11 03:59:52 -07:00
Alexander Ljungberg 3758c86d20 CPFormatter support for CPControl. 2011-06-11 03:59:33 -07:00
Alexander Ljungberg ada547a79d Temporary numberFromString:. 2011-06-11 03:36:44 -07:00
Alexander Ljungberg 445adcb47f CPNumberFormatter rounding mode support. 2011-06-10 11:41:23 -07:00
Alexander Ljungberg 4e9b975ca8 Simple CPNumberFormatter stringFromNumber for CPNumberFormatterDecimalStyle. 2011-06-10 11:23:36 -07:00
Alexander Ljungberg aac09d8208 Clicking on a non-editable level indicator in a table row now selects that row. 2011-06-06 00:44:15 -04:00
Alexander Ljungberg a127fc9ec3 'Enabled' support for nib2cib of NSLevelIndicator. 2011-06-06 00:35:41 -04:00
Alexander Ljungberg 551aa300ea Merge branch 'master' of github.com:280north/cappuccino 2011-06-06 00:11:23 -04:00
Alexander Ljungberg 485e59b5eb More handlesContentAsCompoundValue, although fairly guess-work based. More research needed in the future. 2011-06-06 00:11:04 -04:00
Alexander Ljungberg fdfcbf1da2 CPArrayController handlesContentAsCompoundValue support for contentArray binding. 2011-06-05 19:39:28 -04:00
Alexander Ljungberg 0f5eee0258 nib2cib compatible CPNumberFormatter skeleton. 2011-06-05 19:27:21 -04:00
Alexander Ljungberg 870492eb96 Fix selection behaviour in previous commit. 2011-06-05 19:19:14 -04:00
Alexander Ljungberg b50005049e Fixed: array controller remove: would not only remove selected rows, but any other rows isEqual: to the selected rows. 2011-06-05 18:57:36 -04:00
Alexander Ljungberg 0e890e19ea Fixed: during CPUserDefault change notifications, objectForKey: could return the value from prior to the change. 2011-06-05 17:32:56 -04:00
Alexander Ljungberg 59aabc150c Crude but functional infinite recursion checker, optional at compile time. 2011-06-04 01:23:24 -04:00
Alexander Ljungberg b159815349 Suppress binding updates support. 2011-06-04 00:57:57 -04:00
Alexander Ljungberg bfe5afaddd Whitespace cleanup. 2011-06-03 00:05:30 -04:00
Alexander Ljungberg 64e907156c Minor array controller optimisation: don't send a filterPredicate change notification if clearsFilterPredicateOnInsertion is YES but filterPredicate is already nil when insertObject:atArrangedObjectIndex: is called. 2011-06-03 00:00:39 -04:00
Alexander Ljungberg 5d01ca428c Optimise CPArrayController insertObject:atArrangedObjectIndex: by not attempting to arrange the content array. The given index is only meant for the arrangedObjects. 2011-06-02 23:41:03 -04:00
61 changed files with 4376 additions and 200 deletions
+132 -22
View File
@@ -313,6 +313,8 @@
// Don't use [super setContent:] as that would fire the contentObject change.
// We need to be in control of when notifications fire.
// Note that if we have a contentArray binding, setting the content does /not/
// cause a reverse binding set.
_contentObject = value;
if (_clearsFilterPredicateOnInsertion && _filterPredicate != nil)
@@ -737,6 +739,10 @@
*/
_disableSetContent = YES;
[_contentObject addObject:object];
// Allow handlesContentAsCompoundValue reverse sets to trigger.
[[CPBinder getBinding:@"contentArray" forObject:self] _contentArrayDidChange];
_disableSetContent = NO;
if (willClearPredicate)
@@ -771,7 +777,8 @@
}
/*!
Adds an object at a given index to the receiver's collection.
Adds an object at a given index in the receiver's arrangedObjects. Also add the object
to the content collection (although at the end rather than the given index).
@param id anObject - The object to add to the collection.
@param int anIndex - The index to insert the object at.
@@ -781,8 +788,12 @@
if (![self canAdd])
return;
if (_clearsFilterPredicateOnInsertion)
var willClearPredicate = NO;
if (_clearsFilterPredicateOnInsertion && _filterPredicate)
{
[self willChangeValueForKey:@"filterPredicate"];
willClearPredicate = YES;
}
[self willChangeValueForKey:@"content"];
@@ -790,10 +801,17 @@
See _disableSetContent explanation in addObject:.
*/
_disableSetContent = YES;
[_contentObject insertObject:anObject atIndex:anIndex];
// The atArrangedObjectIndex: part of this method's name only refers to where the
// object goes in arrangedObjects, not in the content array. So use addObject:,
// not insertObject:atIndex: here for speed.
[_contentObject addObject:anObject];
// Allow handlesContentAsCompoundValue reverse sets to trigger.
[[CPBinder getBinding:@"contentArray" forObject:self] _contentArrayDidChange];
_disableSetContent = NO;
if (_clearsFilterPredicateOnInsertion)
if (willClearPredicate)
[self __setFilterPredicate:nil];
[[self arrangedObjects] insertObject:anObject atIndex:anIndex];
@@ -809,7 +827,7 @@
[self __setSelectionIndexes:[CPIndexSet indexSetWithIndex:0]];
[self didChangeValueForKey:@"content"];
if (_clearsFilterPredicateOnInsertion)
if (willClearPredicate)
[self didChangeValueForKey:@"filterPredicate"];
}
@@ -820,26 +838,28 @@
*/
- (void)removeObject:(id)object
{
[self willChangeValueForKey:@"content"];
[self willChangeValueForKey:@"content"];
/*
See _disableSetContent explanation in addObject:.
*/
_disableSetContent = YES;
[_contentObject removeObject:object];
_disableSetContent = NO;
// See _disableSetContent explanation in addObject:.
_disableSetContent = YES;
if (_filterPredicate === nil || [_filterPredicate evaluateWithObject:object])
{
[_contentObject removeObject:object];
// Allow handlesContentAsCompoundValue reverse sets to trigger.
[[CPBinder getBinding:@"contentArray" forObject:self] _contentArrayDidChange];
_disableSetContent = NO;
if (_filterPredicate === nil || [_filterPredicate evaluateWithObject:object])
{
// selectionIndexes change notification will be fired as a result of the
// content change. Don't fire manually.
var pos = [_arrangedObjects indexOfObject:object];
[_arrangedObjects removeObjectAtIndex:pos];
[_selectionIndexes shiftIndexesStartingAtIndex:pos by:-1];
}
}
[self didChangeValueForKey:@"content"];
[self didChangeValueForKey:@"content"];
}
/*!
@@ -875,16 +895,61 @@
*/
- (void)remove:(id)sender
{
[self removeObjects:[[self arrangedObjects] objectsAtIndexes:[self selectionIndexes]]];
[self removeObjectsAtArrangedObjectIndexes:_selectionIndexes];
}
/*!
Removes the objects at the specified indexes in the controller's arranged objects from the content array.
@param CPIndexSet indexes - indexes of the objects to remove.
*/
- (void)removeObjectsAtArrangedObjectIndexes:(CPIndexSet)indexes
- (void)removeObjectsAtArrangedObjectIndexes:(CPIndexSet)anIndexSet
{
[self _removeObjects:[[self arrangedObjects] objectsAtIndexes:indexes]];
[self willChangeValueForKey:@"content"];
/*
See _disableSetContent explanation in addObject:.
*/
_disableSetContent = YES;
var arrangedObjects = [self arrangedObjects],
index = [anIndexSet lastIndex],
position = CPNotFound,
newSelectionIndexes = [_selectionIndexes copy];
while (index !== CPNotFound)
{
var object = [arrangedObjects objectAtIndex:index];
// First try the simple case which should work if there are no sort descriptors.
if ([_contentObject objectAtIndex:index] === object)
[_contentObject removeObjectAtIndex:index];
else
{
// Since we don't have a reverse mapping between the sorted order and the
// unsorted one, we'll just simply have to remove an arbitrary pointer. It might
// be the 'wrong' one - as in not the one the user selected - but the wrong
// one is still just another pointer to the same object, so the user will not
// be able to see any difference.
contentIndex = [_contentObject indexOfObjectIdenticalTo:object];
[_contentObject removeObjectAtIndex:contentIndex];
}
[arrangedObjects removeObjectAtIndex:index];
// Deselect this row if it was selected, and either way shift all selection indexes
// following it up by 1.
[newSelectionIndexes removeIndex:index];
[newSelectionIndexes shiftIndexesStartingAtIndex:index by:-1];
index = [anIndexSet indexLessThanIndex:index];
}
// Allow handlesContentAsCompoundValue reverse sets to trigger.
[[CPBinder getBinding:@"contentArray" forObject:self] _contentArrayDidChange];
_disableSetContent = NO;
// This will automatically handle the avoidsEmptySelection case.
[self __setSelectionIndexes:newSelectionIndexes];
[self didChangeValueForKey:@"content"];
}
/*!
@@ -904,6 +969,8 @@
[contentArray addObject:[objects objectAtIndex:i]];
[self setContent:contentArray];
// Allow handlesContentAsCompoundValue reverse sets to trigger.
[[CPBinder getBinding:@"contentArray" forObject:self] _contentArrayDidChange];
}
/*!
@@ -922,11 +989,13 @@
{
[self willChangeValueForKey:@"content"];
/*
See _disableSetContent explanation in addObject:.
*/
// See _disableSetContent explanation in addObject:.
_disableSetContent = YES;
[_contentObject removeObjectsInArray:objects];
// Allow handlesContentAsCompoundValue reverse sets to trigger.
[[CPBinder getBinding:@"contentArray" forObject:self] _contentArrayDidChange];
_disableSetContent = NO;
var arrangedObjects = [self arrangedObjects],
@@ -985,12 +1054,53 @@
var destination = [_info objectForKey:CPObservedObjectKey],
keyPath = [_info objectForKey:CPObservedKeyPathKey],
options = [_info objectForKey:CPOptionsKey],
isCompound = [self handlesContentAsCompoundValue];
if (!isCompound)
{
newValue = [destination mutableArrayValueForKeyPath:keyPath];
}
else
{
// handlesContentAsCompoundValue == YES so we cannot just set up a proxy.
// Every read and every write must go through transformValue and
// reverseTransformValue, and the resulting object cannot be described by
// a key path.
newValue = [destination valueForKeyPath:keyPath];
}
newValue = [self transformValue:newValue withOptions:options];
if (isCompound)
{
// Make sure we can edit our copy of the content. TODO In Cocoa, this copy
// appears to be deferred until the array actually needs to be edited.
newValue = [newValue mutableCopy];
}
[_source setValue:newValue forKey:aBinding];
}
- (void)_contentArrayDidChange
{
// When handlesContentAsCompoundValue == YES, it is not sufficient to modify the content object
// in place because what we are holding is an array 'unwrapped' from a compound value by
// a value transformer. So when we modify it we need a reverse set and transform to create
// a new compound value.
//
// (The Cocoa documentation on the subject is not very clear but after substantial
// experimentation this seems both reasonable and compliant.)
if ([self handlesContentAsCompoundValue])
{
var destination = [_info objectForKey:CPObservedObjectKey],
keyPath = [_info objectForKey:CPObservedKeyPathKey];
[self suppressSpecificNotificationFromObject:destination keyPath:keyPath];
[self reverseSetValueFor:@"contentArray"];
[self unsuppressSpecificNotificationFromObject:destination keyPath:keyPath];
}
}
@end
var CPArrayControllerAvoidsEmptySelection = @"CPArrayControllerAvoidsEmptySelection",
+41 -8
View File
@@ -20,6 +20,9 @@
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
#import "../Foundation/Ref.h"
@import "../Foundation/CPFormatter.j"
@import "CPFont.j"
@import "CPShadow.j"
@import "CPView.j"
@@ -82,6 +85,7 @@ var CPControlBlackColor = [CPColor blackColor];
@implementation CPControl : CPView
{
id _value;
CPFormatter _formatter @accessors(property=formatter);
// Target-Action Support
id _target;
@@ -221,10 +225,11 @@ var CPControlBlackColor = [CPColor blackColor];
@param anAction the action to send
@param anObject the object to which the action will be sent
*/
- (void)sendAction:(SEL)anAction to:(id)anObject
- (BOOL)sendAction:(SEL)anAction to:(id)anObject
{
[self _reverseSetBinding];
[CPApp sendAction:anAction to:anObject from:self];
return [CPApp sendAction:anAction to:anObject from:self];
}
- (int)sendActionOn:(int)mask
@@ -502,15 +507,46 @@ var CPControlBlackColor = [CPColor blackColor];
*/
- (CPString)stringValue
{
if (_formatter && _value !== undefined && _value !== nil)
{
var formattedValue = [self hasThemeState:CPThemeStateEditing] ? [_formatter editingStringForObjectValue:_value] : [_formatter stringForObjectValue:_value];
if (formattedValue !== nil && formattedValue !== undefined)
return formattedValue;
}
return (_value === undefined || _value === nil) ? "" : String(_value);
}
/*!
Sets the receiver's string value.
*/
- (void)setStringValue:(CPString)anObject
- (void)setStringValue:(CPString)aString
{
[self setObjectValue:anObject];
// Cocoa raises an invalid parameter assertion and returns if you pass nil.
if (aString === nil || aString === undefined)
{
CPLog.warn("nil sent to CPControl -setStringValue");
return;
}
var value;
if (_formatter)
{
value = nil;
if ([_formatter getObjectValue:AT_REF(value) forString:aString errorDescription:nil] === NO)
{
// If the given string is non-empty and doesn't work, Cocoa tries an empty string.
if (!aString || [_formatter getObjectValue:AT_REF(value) forString:@"" errorDescription:nil] === NO)
value = undefined; // Means the value is invalid
}
}
else
value = aString;
[self setObjectValue:value];
}
- (void)takeDoubleValueFrom:(id)sender
@@ -526,21 +562,18 @@ var CPControlBlackColor = [CPColor blackColor];
[self setFloatValue:[sender floatValue]];
}
- (void)takeIntegerValueFrom:(id)sender
{
if ([sender respondsToSelector:@selector(integerValue)])
[self setIntegerValue:[sender integerValue]];
}
- (void)takeIntValueFrom:(id)sender
{
if ([sender respondsToSelector:@selector(intValue)])
[self setIntValue:[sender intValue]];
}
- (void)takeObjectValueFrom:(id)sender
{
if ([sender respondsToSelector:@selector(objectValue)])
@@ -882,7 +915,7 @@ var __Deprecated__CPImageViewImageKey = @"CPImageViewImageKey";
if (_target !== nil)
[aCoder encodeConditionalObject:_target forKey:CPControlTargetKey];
if (_action !== NULL)
if (_action !== nil)
[aCoder encodeObject:_action forKey:CPControlActionKey];
[aCoder encodeInt:_sendActionOn forKey:CPControlSendActionOnKey];
+45 -1
View File
@@ -39,6 +39,8 @@ var CPBindingOperationAnd = 0,
{
CPDictionary _info;
id _source;
JSObject _suppressedNotifications;
}
+ (void)exposeBinding:(CPString)aBinding forClass:(Class)aClass
@@ -122,6 +124,7 @@ var CPBindingOperationAnd = 0,
{
_source = aSource;
_info = [CPDictionary dictionaryWithObjects:[aDestination, aKeyPath] forKeys:[CPObservedObjectKey, CPObservedKeyPathKey]];
_suppressedNotifications = {};
if (options)
[_info setObject:options forKey:CPOptionsKey];
@@ -169,6 +172,10 @@ var CPBindingOperationAnd = 0,
if (!changes)
return;
var objectSuppressions = _suppressedNotifications[[anObject UID]];
if (objectSuppressions && objectSuppressions[aKeyPath])
return;
[self setValueFor:context];
}
@@ -226,6 +233,44 @@ var CPBindingOperationAnd = 0,
return [[options objectForKey:CPContinuouslyUpdatesValueBindingOption] boolValue];
}
- (BOOL)handlesContentAsCompoundValue
{
var options = [_info objectForKey:CPOptionsKey];
return [[options objectForKey:CPHandlesContentAsCompoundValueBindingOption] boolValue];
}
/*!
Use this to avoid reacting to the notifications coming out of a reverseTransformedValue:.
*/
- (void)suppressSpecificNotificationFromObject:(id)anObject keyPath:(CPString)aKeyPath
{
if (!anObject)
return;
var uid = [anObject UID],
objectSuppressions = _suppressedNotifications[uid];
if (!objectSuppressions)
_suppressedNotifications[uid] = objectSuppressions = {};
objectSuppressions[aKeyPath] = YES;
}
/*!
Use this to cancel suppressSpecificNotificationFromObject:keyPath:.
*/
- (void)unsuppressSpecificNotificationFromObject:(id)anObject keyPath:(CPString)aKeyPath
{
if (!anObject)
return;
var uid = [anObject UID],
objectSuppressions = _suppressedNotifications[uid];
if (!objectSuppressions)
return;
delete objectSuppressions[aKeyPath];
}
@end
@implementation CPObject (KeyValueBindingCreation)
@@ -235,7 +280,6 @@ var CPBindingOperationAnd = 0,
[CPBinder exposeBinding:aBinding forClass:[self class]];
}
+ (Class)_binderClassForBinding:(CPString)theBinding
{
return [CPBinder class];
+11 -2
View File
@@ -149,9 +149,9 @@ var _CPLevelIndicatorBezelColor = nil,
var filledColor = _CPLevelIndicatorSegmentNormalColor,
value = [self doubleValue];
if (value < _criticalValue)
if (value <= _criticalValue)
filledColor = _CPLevelIndicatorSegmentCriticalColor;
else if (value < _warningValue)
else if (value <= _warningValue)
filledColor = _CPLevelIndicatorSegmentWarningColor;
for (var i = 0; i < segmentCount; i++)
@@ -223,6 +223,15 @@ var _CPLevelIndicatorBezelColor = nil,
return _isEditable;
}
- (CPView)hitTest:(CPPoint)aPoint
{
// Don't swallow clicks when displayed in a table.
if (![self isEditable])
return nil;
return [super hitTest:aPoint];
}
- (void)mouseDown:(CPEvent)anEvent
{
if (![self isEditable] || ![self isEnabled])
+27 -9
View File
@@ -711,10 +711,7 @@ var _CPMenuBarVisible = NO,
if (aView && !theWindow)
throw "In call to popUpMenuPositioningItem:atLocation:inView:callback:, view is not in any window.";
var delegate = [self delegate];
if ([delegate respondsToSelector:@selector(menuWillOpen:)])
[delegate menuWillOpen:self];
[self _menuWillOpen];
// Convert location to global coordinates if not already in them.
if (aView)
@@ -802,10 +799,7 @@ var _CPMenuBarVisible = NO,
+ (void)popUpContextMenu:(CPMenu)aMenu withEvent:(CPEvent)anEvent forView:(CPView)aView withFont:(CPFont)aFont
{
var delegate = [aMenu delegate];
if ([delegate respondsToSelector:@selector(menuWillOpen:)])
[delegate menuWillOpen:aMenu];
[aMenu _menuWillOpen];
if (!aFont)
aFont = [CPFont systemFontOfSize:12.0];
@@ -875,7 +869,15 @@ var _CPMenuBarVisible = NO,
*/
- (CPMenuItem)highlightedItem
{
return _highlightedIndex >= 0 ? _items[_highlightedIndex] : nil;
if (_highlightedIndex < 0)
return nil;
var highlightedItem = _items[_highlightedIndex];
if ([highlightedItem isSeparatorItem])
return nil;
return highlightedItem;
}
// Managing the Delegate
@@ -890,6 +892,22 @@ var _CPMenuBarVisible = NO,
return _delegate;
}
- (void)_menuWillOpen
{
var delegate = [self delegate];
if ([delegate respondsToSelector:@selector(menuWillOpen:)])
[delegate menuWillOpen:self];
}
- (void)_menuDidClose
{
var delegate = [self delegate];
if ([delegate respondsToSelector:@selector(menuDidClose:)])
[delegate menuDidClose:self];
}
// Handling Tracking
/*!
Cancels tracking.
+15 -7
View File
@@ -11,6 +11,7 @@ var STICKY_TIME_INTERVAL = 500,
@implementation _CPMenuManager: CPObject
{
CPTimeInterval _startTime;
BOOL _hasMouseGoneUpAfterStartedTracking;
int _scrollingState;
CGPoint _lastGlobalLocation;
@@ -87,6 +88,8 @@ var STICKY_TIME_INTERVAL = 500,
return [self trackMenuBarButtonEvent:anEvent];
}
_hasMouseGoneUpAfterStartedTracking = NO;
[self trackEvent:anEvent];
}
@@ -221,8 +224,13 @@ var STICKY_TIME_INTERVAL = 500,
[CPEvent startPeriodicEventsAfterDelay:0.0 withPeriod:0.04];
}
}
else if (type === CPLeftMouseUp && ([anEvent timestamp] - _startTime > (STICKY_TIME_INTERVAL + [activeMenu numberOfItems] * 5)))
[trackingMenu cancelTracking];
else if (type === CPLeftMouseUp)
{
if (_hasMouseGoneUpAfterStartedTracking)
[trackingMenu cancelTracking];
else
_hasMouseGoneUpAfterStartedTracking = YES;
}
}
// Prevent previous selected menu items from opening by stopping the timer if a
@@ -306,11 +314,7 @@ var STICKY_TIME_INTERVAL = 500,
// Hide all submenus.
[self showMenu:nil fromMenu:trackingMenu atPoint:nil];
var delegate = [trackingMenu delegate];
if ([delegate respondsToSelector:@selector(menuDidClose:)])
[delegate menuDidClose:trackingMenu];
[trackingMenu _menuDidClose];
if (_trackingCallback)
_trackingCallback([self trackingMenuContainer], trackingMenu);
@@ -379,6 +383,8 @@ var STICKY_TIME_INTERVAL = 500,
var count = _menuContainerStack.length,
index = count;
[newMenu _menuWillOpen];
// Hide all menus up to the base menu...
while (index--)
{
@@ -398,6 +404,8 @@ var STICKY_TIME_INTERVAL = 500,
[_CPMenuWindow poolMenuWindow:menuContainer];
[_menuContainerStack removeObjectAtIndex:index];
[menu _menuDidClose];
}
if (!newMenu)
+1
View File
@@ -496,6 +496,7 @@ CPOffState
if (_submenu)
{
[_submenu setSupermenu:_menu];
[_submenu setTitle:[self title]]
[self setTarget:_menu];
[self setAction:@selector(submenuAction:)];
+12 -1
View File
@@ -498,7 +498,7 @@ var CPObjectControllerContentKey = @"CPObjectControllerCo
- (CPString)description
{
return "<_CPObservableArray: "+[super description]+" >";
return "<_CPObservableArray: " + [super description] + " >";
}
- (id)initWithArray:(CPArray)anArray
@@ -710,6 +710,17 @@ var CPObjectControllerContentKey = @"CPObjectControllerCo
{
[[_controller selectedObjects] setValue:theValue forKeyPath:theKeyPath];
[_cachedValues removeObjectForKey:theKeyPath];
// Allow handlesContentAsCompoundValue to work, based on observation of Cocoa's
// NSArrayController - when handlesContentAsCompoundValue and setValue:forKey:@"selection.X"
// is called, the array controller causes the compound value to be rewritten if
// handlesContentAsCompoundValue == YES. Note that
// A) this doesn't use observation (observe: X is not visible in backtraces)
// B) it only happens through the selection proxy and not on arrangedObject.X, content.X
// or even selectedObjects.X.
// FIXME The main code for this should somehow be in CPArrayController and also work
// for table based row edits.
[[CPBinder getBinding:@"contentArray" forObject:_controller] _contentArrayDidChange];
}
- (void)setValue:(id)theValue forKey:(CPString)theKeyPath
+38
View File
@@ -1458,6 +1458,44 @@ var CPOutlineViewCoalesceSelectionNotificationStateOff = 0,
userInfo:[CPDictionary dictionaryWithObject:item forKey:"CPObject"]];
}
- (void)keyDown:(CPEvent)anEvent
{
var character = [anEvent charactersIgnoringModifiers],
modifierFlags = [anEvent modifierFlags];
// Check for the key events manually, as opposed to waiting for CPWindow to sent the actual action message
// in _processKeyboardUIKey:, because we might not want to handle the arrow events.
if (character !== CPRightArrowFunctionKey && character !== CPLeftArrowFunctionKey)
return [super keyDown:anEvent];
var rows = [self selectedRowIndexes],
indexes = [],
items = [];
[rows getIndexes:indexes maxCount:-1 inIndexRange:nil];
var i = 0,
c = [indexes count];
for (; i < c; i++)
items.push([self itemAtRow:indexes[i]]);
if (character === CPRightArrowFunctionKey)
{
for (var i = 0; i < c; i++)
[self expandItem:items[i]];
}
else if (character === CPLeftArrowFunctionKey)
{
for (var i = 0; i < c; i++)
[self collapseItem:items[i]];
}
[super keyDown:anEvent];
}
@end
// FIX ME: We're using with() here because Safari fails if we use anOutlineView._itemInfosForItems or whatever...
+2 -2
View File
@@ -2771,7 +2771,7 @@ Your delegate can implement this method to avoid subclassing the tableview to ad
*/
- (BOOL)canDragRowsWithIndexes:(CPIndexSet)rowIndexes atPoint:(CGPoint)mouseDownPoint
{
return YES;
return [rowIndexes count] > 0 && [self numberOfRows] > 0;
}
/*!
@@ -4610,7 +4610,7 @@ Your delegate can implement this method to avoid subclassing the tableview to ad
}
_wasSelectionBroken = true;
}
else if (_wasSelectionBroken && ((shouldGoUpward && i !== [selectedIndexes firstIndex]) || (!shouldGoUpward && i !== [selectedIndexes lastindex])))
else if (_wasSelectionBroken && ((shouldGoUpward && i !== [selectedIndexes firstIndex]) || (!shouldGoUpward && i !== [selectedIndexes lastIndex])))
{
shouldGoUpward ? i = [selectedIndexes firstIndex] - 1 : i = [selectedIndexes lastIndex];
_wasSelectionBroken = false;
+169 -68
View File
@@ -21,6 +21,8 @@
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
#import "../Foundation/Ref.h"
@import "CPControl.j"
@import "CPStringDrawing.j"
@import "CPCompatibility.j"
@@ -83,12 +85,11 @@ CPTextFieldStatePlaceholder = CPThemeState("placeholder");
CPColor _textFieldBackgroundColor;
id _placeholderString;
CPString _placeholderString;
CPString _stringValue;
id _delegate;
CPString _textDidChangeValue;
// NS-style Display Properties
CPTextFieldBezelStyle _bezelStyle;
BOOL _isBordered;
@@ -481,17 +482,17 @@ CPTextFieldStatePlaceholder = CPThemeState("placeholder");
[self setNeedsLayout];
_isEditing = NO;
_stringValue = [self stringValue];
#if PLATFORM(DOM)
var string = [self stringValue],
element = [self _inputElement],
var element = [self _inputElement],
font = [self currentValueForThemeAttribute:@"font"];
// generate the font metric
[font _getMetrics];
element.value = string;
element.value = _stringValue;
element.style.color = [[self currentValueForThemeAttribute:@"text-color"] cssString];
element.style.font = [font cssString];
element.style.zIndex = 1000;
@@ -548,8 +549,6 @@ CPTextFieldStatePlaceholder = CPThemeState("placeholder");
CPTextFieldInputOwner = self;
}, 0.0);
element.value = [self stringValue];
[[[self window] platformWindow] _propagateCurrentDOMEvent:YES];
CPTextFieldInputIsActive = YES;
@@ -572,17 +571,33 @@ CPTextFieldStatePlaceholder = CPThemeState("placeholder");
{
[self unsetThemeState:CPThemeStateEditing];
#if PLATFORM(DOM)
var element = [self _inputElement],
error = @"";
// If there is a formatter, always give it a chance to reject the resignation,
// even if the value has not changed.
if ([self _valueIsValid:element.value] === NO)
{
[self setThemeState:CPThemeStateEditing];
element.focus();
return NO;
}
#endif
// Cache the formatted string
_stringValue = [self stringValue];
_willBecomeFirstResponderByClick = NO;
[self _updatePlaceholderState];
[self setNeedsLayout];
#if PLATFORM(DOM)
var element = [self _inputElement];
if ([self stringValue] !== element.value)
[self _setStringValue:element.value];
CPTextFieldInputResigning = YES;
if (CPTextFieldInputIsActive)
@@ -610,7 +625,7 @@ CPTextFieldStatePlaceholder = CPThemeState("placeholder");
#endif
//post CPControlTextDidEndEditingNotification
// post CPControlTextDidEndEditingNotification
if (_isEditing)
{
_isEditing = NO;
@@ -625,6 +640,28 @@ CPTextFieldStatePlaceholder = CPThemeState("placeholder");
return YES;
}
- (BOOL)_valueIsValid:(CPString)aValue
{
#if PLATFORM(DOM)
var error = @"";
if ([self _setStringValue:aValue isNewValue:NO errorDescription:AT_REF(error)] === NO)
{
var acceptInvalidValue = NO;
if ([_delegate respondsToSelector:@selector(control:didFailToFormatString:errorDescription:)])
acceptInvalidValue = [_delegate control:self didFailToFormatString:[self _inputElement] errorDescription:error];
if (acceptInvalidValue === NO)
return NO;
}
#endif
return YES;
}
/*!
Text fields require panels to become key window, so this returns \c YES.
*/
@@ -685,11 +722,14 @@ CPTextFieldStatePlaceholder = CPThemeState("placeholder");
- (void)keyUp:(CPEvent)anEvent
{
var oldValue = [self stringValue];
[self _setStringValue:[self _inputElement].value];
#if PLATFORM(DOM)
if (oldValue !== [self stringValue])
var newValue = [self _inputElement].value;
if (newValue !== _stringValue)
{
[self _setStringValue:newValue];
if (!_isEditing)
{
_isEditing = YES;
@@ -699,6 +739,8 @@ CPTextFieldStatePlaceholder = CPThemeState("placeholder");
[self textDidChange:[CPNotification notificationWithName:CPControlTextDidChangeNotification object:self userInfo:nil]];
}
#endif
[[[self window] platformWindow] _propagateCurrentDOMEvent:YES];
}
@@ -732,45 +774,46 @@ CPTextFieldStatePlaceholder = CPThemeState("placeholder");
- (void)insertNewline:(id)sender
{
if (_isEditing)
if ([self _valueIsValid:_stringValue])
{
_isEditing = NO;
[self textDidEndEditing:[CPNotification notificationWithName:CPControlTextDidEndEditingNotification object:self userInfo:nil]];
}
if (![self action] || [self sendAction:[self action] to:[self target]])
{
if (_isEditing)
{
_isEditing = NO;
[self textDidEndEditing:[CPNotification notificationWithName:CPControlTextDidEndEditingNotification object:self userInfo:nil]];
}
[self sendAction:[self action] to:[self target]];
[self selectText:nil];
[self selectAll:nil];
}
}
[[[self window] platformWindow] _propagateCurrentDOMEvent:NO];
}
- (void)insertNewlineIgnoringFieldEditor:(id)sender
{
var oldValue = [self stringValue];
[self _inputElement].value += CPNewlineCharacter;
[self _setStringValue:[self _inputElement].value];
if (oldValue !== [self stringValue])
{
if (!_isEditing)
{
_isEditing = YES;
[self textDidBeginEditing:[CPNotification notificationWithName:CPControlTextDidBeginEditingNotification object:self userInfo:nil]];
}
[self textDidChange:[CPNotification notificationWithName:CPControlTextDidChangeNotification object:self userInfo:nil]];
}
[self _insertCharacterIgnoringFieldEditor:CPNewlineCharacter];
}
- (void)insertTabIgnoringFieldEditor:(id)sender
{
var oldValue = [self stringValue];
[self _insertCharacterIgnoringFieldEditor:CPTabCharacter];
}
[self _inputElement].value += CPTabCharacter;
[self _setStringValue:[self _inputElement].value];
- (void)_insertCharacterIgnoringFieldEditor:(CPString)aCharacter
{
#if PLATFORM(DOM)
if (oldValue !== [self stringValue])
var oldValue = _stringValue,
range = [self selectedRange],
element = [self _inputElement];
element.value = [element.value stringByReplacingCharactersInRange:[self selectedRange] withString:aCharacter];
[self _setStringValue:element.value];
// NOTE: _stringValue is now the current input element value
if (oldValue !== _stringValue)
{
if (!_isEditing)
{
@@ -780,6 +823,8 @@ CPTextFieldStatePlaceholder = CPThemeState("placeholder");
[self textDidChange:[CPNotification notificationWithName:CPControlTextDidChangeNotification object:self userInfo:nil]];
}
#endif
}
- (void)textDidBlur:(CPNotification)note
@@ -810,15 +855,8 @@ CPTextFieldStatePlaceholder = CPThemeState("placeholder");
[super textDidChange:note];
}
- (void)sendAction:(SEL)anAction to:(id)anObject
{
[self _reverseSetBinding];
[CPApp sendAction:anAction to:anObject from:self];
}
/*!
Returns the string the text field.
Returns the string in the text field.
*/
- (id)objectValue
{
@@ -827,24 +865,84 @@ CPTextFieldStatePlaceholder = CPThemeState("placeholder");
/*
@ignore
Sets the internal string value without updating the value in the input element
Sets the internal string value without updating the value in the input element.
This should only be invoked when the underlying text element's value has changed.
*/
- (void)_setStringValue:(id)aValue
- (BOOL)_setStringValue:(CPString)aValue
{
[self willChangeValueForKey:@"objectValue"];
[super setObjectValue:String(aValue)];
[self _updatePlaceholderState];
[self didChangeValueForKey:@"objectValue"];
return [self _setStringValue:aValue isNewValue:YES errorDescription:nil];
}
/*
@ignore
Sets the internal string value without updating the value in the input element.
If there is a formatter and formatting fails, returns NO. Otherwise returns YES.
*/
- (BOOL)_setStringValue:(CPString)aValue isNewValue:(BOOL)isNewValue errorDescription:(CPStringRef)anError
{
_stringValue = aValue;
var objectValue = aValue,
formatter = [self formatter],
result = YES;
if (formatter)
{
var object = nil;
if ([formatter getObjectValue:AT_REF(object) forString:aValue errorDescription:anError])
objectValue = object;
else
{
objectValue = undefined; // Mark the value as invalid
result = NO;
}
isNewValue |= objectValue !== [super objectValue];
}
if (isNewValue)
{
[self willChangeValueForKey:@"objectValue"];
[super setObjectValue:objectValue];
[self _updatePlaceholderState];
[self didChangeValueForKey:@"objectValue"];
}
return result;
}
- (void)setObjectValue:(id)aValue
{
[super setObjectValue:aValue];
var formatter = [self formatter];
if (formatter)
{
// If there is a formatter, make sure the object value can be formatted successfully
var formattedString = [self hasThemeState:CPThemeStateEditing] ? [formatter editingStringForObjectValue:aValue] : [formatter stringForObjectValue:aValue];
if (formattedString === nil)
{
var value = nil;
// Formatting failed, get an "empty" object by formatting an empty string.
// If that fails, the value is undefined.
if ([formatter getObjectValue:AT_REF(value) forString:@"" errorDescription:nil] === NO)
value = undefined;
[super setObjectValue:value];
}
}
_stringValue = [self stringValue];
#if PLATFORM(DOM)
if (CPTextFieldInputOwner === self || [[self window] firstResponder] === self)
[self _inputElement].value = aValue;
[self _inputElement].value = _stringValue;
#endif
[self _updatePlaceholderState];
@@ -852,9 +950,7 @@ CPTextFieldStatePlaceholder = CPThemeState("placeholder");
- (void)_updatePlaceholderState
{
var string = [self stringValue];
if ((!string || string.length === 0) && ![self hasThemeState:CPThemeStateEditing])
if ((!_stringValue || _stringValue.length === 0) && ![self hasThemeState:CPThemeStateEditing])
[self setThemeState:CPTextFieldStatePlaceholder];
else
[self unsetThemeState:CPTextFieldStatePlaceholder];
@@ -920,7 +1016,7 @@ CPTextFieldStatePlaceholder = CPThemeState("placeholder");
minSize = [self currentValueForThemeAttribute:@"min-size"],
maxSize = [self currentValueForThemeAttribute:@"max-size"],
lineBreakMode = [self lineBreakMode],
text = ([self stringValue] || @" "),
text = (_stringValue || @" "),
textSize = _CGSizeMakeCopy(frameSize),
font = [self currentValueForThemeAttribute:@"font"];
@@ -991,8 +1087,7 @@ CPTextFieldStatePlaceholder = CPThemeState("placeholder");
return;
var pasteboard = [CPPasteboard generalPasteboard],
stringValue = [self stringValue],
stringForPasting = [stringValue substringWithRange:selectedRange];
stringForPasting = [_stringValue substringWithRange:selectedRange];
[pasteboard declareTypes:[CPStringPboardType] owner:nil];
[pasteboard setString:stringForPasting forType:CPStringPboardType];
@@ -1022,9 +1117,8 @@ CPTextFieldStatePlaceholder = CPThemeState("placeholder");
[self deleteBackward:sender];
var selectedRange = [self selectedRange],
stringValue = [self stringValue],
pasteString = [pasteboard stringForType:CPStringPboardType],
newValue = [stringValue stringByReplacingCharactersInRange:selectedRange withString:pasteString];
newValue = [_stringValue stringByReplacingCharactersInRange:selectedRange withString:pasteString];
[self setStringValue:newValue];
[self setSelectedRange:CPMakeRange(selectedRange.location + pasteString.length, 0)];
@@ -1038,6 +1132,8 @@ CPTextFieldStatePlaceholder = CPThemeState("placeholder");
if ([[self window] firstResponder] !== self)
return CPMakeRange(0, 0);
#if PLATFORM(DOM)
// we wrap this in try catch because firefox will throw an exception in certain instances
try
{
@@ -1064,6 +1160,8 @@ CPTextFieldStatePlaceholder = CPThemeState("placeholder");
// fall through to the return
}
#endif
return CPMakeRange(0, 0);
}
@@ -1072,6 +1170,8 @@ CPTextFieldStatePlaceholder = CPThemeState("placeholder");
if (![[self window] firstResponder] === self)
return;
#if PLATFORM(DOM)
var inputElement = [self _inputElement];
try
@@ -1100,6 +1200,8 @@ CPTextFieldStatePlaceholder = CPThemeState("placeholder");
catch (e)
{
}
#endif
}
- (void)selectAll:(id)sender
@@ -1117,8 +1219,7 @@ CPTextFieldStatePlaceholder = CPThemeState("placeholder");
selectedRange.location += 1;
selectedRange.length -= 1;
var stringValue = [self stringValue],
newValue = [stringValue stringByReplacingCharactersInRange:selectedRange withString:""];
var newValue = [_stringValue stringByReplacingCharactersInRange:selectedRange withString:""];
[self setStringValue:newValue];
[self setSelectedRange:CPMakeRange(selectedRange.location, 0)];
@@ -1271,7 +1372,7 @@ CPTextFieldStatePlaceholder = CPThemeState("placeholder");
string = [self placeholderString];
else
{
string = [self stringValue];
string = _stringValue;
if ([self isSecure])
string = secureStringForString(string);
+6 -1
View File
@@ -519,7 +519,12 @@ CPThemeStateCircular = CPThemeState("circular");
- (void)setValue:(id)aValue
{
[self setValue:aValue forState:CPThemeStateNormal];
_cache = {};
if (aValue === undefined || aValue === nil)
_values = [CPDictionary dictionary];
else
_values = [CPDictionary dictionaryWithObject:aValue forKey:String(CPThemeStateNormal)];
}
- (void)setValue:(id)aValue forState:(CPThemeState)aState
+1 -1
View File
@@ -1015,7 +1015,7 @@ var TOP_MARGIN = 5.0,
_labelSize = [_labelField frame].size;
_minSize = CGSizeMake(MAX(_labelSize.width, minSize.width), _labelSize.height + minSize.height + LABEL_MARGIN + TOP_MARGIN);
_maxSize = CGSizeMake(MAX(_labelSize.width, minSize.width), 100000000.0);
_maxSize = CGSizeMake(MIN(_labelSize.width, maxSize.width), 100000000.0);
[_toolbar tile];
}
+26
View File
@@ -2543,6 +2543,32 @@ setBoundsOrigin:
return (_themeAttributes && _themeAttributes[aName] !== undefined);
}
- (void)registerThemeValues:(CPArray)themeValues
{
for (var i = 0; i < themeValues.length; ++i)
{
var attributeValueState = themeValues[i],
attribute = attributeValueState[0],
value = attributeValueState[1],
state = attributeValueState[2];
if (state)
[self setValue:value forThemeAttribute:attribute inState:state];
else
[self setValue:value forThemeAttribute:attribute];
}
}
- (void)registerThemeValues:(CPArray)themeValues inherit:(CPArray)inheritedValues
{
// Register inherited values first, then override those with the subtheme values.
if (inheritedValues)
[self registerThemeValues:inheritedValues];
if (themeValues)
[self registerThemeValues:themeValues];
}
- (CPView)createEphemeralSubviewNamed:(CPString)aViewName
{
return nil;
+9
View File
@@ -42,7 +42,16 @@
#include "DOM/CPPlatformString.j"
#else
@implementation CPPlatformString : CPBasePlatformString
+ (CGSize)sizeOfString:(CPString)aString withFont:(CPFont)aFont forWidth:(float)aWidth
{
return _CGSizeMakeZero();
}
+ (CPDictionary)metricsOfFont:(CPFont)aFont
{
return [CPDictionary dictionaryWithObjectsAndKeys:0, @"ascender", 0, @"descender", 0, @"lineHeight"];
}
@end
#endif
+1 -1
View File
@@ -225,7 +225,7 @@ var ItemSizes = { },
}
}
+ (void)registerThemeValues:(CPArray)themeValues forView:aView inherit:(CPArray)inheritedValues
+ (void)registerThemeValues:(CPArray)themeValues forView:(CPView)aView inherit:(CPArray)inheritedValues
{
// Register inherited values first, then override those with the subtheme values.
if (inheritedValues)
+22 -16
View File
@@ -1,10 +1,8 @@
require "buildr4osgi"
require File.join(File.dirname(__FILE__), "repositories.rb")
require File.join(File.dirname(__FILE__), "dependencies.rb")
# Keep this structure to allow the build system to update version numbers.
VERSION_NUMBER = "1.0.0.1"
VERSION_NUMBER = "1.0.0.006"
# Shorten expressions
@@ -24,24 +22,32 @@ define "cappuccino" do
file(_("target/Frameworks")).enhance do
mkdir_p "target/Frameworks/Debug"
system <<-BASH
export BUILD_PATH="target/Build"
jake debug release
BASH
export BUILD_PATH="target/Build"
jake debug release
BASH
cp_r "target/Build/Release/AppKit", "target/Frameworks/"
cp_r "target/Build/Release/Foundation", "target/Frameworks/"
cp_r "target/Build/Release/Objective-J", "target/Frameworks/"
cp_r "target/Build/Debug/AppKit", "target/Frameworks/Debug/"
cp_r "target/Build/Debug/Foundation", "target/Frameworks/Debug/"
cp_r "target/Build/Debug/Objective-J", "target/Frameworks/Debug/"
cp_r "target/Build/Release/AppKit", "target/Frameworks/"
cp_r "target/Build/Release/Foundation", "target/Frameworks/"
cp_r "target/Build/Release/Objective-J", "target/Frameworks/"
cp_r "target/Build/Debug/AppKit", "target/Frameworks/Debug/"
cp_r "target/Build/Debug/Foundation", "target/Frameworks/Debug/"
cp_r "target/Build/Debug/Objective-J", "target/Frameworks/Debug/"
end
# file(_("target/Build")).enhance do
# end
package(:bundle).include _("target/patch"), :as => "patch"
package(:bundle).include _("target/Frameworks"), :as => "content/Frameworks"
package(:bundle).include _("build.properties"), :as => "build.properties"
package(:bundle).meta_inf << file('META-INF/web-fragment.xml')
read_m = ::Buildr::Packaging::Java::Manifest.parse(File.read(_("META-INF/MANIFEST.MF"))).main
read_m["Copyright"]="Intalio, Inc 2011"
read_m["Bundle-Version"]=VERSION_NUMBER.gsub(/SNAPSHOT/, "#{Time.now.to_i}")
read_m.delete("Jetty-WarFragmentFolderPath")
read_m.delete("Jetty-WarPatchFragmentFolderPath")
package(:jar).with :manifest => read_m
#package(:jar).include _("target/patch"), :as => "patch"
package(:jar).include _("target/Frameworks"), :as => "META-INF/resources/Frameworks"
#do not include those: they are at best not read harmful.
#package(:bundle).include _("build.properties"), :as => "build.properties"
#package(:bundle).meta_inf << file('META-INF/web-fragment.xml')
end
+5
View File
@@ -72,6 +72,7 @@ CPRoundPlain = 1;
CPRoundDown = 2;
CPRoundUp = 3;
CPRoundBankers = 4;
_CPRoundHalfDown = 5; // Private API rounding mode used by CPNumberFormatter.
//Exceptions
CPDecimalNumberOverflowException = @"CPDecimalNumberOverflowException";
@@ -1330,6 +1331,10 @@ function CPDecimalRound(result, dcm, scale ,roundingMode)
n = result._mantissa[l];
up = (n >= 5);
break;
case _CPRoundHalfDown:
n = result._mantissa[l];
up = (n > 5);
break;
case CPRoundBankers:
n = result._mantissa[l];
if (n > 5)
+41 -25
View File
@@ -33,6 +33,8 @@
make sure that you cannot configure the public subclasses CPDateFormatter and CPNumberFormatter to satisfy your requirements.
*/
#import "Ref.h"
@import "CPException.j"
@import "CPObject.j"
@@ -80,19 +82,19 @@
/*!
The default implementation of this method raises an exception.
When implementing a subclass, return by reference the object anObject after creating it from string.
Return YES if the conversion is successful. If you return NO, also return by indirection (in error)
When implementing a subclass, return by reference the object anObject after creating it from aString.
Return \c YES if the conversion is successful. If you return \c NO, also return by reference (in anError)
a localized user-presentable CPString object that explains the reason why the conversion failed; the delegate
(if any) of the CPControl object managing the cell can then respond to the failure in
control:didFailToFormatString:errorDescription:. However, if error is nil, the sender is not interested in
(if any) of the CPControl object can then respond to the failure in
control:didFailToFormatString:errorDescription:. However, if anError is nil, the sender is not interested in
the error description, and you should not attempt to assign one.
@param anObject if conversion is successful, upon return contains the object created from the string
@param aString the string to parse.
@param anError if non-nil, if there is an error during the conversion, upon return contains an CPString object that describes the problem.
@return BOOL YES if the conversion from the string to a view content object was successful, otherwise NO.
@return BOOL \c YES if the conversion from the string to a view content object was successful, otherwise \c NO.
*/
- (BOOL)getObjectValue:(id)anObject forString:(CPString)aString errorDescription:(CPString)anError
- (BOOL)getObjectValue:(idRef)anObject forString:(CPString)aString errorDescription:(CPStringRef)anError
{
_CPRaiseInvalidAbstractInvocation(self, _cmd);
return NO;
@@ -104,11 +106,11 @@
This method is invoked each time the user presses a key while the cell has the keyboard focus it lets you verify and
edit the cell text as the user types it.
In a subclass implementation, evaluate partialString according to the context, edit the text if necessary, and return
by reference any edited string in newString. Return YES if partialString is acceptable and NO if partialString is unacceptable.
If you return NO and newString is nil, the cell displays partialString minus the last character typed. If you return NO, you can
also return by indirection an CPString object (in error) that explains the reason why the validation failed; the delegate (if any)
of the CPControl object managing the cell can then respond to the failure in control:didFailToValidatePartialString:errorDescription:.
In a subclass implementation, evaluate aPartialString according to the context, edit the text if necessary, and return
by reference any edited string in aNewString. Return \c YES if aPartialString is acceptable and \c NO if aPartialString is unacceptable.
If you return \c NO and aNewString is nil, the control displays aPartialString minus the last character typed. If you return \c NO, you can
also return by reference a CPString object (in anError) that explains the reason why the validation failed; the delegate (if any)
of the CPControl can then respond to the failure in control:didFailToValidatePartialString:errorDescription:.
The selection range will always be set to the end of the text if replacement occurs.
This method is a compatibility method. If a subclass overrides this method and does not override
@@ -118,12 +120,16 @@
@param aPartialString the text currently in the view.
@param aNewString if aPartialString needs to be modified, upon return contains the replacement string.
@param anError if non-nil, if validation fails contains a CPString object that describes the problem.
@return YES if aPartialString is an acceptable value, otherwise NO.
@return \c YES if aPartialString is an acceptable value, otherwise \c NO.
*/
- (BOOL)isPartialStringValid:(CPString)aPartialString newEditingString:(CPString)aNewString errorDescription:(CPString)anError
- (BOOL)isPartialStringValid:(CPString)aPartialString newEditingString:(CPStringRef)aNewString errorDescription:(CPStringRef)anError
{
_CPRaiseInvalidAbstractInvocation(self, _cmd);
return NO;
AT_DEREF(aPartialString, nil);
if (anError)
AT_DEREF(anError, nil);
return YES;
}
/*!
@@ -131,24 +137,35 @@
not necessarily at the end of the string, and preserve the selection (or set a different one, such as selecting the erroneous part of
the string the user has typed).
In a subclass implementation, evaluate partialString according to the context. Return YES if partialStringPtr is acceptable and NO if partialStringPtr
is unacceptable. Assign a new string to partialStringPtr and a new range to proposedSelRangePtr and return NO if you want to replace the string and
change the selection range. If you return NO, you can also return by indirection an CPString object (in error) that explains the reason why the
validation failed; the delegate (if any) of the CPControl object managing the cell can then respond to the failure in
In a subclass implementation, evaluate aPartialString according to the context. Return \c YES if aPartialString is acceptable and \c NO if aPartialString
is unacceptable. Assign a new string by reference to aPartialString and a new range by reference to aProposedSelectedRange and return \c NO if you want to replace the string and
change the selection range. If you return \c NO, you can also return by reference a CPString object (in anError) that explains the reason why the
validation failed; the delegate (if any) of the CPControl can then respond to the failure in
control:didFailToValidatePartialString:errorDescription:.
@param aPartialString The new string to validate.
@param aProposedSelectedRange The selection range that will be used if the string is accepted or replaced.
@param originalString The original string, before the proposed change.
@param originalSelectedRange The selection range over which the change is to take place.
@param error If non-nil, if validation fails contains an CPString object that describes the problem.
@return YES if aPartialString is acceptable, otherwise NO.
@param anError If non-nil, if validation fails contains an CPString object that describes the problem.
@return \c YES if aPartialString is acceptable, otherwise \c NO.
*/
- (BOOL)isPartialStringValue:(CPString)aPartialString proposedSelectedRange:(CPRange)aProposedSelectedRange originalString:(CPString)originalString originalSelectedRange:(CPRange)originalSelectedRange errorDescription:(CPString)anError
- (BOOL)isPartialStringValid:(CPStringRef)aPartialString proposedSelectedRange:(CPRangeRef)aProposedSelectedRange originalString:(CPString)originalString originalSelectedRange:(CPRange)originalSelectedRange errorDescription:(CPStringRef)anError
{
_CPRaiseInvalidAbstractInvocation(self, _cmd);
return NO;
var newString = nil,
valid = [self isPartialStringValid:aPartialString newEditingString:AT_REF(newString) errorDescription:anError];
if (!valid)
{
AT_DEREF(aPartialString, newString);
// If a new string is passed back, the selection is always put at the end
if (newString !== nil)
AT_DEREF(aProposedSelectedRange, CPMakeRange(newString.length, 0));
}
return valid;
}
- (id)initWithCoder:(CPCoder)aCoder
@@ -158,7 +175,6 @@
- (void)encodeWithCoder:(CPCoder)aCoder
{
}
@end
+190
View File
@@ -0,0 +1,190 @@
/*
* CPNumberFormatter.j
* Foundation
*
* Created by Alexander Ljungberg.
* Copyright 2011, WireLoad 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 "Ref.h"
@import <Foundation/CPString.j>
@import <Foundation/CPFormatter.j>
@import <Foundation/CPDecimalNumber.j>
#define UPDATE_NUMBER_HANDLER_IF_NECESSARY() if (!_numberHandler) \
_numberHandler = [CPDecimalNumberHandler decimalNumberHandlerWithRoundingMode:_roundingMode scale:_maximumFractionalDigits raiseOnExactness:NO raiseOnOverflow:NO raiseOnUnderflow:NO raiseOnDivideByZero:YES];
#define SET_NEEDS_NUMBER_HANDLER_UPDATE() _numberHandler = nil;
CPNumberFormatterNoStyle = 0;
CPNumberFormatterDecimalStyle = 1;
CPNumberFormatterCurrencyStyle = 2;
CPNumberFormatterPercentStyle = 3;
CPNumberFormatterScientificStyle = 4;
CPNumberFormatterSpellOutStyle = 5;
CPNumberFormatterRoundCeiling = CPRoundUp;
CPNumberFormatterRoundFloor = CPRoundDown;
CPNumberFormatterRoundDown = CPRoundDown;
CPNumberFormatterRoundUp = CPRoundUp;
CPNumberFormatterRoundHalfEven = CPRoundBankers;
CPNumberFormatterRoundHalfDown = _CPRoundHalfDown;
CPNumberFormatterRoundHalfUp = CPRoundPlain;
/*!
@ingroup foundation
@class CPNumberFormatter
CPNumberFormatter takes a numeric NSNumber value and formats it as text for
display. It also supports the converse, taking text and interpreting it as a
CPNumber by configurable formatting rules.
*/
@implementation CPNumberFormatter : CPFormatter
{
CPNumberFormatterStyle _numberStyle @accessors(property=numberStyle);
CPString _perMillSymbol @accessors(property=perMillSymbol);
CPNumberFormatterRoundingMode _roundingMode @accessors(property=roundingMode);
CPUInteger _maximumFractionalDigits @accessors(property=maximalFractionalDigits);
CPDecimalNumberHandler _numberHandler;
}
- (id)init
{
if (self = [super init])
{
_roundingMode = CPNumberFormatterRoundHalfUp;
_maximumFractionalDigits = 3;
}
return self;
}
- (CPString)stringFromNumber:(CPNumber)number
{
// TODO Add locale support.
switch(_numberStyle)
{
case CPNumberFormatterDecimalStyle:
UPDATE_NUMBER_HANDLER_IF_NECESSARY();
var dcmn = [CPDecimalNumber numberWithFloat:number];
dcmn = [dcmn decimalNumberByRoundingAccordingToBehavior:_numberHandler];
var output = [dcmn descriptionWithLocale:nil],
parts = [output componentsSeparatedByString:"."], // FIXME Locale specific.
preFraction = parts[0],
fraction = parts.length > 1 ? parts[1] : "",
preFractionLength = [preFraction length],
commaPosition = 3,
perMillSymbol = [self _effectivePerMillSymbol];
// TODO This is just a temporary solution. Should be generalised.
// Add in thousands separators.
if (perMillSymbol)
while(commaPosition < [preFraction length])
{
preFraction = [preFraction stringByReplacingCharactersInRange:CPMakeRange(commaPosition, 0) withString:perMillSymbol];
commaPosition += 4;
}
if (fraction)
return preFraction + "." + fraction;
else
return preFraction;
default:
return [number description];
}
}
- (CPNumber)numberFromString:(CPString)string
{
// TODO
return parseFloat(string);
}
- (CPString)stringForObjectValue:(id)anObject
{
if ([anObject isKindOfClass:[CPNumber class]])
return [self stringFromNumber:anObject];
else
return [anObject description];
}
- (CPString)editingStringForObjectValue:(id)anObject
{
return [self stringForObjectValue:anObject];
}
- (BOOL)getObjectValue:(id)anObject forString:(CPString)aString errorDescription:(CPString)anError
{
// TODO Error handling.
var value = [self numberFromString:aString];
AT_DEREF(anObject, value);
return YES;
}
/*!
@ignore
Return the perMillSymbol if set, otherwise the locale default.
*/
- (CPString)_effectivePerMillSymbol
{
if (_perMillSymbol === nil || _perMillSymbol === undefined)
return ","; // (FIXME US Locale specific.)
return _perMillSymbol;
}
- (void)setRoundingMode:(CPNumberFormatterRoundingMode)aRoundingMode
{
_roundingMode = aRoundingMode;
SET_NEEDS_NUMBER_HANDLER_UPDATE();
}
- (void)setMaximumFractionDigits:(CPUInteger)aNumber
{
_maximumFractionalDigits = aNumber;
SET_NEEDS_NUMBER_HANDLER_UPDATE();
}
@end
var CPNumberFormatterStyleKey = "CPNumberFormatterStyleKey";
@implementation CPNumberFormatter (CPCoding)
- (id)initWithCoder:(CPCoder)aCoder
{
self = [super initWithCoder:aCoder];
if (self)
{
_numberStyle = [aCoder decodeIntForKey:CPNumberFormatterStyleKey];
}
return self;
}
- (void)encodeWithCoder:(CPCoder)aCoder
{
[super encodeWithCoder:aCoder];
[aCoder encodeInt:_numberStyle forKey:CPNumberFormatterStyleKey];
}
@end
+2 -4
View File
@@ -184,9 +184,8 @@ var StandardUserDefaults;
}
[domain setObject:anObject forKey:aKey];
[self domainDidChange:aDomain];
_searchListNeedsReload = YES;
[self domainDidChange:aDomain];
}
/*!
@@ -211,9 +210,8 @@ var StandardUserDefaults;
return;
[domain removeObjectForKey:aKey];
[self domainDidChange:aDomain];
_searchListNeedsReload = YES;
[self domainDidChange:aDomain];
}
/*!
+1
View File
@@ -49,6 +49,7 @@
@import "CPNotificationCenter.j"
@import "CPNull.j"
@import "CPNumber.j"
@import "CPNumberFormatter.j"
@import "CPObject.j"
@import "CPObjJRuntime.j"
@import "CPOperation.j"
+25
View File
@@ -0,0 +1,25 @@
source "http://rubygems.org"
gem "atoulme-Antwrap", "0.7.1" # -java
gem "bouncy-castle-java", "1.5.0146.1"
gem "builder", "2.1.2"
gem "buildr", "1.4.5" # -java
gem "buildr4osgi", "0.9.6"
gem "bundler", "1.0.0.rc.5"
gem "diff-lcs", "1.1.2"
gem "highline", "1.5.1"
gem "hoe", "2.3.3"
gem "jruby-openssl", "0.7.4"
gem "json_pure", "1.4.3"
gem "manifest", "0.0.8"
gem "minitar", "0.5.3"
gem "net-sftp", "2.0.4"
gem "net-ssh", "2.0.23" # 2.1.4
gem "rake", "0.8.7" # 0.9.1
gem "rspec", "2.1.0"
gem "rspec-core", "2.1.0"
gem "rspec-expectations", "2.1.0"
gem "rspec-mocks", "2.1.0"
gem "rubyforge", "2.0.3"
gem "rubyzip", "0.9.4"
gem "xml-simple", "1.0.12"
+81
View File
@@ -0,0 +1,81 @@
GEM
remote: http://rubygems.org/
specs:
atoulme-Antwrap (0.7.1-java)
bouncy-castle-java (1.5.0146.1)
builder (2.1.2)
buildr (1.4.5-java)
atoulme-Antwrap (= 0.7.1)
builder (= 2.1.2)
diff-lcs (= 1.1.2)
highline (= 1.5.1)
hoe (= 2.3.3)
jruby-openssl (>= 0.7)
json_pure (= 1.4.3)
minitar (= 0.5.3)
net-sftp (= 2.0.4)
net-ssh (= 2.0.23)
rake (= 0.8.7)
rspec (= 2.1.0)
rspec-core (= 2.1.0)
rspec-expectations (= 2.1.0)
rspec-mocks (= 2.1.0)
rubyforge (= 2.0.3)
rubyzip (= 0.9.4)
xml-simple (= 1.0.12)
buildr4osgi (0.9.6)
manifest (= 0.0.8)
diff-lcs (1.1.2)
highline (1.5.1)
hoe (2.3.3)
rake (>= 0.8.7)
rubyforge (>= 1.0.4)
jruby-openssl (0.7.4)
bouncy-castle-java
json_pure (1.4.3)
manifest (0.0.8)
minitar (0.5.3)
net-sftp (2.0.4)
net-ssh (>= 2.0.9)
net-ssh (2.0.23)
rake (0.8.7)
rspec (2.1.0)
rspec-core (~> 2.1.0)
rspec-expectations (~> 2.1.0)
rspec-mocks (~> 2.1.0)
rspec-core (2.1.0)
rspec-expectations (2.1.0)
diff-lcs (~> 1.1.2)
rspec-mocks (2.1.0)
rubyforge (2.0.3)
json_pure (>= 1.1.7)
rubyzip (0.9.4)
xml-simple (1.0.12)
PLATFORMS
java
DEPENDENCIES
atoulme-Antwrap (= 0.7.1)
bouncy-castle-java (= 1.5.0146.1)
builder (= 2.1.2)
buildr (= 1.4.5)
buildr4osgi (= 0.9.6)
bundler (= 1.0.0.rc.5)
diff-lcs (= 1.1.2)
highline (= 1.5.1)
hoe (= 2.3.3)
jruby-openssl (= 0.7.4)
json_pure (= 1.4.3)
manifest (= 0.0.8)
minitar (= 0.5.3)
net-sftp (= 2.0.4)
net-ssh (= 2.0.23)
rake (= 0.8.7)
rspec (= 2.1.0)
rspec-core (= 2.1.0)
rspec-expectations (= 2.1.0)
rspec-mocks (= 2.1.0)
rubyforge (= 2.0.3)
rubyzip (= 0.9.4)
xml-simple (= 1.0.12)
+2 -2
View File
@@ -3,7 +3,7 @@ Bundle-ManifestVersion: 2
Bundle-Name: cappuccinofrag
Bundle-SymbolicName: com.intalio.cloud.cappuccino
Fragment-Host: com.intalio.cloud.carbon
Jetty-WarFragmentFolderPath: /content
Jetty-WarPatchFragmentFolderPath: /patch
Jetty-WarPatchFragmentFolderPath: /
Bundle-Version: 1.0.0.qualifier
Bundle-RequiredExecutionEnvironment: JavaSE-1.6
Eclipse-BundleShape: dir
-17
View File
@@ -1,17 +0,0 @@
<?xml version="1.0" encoding="ISO-8859-1"?>
<web-fragment
xmlns="http://java.sun.com/xml/ns/javaee"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-app_3_0.xsd"
metadata-complete="false"
version="3.0">
<servlet>
<servlet-name>cappuccino-fragment</servlet-name>
</servlet>
<servlet-mapping>
<servlet-name>cappuccino-fragment</servlet-name>
<url-pattern>/carbon</url-pattern>
</servlet-mapping>
</web-fragment>
@@ -836,8 +836,7 @@ BundleTask.prototype.defineSourceTasks = function()
environmentSources.forEach(function(/*String*/ aFilename)
{
// if this file doesn't exist or isn't a .j file, don't preprocess it.
if (!FILE.exists(aFilename) || FILE.extension(aFilename) !== '.j')
if (!FILE.exists(aFilename))
return;
var relativePath = aFilename.substring(basePathLength ? basePathLength + 1 : basePathLength),
@@ -845,8 +844,19 @@ BundleTask.prototype.defineSourceTasks = function()
filedir (compiledEnvironmentSource, [aFilename], function()
{
TERM.stream.write("Compiling [\0blue(" + anEnvironment + "\0)] \0purple(" + aFilename + "\0)").flush();
var compiled = require("objective-j/compiler").compile(aFilename, environmentCompilerFlags);
var compile
// if this file doesn't exist or isn't a .j file, don't preprocess it.
if (FILE.extension(aFilename) !== ".j")
{
TERM.stream.write("Including [\0blue(" + anEnvironment + "\0)] \0purple(" + aFilename + "\0)").flush();
var compiled = FILE.read(aFilename, { charset:"UTF-8" });
}
else
{
TERM.stream.write("Compiling [\0blue(" + anEnvironment + "\0)] \0purple(" + aFilename + "\0)").flush();
var compiled = require("objective-j/compiler").compile(aFilename, environmentCompilerFlags);
}
TERM.stream.print(Array(Math.round(compiled.length / 1024) + 3).join("."));
FILE.write(compiledEnvironmentSource, compiled, { charset:"UTF-8" });
});
+26
View File
@@ -33,6 +33,15 @@ var CLS_CLASS = 0x1,
#define GETMETA(aClass) (ISMETA(aClass) ? aClass : aClass.isa)
#define ISINITIALIZED(aClass) GETINFO(GETMETA(aClass), CLS_INITIALIZED)
// MAXIMUM_RECURSION_CHECKS
// If defined, objj_msgSend will check for recursion deeper than MAXIMUM_RECURSION_DEPTH and
// throw an error if found. While crude, this can be helpful when your JavaScript debugger
// crashes on recursion errors (e.g. Safari) or ignores them (e.g. Chrome).
// #define MAXIMUM_RECURSION_CHECKS
#define MAXIMUM_RECURSION_DEPTH 80
GLOBAL(objj_ivar) = function(/*String*/ aName, /*String*/ aType)
{
this.name = aName;
@@ -604,6 +613,10 @@ DISPLAY_NAME(ivar_getTypeEncoding);
// Sending Messages
#ifdef MAXIMUM_RECURSION_CHECKS
var __objj_msgSend__StackDepth = 0;
#endif
GLOBAL(objj_msgSend) = function(/*id*/ aReceiver, /*SEL*/ aSelector)
{
if (aReceiver == nil)
@@ -613,6 +626,13 @@ GLOBAL(objj_msgSend) = function(/*id*/ aReceiver, /*SEL*/ aSelector)
CLASS_GET_METHOD_IMPLEMENTATION(var implementation, isa, aSelector);
#ifdef MAXIMUM_RECURSION_CHECKS
if (__objj_msgSend__StackDepth++ > MAXIMUM_RECURSION_DEPTH)
throw new Error("Maximum call stack depth exceeded.");
try {
#endif
switch(arguments.length)
{
case 2: return implementation(aReceiver, aSelector);
@@ -621,6 +641,12 @@ GLOBAL(objj_msgSend) = function(/*id*/ aReceiver, /*SEL*/ aSelector)
}
return implementation.apply(aReceiver, arguments);
#ifdef MAXIMUM_RECURSION_CHECKS
} finally {
__objj_msgSend__StackDepth--;
}
#endif
}
DISPLAY_NAME(objj_msgSend);
+42
View File
@@ -80,6 +80,7 @@
[arrayController insertObject:object atArrangedObjectIndex:1];
[self assert:object equals:[[arrayController arrangedObjects] objectAtIndex:1]];
[self assertTrue:[[arrayController content] containsObject:object] message:@"object should be inserted into content"];
}
- (void)testAddObjectUpdatesArrangedObjectsWithoutSortDescriptors
@@ -438,6 +439,31 @@
[self assert:1 equals:[observations count] message:@"exactly 1 notification for addObject (clearsFilterPredicate YES)"];
}
/*!
Test that if there is no filter predicate to clear, insertObject:atArrangedObjectIndex: with
clearsFilterPredicate YES does not send a false filterPredicate notification.
*/
- (void)testObservationDuringInsertObject_atArrangedIndex_
{
var arrayController = [self arrayController];
[arrayController addObserver:self forKeyPath:@"filterPredicate" options:CPKeyValueObservingOptionOld | CPKeyValueObservingOptionNew context:nil];
// Add something to clear.
[arrayController setFilterPredicate:[CPPredicate predicateWithFormat:@"(name != %@)", "Francisco"]];
observations = [];
var aPerson = [Employee employeeWithName:@"Alexander" department:[Department departmentWithName:@"Cosmic Path Finding"]];
[arrayController setClearsFilterPredicateOnInsertion:YES];
[self assert:0 equals:[observations count] message:@"no observations before insertObject test"];
[arrayController insertObject:aPerson atArrangedObjectIndex:1];
[self assert:1 equals:[observations count] message:@"exactly 1 notification for insertObject (clearsFilterPredicate YES)"];
// Now that the filter is already cleared, we should not get notified that it clears again on the second insert.
[arrayController insertObject:aPerson atArrangedObjectIndex:1];
[self assert:1 equals:[observations count] message:@"exactly 1 notification for insertObject x 2 (clearsFilterPredicate YES)"];
}
- (void)testCompoundKeyPaths
{
var departmentNameField = [[CPTextField alloc] init];
@@ -482,6 +508,22 @@
[self assertTrue:[[arrayController arrangedObjects] count] > 0];
}
/**
In a table with arranged contents like [1, 1, 2, 1], selecting the second '1' and removing it
should result in [1, 2, 1] - not [2]. E.g. we don't use removeObject:1 but only remove the
actually selected instance.
*/
- (void)testRemove_OneOfMultipleEqualObjects
{
var ac = [CPArrayController new],
contentArray = [1, 1, 2, 1];
[ac setContent:contentArray];
[self assert:[1, 1, 2, 1] equals:[ac arrangedObjects]];
[ac setSelectionIndexes:[CPIndexSet indexSetWithIndex:1]];
[ac remove:nil];
[self assert:[1, 2, 1] equals:[ac arrangedObjects] message:"only one copy of 1 removed + the right copy should be removed"];
}
- (void)observeValueForKeyPath:keyPath
ofObject:anActivity
change:change
+23
View File
@@ -249,6 +249,29 @@
CPLog(@"here: "+aKeyPath+" value: "+[anObject valueForKey:aKeyPath]);
}
- (void)testSuppressNotification
{
var control = [[CPTextField alloc] init],
anotherControl = [[CPTextField alloc] init];
[control setStringValue:@"brown"];
[control bind:CPValueBinding toObject:self withKeyPath:@"FOO" options:nil];
[self setValue:@"green" forKeyPath:@"FOO"];
[self assert:@"green" equals:[control stringValue] message:@"normal binding action"];
var binding = [CPBinder getBinding:CPValueBinding forObject:control];
[binding suppressSpecificNotificationFromObject:self keyPath:@"FOO"];
[self setValue:@"orange" forKeyPath:@"FOO"];
[self assert:@"green" equals:[control stringValue] message:@"binding update suppressed"];
[binding unsuppressSpecificNotificationFromObject:anotherControl keyPath:@"FOO"];
[self setValue:@"blue" forKeyPath:@"FOO"];
[self assert:@"green" equals:[control stringValue] message:@"binding update still suppressed"];
[binding unsuppressSpecificNotificationFromObject:self keyPath:@"FOO"];
[self setValue:@"octarine" forKeyPath:@"FOO"];
[self assert:@"octarine" equals:[control stringValue] message:@"binding update no longer suppressed"];
}
@end
@implementation BindingTester : CPObject
+25
View File
@@ -22,6 +22,31 @@
[self assert:[view valueForThemeAttribute:@"content-inset"].top equals:[decoded valueForThemeAttribute:@"content-inset"].top message:@"content-inset should unarchive correctly"];
}
- (void)testFormatters
{
var control = [[CPTextField alloc] initWithFrame:CGRectMakeZero()],
numberFormatter = [[CPNumberFormatter alloc] init];
[numberFormatter setNumberStyle:CPNumberFormatterDecimalStyle];
[numberFormatter setMaximumFractionDigits:3];
[control setFormatter:numberFormatter];
[control setStringValue:@"12.3456"];
[self assert:[CPNumber class] equals:[[control objectValue] class] message:@"object should be a number"];
// Note that the control stores the value with a different precision than the maximum fraction
// digits of the number formatter. It's a little surprising but this makes the implementation easier
// and Cocoa does it too.
[self assert:[CPNumber numberWithFloat:12.3456] equals:[control objectValue]];
[self assert:"12.346" equals:[control stringValue]];
[control setFloatValue:45.3456];
[self assertTrue:"45.346" === [control stringValue]];
[control setFormatter:nil];
[control setStringValue:@"12"];
[self assert:@"12" equals:[control objectValue]];
}
@end
@implementation CPTextFieldSubclass : CPTextField
+48
View File
@@ -0,0 +1,48 @@
@import <Foundation/Foundation.j>
@implementation CPNumberFormatterTest : OJTestCase
{
}
- (void)testDecimalStyle
{
var numberFormatter = [[CPNumberFormatter alloc] init];
[numberFormatter setNumberStyle:CPNumberFormatterDecimalStyle];
var formattedNumberString = [numberFormatter stringFromNumber:[CPNumber numberWithInt:123]];
[self assert:@"123" equals:formattedNumberString];
formattedNumberString = [numberFormatter stringFromNumber:[CPNumber numberWithFloat:122344.4563]];
// TODO Locale support. This test is sensitive to float precision.
[self assert:@"122,344.456" equals:formattedNumberString];
}
- (void)testRoundingMode
{
var numberFormatter = [[CPNumberFormatter alloc] init],
formattedNumberString;
[numberFormatter setNumberStyle:CPNumberFormatterDecimalStyle];
[numberFormatter setRoundingMode:CPNumberFormatterRoundUp];
formattedNumberString = [numberFormatter stringFromNumber:[CPNumber numberWithFloat:123.5672]];
[self assert:@"123.568" equals:formattedNumberString];
[numberFormatter setRoundingMode:CPNumberFormatterRoundDown];
formattedNumberString = [numberFormatter stringFromNumber:[CPNumber numberWithFloat:123.5679]];
[self assert:@"123.567" equals:formattedNumberString];
[numberFormatter setRoundingMode:CPNumberFormatterRoundHalfEven];
formattedNumberString = [numberFormatter stringFromNumber:[CPNumber numberWithFloat:123.5675]];
[self assert:@"123.568" equals:formattedNumberString];
formattedNumberString = [numberFormatter stringFromNumber:[CPNumber numberWithFloat:123.5665]];
[self assert:@"123.566" equals:formattedNumberString];
[numberFormatter setRoundingMode:CPNumberFormatterRoundHalfDown];
formattedNumberString = [numberFormatter stringFromNumber:[CPNumber numberWithFloat:123.5675]];
[self assert:@"123.567" equals:formattedNumberString];
[numberFormatter setRoundingMode:CPNumberFormatterRoundHalfUp];
formattedNumberString = [numberFormatter stringFromNumber:[CPNumber numberWithFloat:123.5675]];
[self assert:@"123.568" equals:formattedNumberString];
}
@end
+17 -1
View File
@@ -18,6 +18,7 @@ CPUserDefaultsTestKey2 = @"KEY2";
@implementation CPUserDefaultsTest : OJTestCase
{
CPUserDefaults target;
id lastObservedCPUserDefaultsTestKey1;
}
- (void)setUp
@@ -40,7 +41,6 @@ CPUserDefaultsTestKey2 = @"KEY2";
}
- (void)testSetObjectForKey
{
[target setObject:[CPArray arrayWithObjects:@"cell1", @"cell2"] forKey:CPUserDefaultsTestKey1];
@@ -133,7 +133,23 @@ CPUserDefaultsTestKey2 = @"KEY2";
[target removeObjectForKey:CPUserDefaultsTestKey1];
[self assert:[target dataForKey:CPUserDefaultsTestKey1] equals:nil];
}
- (void)testNotification
{
[target setDouble:5.0 forKey:CPUserDefaultsTestKey1];
[[CPNotificationCenter defaultCenter] addObserver:self selector:@selector(userDefaultsDidChange:) name:CPUserDefaultsDidChangeNotification object:target];
// Prod the class to resolve any outstanding _searchListNeedsReload's.
[self assert:5.0 equals:[target objectForKey:CPUserDefaultsTestKey1] message:"normal read"];
[target setDouble:10.0 forKey:CPUserDefaultsTestKey1];
[self assert:[target objectForKey:CPUserDefaultsTestKey1] equals:lastObservedCPUserDefaultsTestKey1 message:"should observe new value"];
}
- (void)userDefaultsDidChange:(CPNotification)aNotification
{
lastObservedCPUserDefaultsTestKey1 = [target objectForKey:CPUserDefaultsTestKey1];
}
@end
@@ -0,0 +1,205 @@
/*
* AppController.j
* CPFormatterTest
*
* Created by aparajita on June 30, 2011.
* Copyright 2011, Victory-Heart Productions All rights reserved.
*/
@import <Foundation/CPObject.j>
@import "DateFormatter.j"
var RecordData = [
{name:"Tom", age:34},
{name:"Dick", age:27},
{name:"Harry", age:50}
];
var randomFromTo = function(from, to)
{
return Math.floor(Math.random() * (to - from + 1) + from);
};
@implementation AppController : CPObject
{
CPWindow theWindow;
CPTextField dateField1;
CPTextField dateField2;
CPTextField error1;
CPTextField textField;
CPTextField error2;
CPTextField recordField;
CPPopUpButton recordMenu;
}
- (void)awakeFromCib
{
[theWindow setInitialFirstResponder:dateField1];
[dateField1 setFormatter:[DateFormatter formatterWithDisplayFormat:@"D M jS, Y" editingFormat:@"m/j/Y" emptyIsValid:NO]];
[dateField2 setFormatter:[DateFormatter formatterWithDisplayFormat:@"m-d-Y" editingFormat:@"m-d-Y" emptyIsValid:YES]];
[dateField2 setDelegate:self];
[[CPNotificationCenter defaultCenter] addObserver:self
selector:@selector(resetErrorMessage:)
name:CPTextFieldDidFocusNotification
object:nil];
[textField setFormatter:[TextFormatter new]];
[textField setDelegate:self];
[error1 setStringValue:@""];
[error2 setStringValue:@""];
[recordField setFormatter:[ContactFormatter new]];
[recordField setObjectValue:RecordData[0]];
}
- (void)selectRecord:(id)sender
{
var record = RecordData[[sender selectedIndex]];
[recordField setObjectValue:record];
}
- (void)setDate1:(id)sender
{
[self setDate:dateField1];
}
- (void)setDate2:(id)sender
{
[self setDate:dateField2];
}
- (void)setDate:(CPTextField)field
{
var date = new Date(randomFromTo(1931, 2012), randomFromTo(0, 11), randomFromTo(1, 31));
[field setObjectValue:date];
}
- (void)objectValue1:(id)sender
{
CPLog.info("Date 1: %s", [[dateField1 objectValue] description]);
}
- (void)objectValue2:(id)sender
{
CPLog.info("Date 2: %s", [[dateField2 objectValue] description]);
}
- (void)setNil:(id)sender
{
[dateField1 setStringValue:nil]; // should log a warning and do nothing
}
- (void)setEmptyOK:(id)sender
{
[[dateField2 formatter] setEmptyIsValid:[sender state] === CPOnState];
}
- (void)controlTextDidChange:(CPNotification)aNotification
{
var field = [aNotification object],
error;
if (field === dateField2)
error = error1;
else if (field === textField)
error = error2;
[error setStringValue:@""];
}
- (BOOL)control:(CPControl)aControl didFailToFormatString:(CPString)aString errorDescription:(CPString)anError
{
CPLog.info("control:didFailToFormatString:%s errorDescription:%s", aString, anError);
if (anError)
{
var error;
if (aControl === dateField2)
error = error1;
else if (aControl === textField)
error = error2;
[error setStringValue:anError];
}
return NO;
}
- (void)resetErrorMessage:(CPNotification)aNotification
{
[error2 setStringValue:@""];
}
@end
@implementation TextFormatter : CPFormatter
- (CPString)stringForObjectValue:(id)anObject
{
var result;
if ([anObject isKindOfClass:[CPString class]])
result = [self errorDescriptionForString:anObject] === nil ? anObject : nil;
else
result = nil;
console.log("stringForObjectValue:%s ==> %s", [anObject description], result);
return result;
}
- (BOOL)getObjectValue:(CPStringRef)anObject forString:(CPString)aString errorDescription:(CPStringRef)anError
{
var error = [self errorDescriptionForString:aString];
if (error)
{
anObject(nil);
if (anError)
anError(error);
}
else
anObject(aString);
result = error === nil;
console.log("getObjectValue:forString:%s ==> %s", aString, result);
return result;
}
- (CPString)errorDescriptionForString:(CPString)aString
{
if (aString.length > 7)
return @"Maximum length is 7 characters.";
else if (!/^[a-zA-Z0-9]*$/.test(aString))
return @"Invalid characters in string.";
else
return nil;
}
@end
@implementation ContactFormatter : CPFormatter
- (CPString)stringForObjectValue:(id)anObject
{
if (anObject && typeof(anObject) === "object" && anObject.hasOwnProperty("name"))
return [CPString stringWithFormat:@"%s (age %d)", anObject.name, anObject.age];
else
return nil;
}
- (BOOL)getObjectValue:(CPStringRef)anObject forString:(CPString)aString errorDescription:(CPStringRef)anError
{
// We don't support reverse conversion
return NO;
}
@end
@@ -0,0 +1,125 @@
/*
* AppController.j
* CPFormatterTest
*
* Created by aparajita on July 6, 2011.
* Copyright 2011, Victory-Heart Productions All rights reserved.
*/
@import <Foundation/CPFormatter.j>
@implementation DateFormatter : CPFormatter
{
CPString displayFormat;
CPString editingFormat;
BOOL emptyIsValid @accessors(getter=isEmptyValid);
}
+ (DateFormatter)formatterWithDisplayFormat:(CPString)aDisplayFormat editingFormat:(CPString)anEditingFormat emptyIsValid:(BOOL)emptyValid
{
return [[self alloc] initWithDisplayFormat:aDisplayFormat editingFormat:anEditingFormat emptyIsValid:emptyValid];
}
- (id)initWithDisplayFormat:(CPString)aDisplayFormat editingFormat:(CPString)anEditingFormat emptyIsValid:(BOOL)emptyValid
{
self = [super init];
if (self)
{
displayFormat = aDisplayFormat;
editingFormat = anEditingFormat;
emptyIsValid = emptyValid;
}
return self;
}
- (CPString)stringForObjectValue:(id)anObject
{
var result;
if ([anObject isKindOfClass:[CPDate class]])
result = anObject.dateFormat(displayFormat);
else
result = nil;
console.log("stringForObjectValue:%s ==> %s", [anObject description], result);
return result;
}
- (CPString)editingStringForObjectValue:(id)anObject
{
var result;
if ([anObject isKindOfClass:[CPDate class]])
result = anObject.dateFormat(editingFormat);
else
result = nil;
console.log("editingStringForObjectValue:%s ==> %s", [anObject description], result);
return result;
}
- (BOOL)getObjectValue:(CPStringRef)anObject forString:(CPString)aString errorDescription:(CPStringRef)anError
{
var result;
if (anError)
anError(nil);
if (aString.length === 0)
{
anObject(nil);
result = emptyIsValid;
if (anError)
anError(@"Please enter a date.");
}
else
{
var date = Date.parseDate(aString, editingFormat);
anObject(date);
if (date === nil && anError)
anError(@"Invalid date format.");
result = date !== nil;
}
console.log("getObjectValue:forString:%s ==> %s", aString, result);
return result;
}
@end
var DateFormatterDisplayFormatKey = @"DateFormatterDisplayFormatKey",
DateFormatterEditingFormatKey = @"DateFormatterEditingFormatKey",
DateFormatterEmptyIsValidKey = @"DateFormatterEmptyIsValidKey";
@implementation DateFormatter (CPCoding)
- (id)initWithCoder:(CPCoder)aCoder
{
self = [super initWithCoder:aCoder];
if (self)
{
displayFormat = [aCoder decodeObjectForKey:DateFormatterDisplayFormatKey];
editingFormat = [aCoder decodeObjectForKey:DateFormatterEditingFormatKey];
emptyIsValid = [aCoder decodeObjectForKey:DateFormatterEmptyIsValidKey];
}
return self;
}
- (void)encodeWithCoder:(CPCoder)aCoder
{
[super encodeWithCoder:aCoder];
[aCoder encodeObject:displayFormat forKey:DateFormatterDisplayFormatKey];
[aCoder encodeObject:editingFormat forKey:DateFormatterEditingFormatKey];
[aCoder encodeObject:emptyIsValid forKey:DateFormatterEmptyIsValidKey];
}
@end
+10
View File
@@ -0,0 +1,10 @@
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>Main cib file base name</key>
<string>MainMenu.cib</string>
<key>CPBundleName</key>
<string>CPFormatterTest</string>
</dict>
</plist>
+94
View File
@@ -0,0 +1,94 @@
/*
* Jakefile
* CPFormatterTest
*
* Created by aparajita on June 30, 2011.
* Copyright 2011, Victory-Heart Productions All rights reserved.
*/
var ENV = require("system").env,
FILE = require("file"),
JAKE = require("jake"),
task = JAKE.task,
FileList = JAKE.FileList,
app = require("cappuccino/jake").app,
configuration = ENV["CONFIG"] || ENV["CONFIGURATION"] || ENV["c"] || "Debug",
OS = require("os");
app ("CPFormatterTest", function(task)
{
task.setBuildIntermediatesPath(FILE.join("Build", "CPFormatterTest.build", configuration));
task.setBuildPath(FILE.join("Build", configuration));
task.setProductName("CPFormatterTest");
task.setIdentifier("com.aparajita.CPFormatterTest");
task.setVersion("1.0");
task.setAuthor("Victory-Heart Productions");
task.setEmail("feedback @nospam@ yourcompany.com");
task.setSummary("CPFormatterTest");
task.setSources((new FileList("**/*.j")).exclude(FILE.join("Build", "**")));
task.setResources(new FileList("Resources/**"));
task.setIndexFilePath("index.html");
task.setInfoPlistPath("Info.plist");
task.setNib2CibFlags("-R Resources/");
if (configuration === "Debug")
task.setCompilerFlags("-DDEBUG -g");
else
task.setCompilerFlags("-O");
});
task ("default", ["CPFormatterTest"], function()
{
printResults(configuration);
});
task ("build", ["default"]);
task ("debug", function()
{
ENV["CONFIGURATION"] = "Debug";
JAKE.subjake(["."], "build", ENV);
});
task ("release", function()
{
ENV["CONFIGURATION"] = "Release";
JAKE.subjake(["."], "build", ENV);
});
task ("run", ["debug"], function()
{
OS.system(["open", FILE.join("Build", "Debug", "CPFormatterTest", "index.html")]);
});
task ("run-release", ["release"], function()
{
OS.system(["open", FILE.join("Build", "Release", "CPFormatterTest", "index.html")]);
});
task ("deploy", ["release"], function()
{
FILE.mkdirs(FILE.join("Build", "Deployment", "CPFormatterTest"));
OS.system(["press", "-f", FILE.join("Build", "Release", "CPFormatterTest"), FILE.join("Build", "Deployment", "CPFormatterTest")]);
printResults("Deployment")
});
task ("desktop", ["release"], function()
{
FILE.mkdirs(FILE.join("Build", "Desktop", "CPFormatterTest"));
require("cappuccino/nativehost").buildNativeHost(FILE.join("Build", "Release", "CPFormatterTest"), FILE.join("Build", "Desktop", "CPFormatterTest", "CPFormatterTest.app"));
printResults("Desktop")
});
task ("run-desktop", ["desktop"], function()
{
OS.system([FILE.join("Build", "Desktop", "CPFormatterTest", "CPFormatterTest.app", "Contents", "MacOS", "NativeHost"), "-i"]);
});
function printResults(configuration)
{
print("----------------------------");
print(configuration+" app built at path: "+FILE.join("Build", configuration, "CPFormatterTest"));
print("----------------------------");
}
File diff suppressed because one or more lines are too long
File diff suppressed because it is too large Load Diff
@@ -0,0 +1,471 @@
/*
* http://code.google.com/p/flexible-js-formatting/
*
* Copyright (C) 2004 Baron Schwartz <baron at sequent dot org>
*
* 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.
*/
Date.parseFunctions = {count:0};
Date.parseRegexes = [];
Date.formatFunctions = {count:0};
Date.prototype.dateFormat = function(format, ignore_offset) {
if (Date.formatFunctions[format] == null) {
Date.createNewFormat(format);
}
var func = Date.formatFunctions[format];
if (ignore_offset || ! this.offset) {
return this[func]();
} else {
return (new Date(this.valueOf() - this.offset))[func]();
}
};
Date.createNewFormat = function(format) {
var funcName = "format" + Date.formatFunctions.count++;
Date.formatFunctions[format] = funcName;
var code = "Date.prototype." + funcName + " = function(){return ";
var special = false;
var ch = '';
for (var i = 0; i < format.length; ++i) {
ch = format.charAt(i);
// escape character start
if (!special && ch == "\\") {
special = true;
}
// escaped string
else if (!special && ch == '"') {
var end = format.indexOf('"', i+1);
if (end==-1)
{
end = format.length;
}
code += "'" + String.escape(format.substring(i+1, end)) + "' + ";
i = end;
}
// escaped character
else if (special) {
special = false;
code += "'" + String.escape(ch) + "' + ";
}
else {
code += Date.getFormatCode(ch);
}
}
eval(code.substring(0, code.length - 3) + ";}");
};
Date.getFormatCode = function(character) {
switch (character) {
case "d":
return "String.leftPad(this.getDate(), 2, '0') + ";
case "D":
return "Date.dayNames[this.getDay()].substring(0, 3) + ";
case "j":
return "this.getDate() + ";
case "l":
return "Date.dayNames[this.getDay()] + ";
case "S":
return "this.getSuffix() + ";
case "w":
return "this.getDay() + ";
case "z":
return "this.getDayOfYear() + ";
case "W":
return "this.getWeekOfYear() + ";
case "F":
return "Date.monthNames[this.getMonth()] + ";
case "m":
return "String.leftPad(this.getMonth() + 1, 2, '0') + ";
case "M":
return "Date.monthNames[this.getMonth()].substring(0, 3) + ";
case "n":
return "(this.getMonth() + 1) + ";
case "t":
return "this.getDaysInMonth() + ";
case "L":
return "(this.isLeapYear() ? 1 : 0) + ";
case "Y":
return "this.getFullYear() + ";
case "y":
return "('' + this.getFullYear()).substring(2, 4) + ";
case "a":
return "(this.getHours() < 12 ? 'am' : 'pm') + ";
case "A":
return "(this.getHours() < 12 ? 'AM' : 'PM') + ";
case "g":
return "((this.getHours() %12) ? this.getHours() % 12 : 12) + ";
case "G":
return "this.getHours() + ";
case "h":
return "String.leftPad((this.getHours() %12) ? this.getHours() % 12 : 12, 2, '0') + ";
case "H":
return "String.leftPad(this.getHours(), 2, '0') + ";
case "i":
return "String.leftPad(this.getMinutes(), 2, '0') + ";
case "s":
return "String.leftPad(this.getSeconds(), 2, '0') + ";
case "X":
return "String.leftPad(this.getMilliseconds(), 3, '0') + ";
case "O":
return "this.getGMTOffset() + ";
case "T":
return "this.getTimezone() + ";
case "Z":
return "(this.getTimezoneOffset() * -60) + ";
case "q": // quarter num, Q for name?
return "this.getQuarter() + ";
default:
return "'" + String.escape(character) + "' + ";
}
};
Date.parseDate = function(input, format) {
if (Date.parseFunctions[format] == null) {
Date.createParser(format);
}
var func = Date.parseFunctions[format];
return Date[func](input);
};
Date.createParser = function(format) {
var funcName = "parse" + Date.parseFunctions.count++;
var regexNum = Date.parseRegexes.length;
var currentGroup = 1;
Date.parseFunctions[format] = funcName;
var code = "Date." + funcName + " = function(input){\n"
+ "var y = -1, m = -1, d = -1, h = -1, i = -1, s = -1, ms = -1, z = 0;\n"
+ "var d = new Date();\n"
+ "y = d.getFullYear();\n"
+ "m = d.getMonth();\n"
+ "d = d.getDate();\n"
+ "var results = input.match(Date.parseRegexes[" + regexNum + "]);\n"
+ "if (results && results.length > 0) {" ;
var regex = "";
var special = false;
var ch = '';
for (var i = 0; i < format.length; ++i) {
ch = format.charAt(i);
if (!special && ch == "\\") {
special = true;
}
else if (special) {
special = false;
regex += String.escape(ch);
}
else {
obj = Date.formatCodeToRegex(ch, currentGroup);
currentGroup += obj.g;
regex += obj.s;
if (obj.g && obj.c) {
code += obj.c;
}
}
}
code += "if (y > 0 && m >= 0 && d > 0 && h >= 0 && i >= 0 && s >= 0 && ms >= 0)\n"
+ "{return new Date(y, m, d, h, i, s, ms).applyOffset(z);}\n"
+ "if (y > 0 && m >= 0 && d > 0 && h >= 0 && i >= 0 && s >= 0)\n"
+ "{return new Date(y, m, d, h, i, s).applyOffset(z);}\n"
+ "else if (y > 0 && m >= 0 && d > 0 && h >= 0 && i >= 0)\n"
+ "{return new Date(y, m, d, h, i).applyOffset(z);}\n"
+ "else if (y > 0 && m >= 0 && d > 0 && h >= 0)\n"
+ "{return new Date(y, m, d, h).applyOffset(z);}\n"
+ "else if (y > 0 && m >= 0 && d > 0)\n"
+ "{return new Date(y, m, d).applyOffset(z);}\n"
+ "else if (y > 0 && m >= 0)\n"
+ "{return new Date(y, m).applyOffset(z);}\n"
+ "else if (y > 0)\n"
+ "{return new Date(y).applyOffset(z);}\n"
+ "}return null;}";
Date.parseRegexes[regexNum] = new RegExp("^" + regex + "$");
eval(code);
};
Date.formatCodeToRegex = function(character, currentGroup) {
switch (character) {
case "D":
return {g:0,
c:null,
s:"(?:Sun|Mon|Tue|Wed|Thu|Fri|Sat)"};
case "j":
case "d":
return {g:1,
c:"d = parseInt(results[" + currentGroup + "], 10);\n",
s:"(\\d{1,2})"};
case "l":
return {g:0,
c:null,
s:"(?:" + Date.dayNames.join("|") + ")"};
case "S":
return {g:0,
c:null,
s:"(?:st|nd|rd|th)"};
case "w":
return {g:0,
c:null,
s:"\\d"};
case "z":
return {g:0,
c:null,
s:"(?:\\d{1,3})"};
case "W":
return {g:0,
c:null,
s:"(?:\\d{2})"};
case "F":
return {g:1,
c:"m = parseInt(Date.monthNumbers[results[" + currentGroup + "].substring(0, 3)], 10);\n",
s:"(" + Date.monthNames.join("|") + ")"};
case "M":
return {g:1,
c:"m = parseInt(Date.monthNumbers[results[" + currentGroup + "]], 10);\n",
s:"(Jan|Feb|Mar|Apr|May|Jun|Jul|Aug|Sep|Oct|Nov|Dec)"};
case "n":
case "m":
return {g:1,
c:"m = parseInt(results[" + currentGroup + "], 10) - 1;\n",
s:"(\\d{1,2})"};
case "t":
return {g:0,
c:null,
s:"\\d{1,2}"};
case "L":
return {g:0,
c:null,
s:"(?:1|0)"};
case "Y":
return {g:1,
c:"y = parseInt(results[" + currentGroup + "], 10);\n",
s:"(\\d{4})"};
case "y":
return {g:1,
c:"var ty = parseInt(results[" + currentGroup + "], 10);\n"
+ "y = ty > Date.y2kYear ? 1900 + ty : 2000 + ty;\n",
s:"(\\d{1,2})"};
case "a":
return {g:1,
c:"if (results[" + currentGroup + "] == 'am') {\n"
+ "if (h == 12) { h = 0; }\n"
+ "} else { if (h < 12) { h += 12; }}",
s:"(am|pm)"};
case "A":
return {g:1,
c:"if (results[" + currentGroup + "] == 'AM') {\n"
+ "if (h == 12) { h = 0; }\n"
+ "} else { if (h < 12) { h += 12; }}",
s:"(AM|PM)"};
case "g":
case "G":
case "h":
case "H":
return {g:1,
c:"h = parseInt(results[" + currentGroup + "], 10);\n",
s:"(\\d{1,2})"};
case "i":
return {g:1,
c:"i = parseInt(results[" + currentGroup + "], 10);\n",
s:"(\\d{2})"};
case "s":
return {g:1,
c:"s = parseInt(results[" + currentGroup + "], 10);\n",
s:"(\\d{2})"};
case "X":
return {g:1,
c:"ms = parseInt(results[" + currentGroup + "], 10);\n",
s:"(\\d{3})"};
case "O":
case "P":
return {g:1,
c:"z = Date.parseOffset(results[" + currentGroup + "], 10);\n",
s:"(Z|[+-]\\d{2}:?\\d{2})"}; // "Z", "+05:00", "+0500" all acceptable.
case "T":
return {g:0,
c:null,
s:"[A-Z]{3}"};
case "Z":
return {g:1,
c:"s = parseInt(results[" + currentGroup + "], 10);\n",
s:"([+-]\\d{1,5})"};
default:
return {g:0,
c:null,
s:String.escape(character)};
}
};
Date.parseOffset = function(str) {
if (str == "Z") { return 0 ; } // UTC, no offset.
var seconds ;
seconds = parseInt(str[0] + str[1] + str[2]) * 3600 ; // e.g., "+05" or "-08"
if (str[3] == ":") { // "+HH:MM" is preferred iso8601 format ("O")
seconds += parseInt(str[4] + str[5]) * 60;
} else { // "+HHMM" is frequently used, though. ("P")
seconds += parseInt(str[3] + str[4]) * 60;
}
return seconds ;
};
// convert the parsed date into UTC, but store the offset so we can optionally use it in dateFormat()
Date.prototype.applyOffset = function(offset_seconds) {
this.offset = offset_seconds * 1000 ;
this.setTime(this.valueOf() + this.offset);
return this ;
};
Date.prototype.getTimezone = function() {
return this.toString().replace(
/^.*? ([A-Z]{3}) [0-9]{4}.*$/, "$1").replace(
/^.*?\(([A-Z])[a-z]+ ([A-Z])[a-z]+ ([A-Z])[a-z]+\)$/, "$1$2$3").replace(
/^.*?[0-9]{4} \(([A-Z]{3})\)/, "$1");
};
Date.prototype.getGMTOffset = function() {
return (this.getTimezoneOffset() > 0 ? "-" : "+")
+ String.leftPad(Math.floor(this.getTimezoneOffset() / 60), 2, "0")
+ String.leftPad(this.getTimezoneOffset() % 60, 2, "0");
};
Date.prototype.getDayOfYear = function() {
var num = 0;
Date.daysInMonth[1] = this.isLeapYear() ? 29 : 28;
for (var i = 0; i < this.getMonth(); ++i) {
num += Date.daysInMonth[i];
}
return num + this.getDate() - 1;
};
Date.prototype.getWeekOfYear = function() {
// Skip to Thursday of this week
var now = this.getDayOfYear() + (4 - this.getDay());
// Find the first Thursday of the year
var jan1 = new Date(this.getFullYear(), 0, 1);
var then = (7 - jan1.getDay() + 4);
document.write(then);
return String.leftPad(((now - then) / 7) + 1, 2, "0");
};
Date.prototype.isLeapYear = function() {
var year = this.getFullYear();
return ((year & 3) == 0 && (year % 100 || (year % 400 == 0 && year)));
};
Date.prototype.getFirstDayOfMonth = function() {
var day = (this.getDay() - (this.getDate() - 1)) % 7;
return (day < 0) ? (day + 7) : day;
};
Date.prototype.getLastDayOfMonth = function() {
var day = (this.getDay() + (Date.daysInMonth[this.getMonth()] - this.getDate())) % 7;
return (day < 0) ? (day + 7) : day;
};
Date.prototype.getDaysInMonth = function() {
Date.daysInMonth[1] = this.isLeapYear() ? 29 : 28;
return Date.daysInMonth[this.getMonth()];
};
Date.prototype.getQuarter = function() {
return Date.quarterFromMonthNum[this.getMonth()];
};
Date.prototype.getSuffix = function() {
switch (this.getDate()) {
case 1:
case 21:
case 31:
return "st";
case 2:
case 22:
return "nd";
case 3:
case 23:
return "rd";
default:
return "th";
}
};
String.escape = function(string) {
return string.replace(/('|\\)/g, "\\$1");
};
String.leftPad = function (val, size, ch) {
var result = new String(val);
if (ch == null) {
ch = " ";
}
while (result.length < size) {
result = ch + result;
}
return result;
};
Date.quarterFromMonthNum = [1,1,1,2,2,2,3,3,3,4,4,4];
Date.daysInMonth = [31,28,31,30,31,30,31,31,30,31,30,31];
Date.monthNames =
["January",
"February",
"March",
"April",
"May",
"June",
"July",
"August",
"September",
"October",
"November",
"December"];
Date.dayNames =
["Sunday",
"Monday",
"Tuesday",
"Wednesday",
"Thursday",
"Friday",
"Saturday"];
Date.y2kYear = 50;
Date.monthNumbers = {
Jan:0,
Feb:1,
Mar:2,
Apr:3,
May:4,
Jun:5,
Jul:6,
Aug:7,
Sep:8,
Oct:9,
Nov:10,
Dec:11};
Date.patterns = {
ISO8601LongPattern: "Y\\-m\\-d\\TH\\:i\\:sO",
ISO8601ShortPattern: "Y\\-m\\-d",
ShortDatePattern: "n/j/Y",
LongDatePattern: "l, F d, Y",
FullDateTimePattern: "l, F d, Y g:i:s A",
MonthDayPattern: "F d",
ShortTimePattern: "g:i A",
LongTimePattern: "g:i:s A",
SortableDateTimePattern: "Y-m-d\\TH:i:s",
UniversalSortableDateTimePattern: "Y-m-d H:i:sO",
YearMonthPattern: "F, Y"};
Binary file not shown.

After

Width:  |  Height:  |  Size: 1.8 KiB

@@ -0,0 +1,105 @@
<!DOCTYPE html
PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN"
"http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en" lang="en">
<!--
index-debug.html
CPFormatterTest
Created by aparajita on June 30, 2011.
Copyright 2011, Victory-Heart Productions All rights reserved.
-->
<head>
<meta http-equiv="X-UA-Compatible" content="IE=EmulateIE7, chrome=1" />
<meta name="viewport" content="initial-scale=1.0, user-scalable=no" />
<meta name="apple-mobile-web-app-capable" content="yes" />
<meta name="apple-mobile-web-app-status-bar-style" content="black" />
<link rel="apple-touch-icon" href="Resources/icon.png" />
<link rel="apple-touch-startup-image" href="Resources/default.png" />
<title>CPFormatterTest</title>
<script src="Resources/date-functions.js" type="text/javascript" charset="utf-8"></script>
<script type="text/javascript">
OBJJ_MAIN_FILE = "main.j";
OBJJ_INCLUDE_PATHS = ["Frameworks/Debug", "Frameworks"];
</script>
<script type="text/javascript" src="Frameworks/Debug/Objective-J/Objective-J.js" charset="UTF-8"></script>
<script type="text/javascript">
objj_msgSend_reset();
// DEBUG OPTIONS:
// Uncomment to enable printing of backtraces on exceptions:
//objj_msgSend_decorate(objj_backtrace_decorator);
// Uncomment to supress exceptions that take place inside a message
//objj_msgSend_decorate(objj_supress_exceptions_decorator)
// Uncomment to enable runtime type checking:
//objj_msgSend_decorate(objj_typecheck_decorator);
// Uncomment (along with both above) to print backtraces on type check errors:
//objj_typecheck_prints_backtrace = true;
// Uncomment to disable the default logger (CPLogConsole if window.console exists, CPLogPopup otherwise):
//CPLogUnregister(CPLogDefault);
// Uncomment to enable a specific logger:
//CPLogRegister(CPLogConsole);
//CPLogRegister(CPLogPopup);
</script>
<style type="text/css">
body{margin:0; padding:0;}
#container {position: absolute; top:50%; left:50%;}
#content {width:800px; text-align:center; margin-left: -400px; height:50px; margin-top:-25px; line-height: 50px;}
#content {font-family: "Helvetica", "Arial", sans-serif; font-size: 18px; color: black; text-shadow: 0px 1px 0px white; }
#loadgraphic {margin-right: 0.2em; margin-bottom:-2px;}
</style>
<!--[if lt IE 7]>
<STYLE type="text/css">
#container { position: relative; top: 50%; }
#content { position: relative;}
</STYLE>
<![endif]-->
</head>
<body style="">
<div id="cappuccino-body">
<div id="loadingcontainer" style="background-color: #eeeeee; overflow:hidden; width:100%; height:100%; position: absolute; top: 0; left: 0;">
<script type="text/javascript">
document.write("<div id='container'><p id='content'>" +
"<img id='loadgraphic' width='16' height='16' src='Resources/spinner.gif' /> " +
"Loading CPFormatterTest...</p></div>");
</script>
<noscript>
<div id="container">
<div style="width: 440px; padding: 10px 25px 20px 25px; font-family: sans-serif; background-color: #ffffff; position: relative; left: -245px; top: -120px; text-align: center; -moz-border-radius: 20px; -webkit-border-radius: 20px; color: #555555">
<p style="line-height: 1.4em;">JavaScript is required for this site to work correctly but is either disabled or not supported by your browser.</p>
<p style="font-size:120%; padding:10px;"><a href="http://cappuccino.org/noscript">Show me how to enable JavaScript</a></p>
<p style="font-size:80%;">You may want to upgrade to a newer browser while you're at it:</p>
<ul style="margin:0;padding:0; text-align: center; font-size:80%;" >
<li style="display: inline;"><a href="http://www.apple.com/safari/download/">Safari</a></li>
<li style="display: inline;"><a href="http://www.mozilla.com/en-US/firefox/">Firefox</a></li>
<li style="display: inline;"><a href="http://www.google.com/chrome/">Chrome</a></li>
<li style="display: inline;"><a href="http://www.opera.com/download/">Opera</a></li>
<li style="display: inline;"><a href="http://www.microsoft.com/windows/downloads/ie/getitnow.mspx">Internet Explorer</a></li>
</ul>
</div>
</div>
</noscript>
</div>
</div>
</body>
</html>
+79
View File
@@ -0,0 +1,79 @@
<!DOCTYPE html
PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN"
"http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en" lang="en">
<!--
index.html
CPFormatterTest
Created by aparajita on June 30, 2011.
Copyright 2011, Victory-Heart Productions All rights reserved.
-->
<head>
<meta http-equiv="X-UA-Compatible" content="IE=EmulateIE7, chrome=1" />
<meta name="viewport" content="initial-scale=1.0, user-scalable=no" />
<meta name="apple-mobile-web-app-capable" content="yes" />
<meta name="apple-mobile-web-app-status-bar-style" content="black" />
<link rel="apple-touch-icon" href="Resources/icon.png" />
<link rel="apple-touch-startup-image" href="Resources/default.png" />
<title>CPFormatterTest</title>
<script src="Resources/date-functions.js" type="text/javascript" charset="utf-8"></script>
<script type="text/javascript">
OBJJ_MAIN_FILE = "main.j";
</script>
<script type="text/javascript" src="Frameworks/Objective-J/Objective-J.js" charset="UTF-8"></script>
<style type="text/css">
body{margin:0; padding:0;}
#container {position: absolute; top:50%; left:50%;}
#content {width:800px; text-align:center; margin-left: -400px; height:50px; margin-top:-25px; line-height: 50px;}
#content {font-family: "Helvetica", "Arial", sans-serif; font-size: 18px; color: black; text-shadow: 0px 1px 0px white; }
#loadgraphic {margin-right: 0.2em; margin-bottom:-2px;}
</style>
<!--[if lt IE 7]>
<STYLE type="text/css">
#container { position: relative; top: 50%; }
#content { position: relative;}
</STYLE>
<![endif]-->
</head>
<body style="">
<div id="cappuccino-body">
<div id="loadingcontainer" style="background-color: #eeeeee; overflow:hidden; width:100%; height:100%; position: absolute; top: 0; left: 0;">
<script type="text/javascript">
document.write("<div id='container'><p id='content'>" +
"<img id='loadgraphic' width='16' height='16' src='Resources/spinner.gif' /> " +
"Loading CPFormatterTest...</p></div>");
</script>
<noscript>
<div id="container">
<div style="width: 440px; padding: 10px 25px 20px 25px; font-family: sans-serif; background-color: #ffffff; position: relative; left: -245px; top: -120px; text-align: center; -moz-border-radius: 20px; -webkit-border-radius: 20px; color: #555555">
<p style="line-height: 1.4em;">JavaScript is required for this site to work correctly but is either disabled or not supported by your browser.</p>
<p style="font-size:120%; padding:10px;"><a href="http://cappuccino.org/noscript">Show me how to enable JavaScript</a></p>
<p style="font-size:80%;">You may want to upgrade to a newer browser while you're at it:</p>
<ul style="margin:0;padding:0; text-align: center; font-size:80%;" >
<li style="display: inline;"><a href="http://www.apple.com/safari/download/">Safari</a></li>
<li style="display: inline;"><a href="http://www.mozilla.com/en-US/firefox/">Firefox</a></li>
<li style="display: inline;"><a href="http://www.google.com/chrome/">Chrome</a></li>
<li style="display: inline;"><a href="http://www.opera.com/download/">Opera</a></li>
<li style="display: inline;"><a href="http://www.microsoft.com/windows/downloads/ie/getitnow.mspx">Internet Explorer</a></li>
</ul>
</div>
</div>
</noscript>
</div>
</div>
</body>
</html>
+23
View File
@@ -0,0 +1,23 @@
/*
* AppController.j
* CPFormatterTest
*
* Created by aparajita on June 30, 2011.
* Copyright 2011, Victory-Heart Productions All rights reserved.
*/
@import <Foundation/Foundation.j>
@import <AppKit/AppKit.j>
@import "AppController.j"
function formatter(aString, aLevel, aTitle)
{
return aString;
}
function main(args, namedArgs)
{
CPLogRegister(CPLogConsole, null, formatter);
CPApplicationMain(args, namedArgs);
}
+326
View File
@@ -0,0 +1,326 @@
@import <Foundation/Foundation.j>
@implementation RootClass
{
}
+ (id)alloc
{
return class_createInstance(self);
}
@end
@implementation RootClassWithDoesNotRecognizeSelector
{
}
+ (id)alloc
{
return class_createInstance(self);
}
- (void)doesNotRecognizeSelector:(SEL)aSelector
{
throw "ERROR";
}
@end
@implementation Subclass : CPObject
{
}
@end
@implementation RootClassWithForwardingTarget
{
}
+ (void)initialize
{
}
+ (id)alloc
{
return class_createInstance(self);
}
- (id)forwardingTargetForSelector:(SEL)aSelector
{
if (aSelector !== @selector(doesNotExist))
return nil;
if (class_isMetaClass(isa))
return GlobalMethodDispatchTest;
return GlobalMethodDispatchTest;
}
@end
@implementation SubclassWithForwardingTarget : CPObject
{
}
+ (id)forwardingTargetForSelector:(SEL)aSelector
{
if (aSelector !== @selector(doesNotExist))
return [super forwardingTargetForSelector:aSelector];
return GlobalMethodDispatchTest;
}
- (id)forwardingTargetForSelector:(SEL)aSelector
{
if (aSelector !== @selector(doesNotExist))
return [super forwardingTargetForSelector:aSelector];
return GlobalMethodDispatchTest;
}
@end
@implementation RootClassWithForwardInvocation
{
}
+ (void)initialize
{
}
+ (id)alloc
{
return class_createInstance(self);
}
- (CPMethodSignature)methodSignatureForSelector:(SEL)aSelector
{
if (aSelector === @selector(doesNotExist))
return 1;
return nil;
}
- (void)forwardInvocation:(CPInvocation)anInvocation
{
[anInvocation setTarget:GlobalMethodDispatchTest];
[anInvocation invoke];
}
@end
@implementation SubclassWithForwardInvocation : CPObject
{
}
+ (CPMethodSignature)methodSignatureForSelector:(SEL)aSelector
{
if (aSelector === @selector(doesNotExist))
return 1;
return nil;
}
+ (void)forwardInvocation:(CPInvocation)anInvocation
{
[anInvocation setTarget:GlobalMethodDispatchTest];
[anInvocation invoke];
}
- (CPMethodSignature)methodSignatureForSelector:(SEL)aSelector
{
if (aSelector === @selector(doesNotExist))
return 1;
return nil;
}
- (void)forwardInvocation:(CPInvocation)anInvocation
{
[anInvocation setTarget:GlobalMethodDispatchTest];
[anInvocation invoke];
}
@end
var GlobalMethodDispatchTest;
@implementation MethodDispatchTest : OJTestCase
{
}
- (id)init
{
self = [super init];
if (self)
GlobalMethodDispatchTest = self;
return self;
}
- (BOOL)doesNotExist
{
return YES;
}
- (void)test_RootClass_class_doesNotRecognizeSelector_
{
try
{
[RootClass doesNotExist];
}
catch (anException)
{
[self assert:anException equals:"RootClass does not implement doesNotRecognizeSelector:. Did you forget a superclass for RootClass?"];
}
}
- (void)test_RootClass_instance_doesNotRecognizeSelector_
{
var object = [RootClass alloc];
try
{
[object doesNotExist];
}
catch (anException)
{
[self assert:anException equals:"RootClass does not implement doesNotRecognizeSelector:. Did you forget a superclass for RootClass?"];
}
}
- (void)test_RootClassWithDoesNotRecognizeSelector_class_doesNotRecognizeSelector_
{
try
{
[RootClassWithDoesNotRecognizeSelector doesNotExist];
}
catch (anException)
{
[self assert:anException equals:"ERROR"];
}
}
- (void)test_RootClassWithDoesNotRecognizeSelector_instance_doesNotRecognizeSelector_
{
var object = [RootClassWithDoesNotRecognizeSelector alloc];
try
{
[object doesNotExist];
}
catch (anException)
{
[self assert:anException equals:"ERROR"];
}
}
- (void)test_CPObject_class_doesNotRecognizeSelector_
{
try
{
[CPObject doesNotExist];
}
catch (anException)
{
[self assert:[anException name] equals:CPInvalidArgumentException];
[self assert:[anException reason] equals:@"+ [CPObject doesNotExist] unrecognized selector sent to class CPObject"];
}
}
- (void)test_CPObject_instance_doesNotRecognizeSelector_
{
var object = [CPObject alloc];
try
{
[object doesNotExist];
}
catch (anException)
{
[self assert:[anException name] equals:CPInvalidArgumentException];
[self assert:[anException reason] equals:@"- [CPObject doesNotExist] unrecognized selector sent to instance 0x" + [CPString stringWithHash:[object UID]]];
}
}
- (void)test_Subclass_class_doesNotRecognizeSelector_
{
try
{
[Subclass doesNotExist];
}
catch (anException)
{
[self assert:[anException name] equals:CPInvalidArgumentException];
[self assert:[anException reason] equals:@"+ [Subclass doesNotExist] unrecognized selector sent to class Subclass"];
}
}
- (void)test_Subclass_instance_doesNotRecognizeSelector_
{
var object = [Subclass alloc];
try
{
[object doesNotExist];
}
catch (anException)
{
[self assert:[anException name] equals:CPInvalidArgumentException];
[self assert:[anException reason] equals:@"- [Subclass doesNotExist] unrecognized selector sent to instance 0x" + [CPString stringWithHash:[object UID]]];
}
}
- (void)test_RootClassWithForwardingTarget_class_forwardingTargetForSelector_
{
[self assert:YES equals:[RootClassWithForwardingTarget doesNotExist]];
}
- (void)test_RootClassWithForwardingTarget_instance_forwardingTargetForSelector_
{
var object = [RootClassWithForwardingTarget alloc];
[self assert:YES equals:[object doesNotExist]];
}
- (void)test_SubclassWithForwardingTarget_class_forwardingTargetForSelector_
{
[self assert:YES equals:[SubclassWithForwardingTarget doesNotExist]];
}
- (void)test_SubclassWithForwardingTarget_instance_forwardingTargetForSelector_
{
var object = [[SubclassWithForwardingTarget alloc] init];
[self assert:YES equals:[object doesNotExist]];
}
- (void)test_RootClassWithForwardInvocation_class_forwardInvocation_
{
[self assert:YES equals:[RootClassWithForwardInvocation doesNotExist]];
}
- (void)test_RootClassWithForwardInvocation_instance_forwardInvocation_
{
var object = [RootClassWithForwardInvocation alloc];
[self assert:YES equals:[object doesNotExist]];
}
- (void)test_SubclassWithForwardInvocation_class_forwardInvocation_
{
[self assert:YES equals:[SubclassWithForwardInvocation doesNotExist]];
}
- (void)test_SubclassWithForwardInvocation_instance_forwardInvocation_
{
var object = [[SubclassWithForwardInvocation alloc] init];
[self assert:YES equals:[object doesNotExist]];
}
@end
+15
View File
@@ -189,6 +189,8 @@ function gen(/*va_args*/)
{
fail("The directory " + FILE.absolute(destinationProject) + " already exists.");
}
executePostInstallScript(destinationProject);
}
function createFrameworksInFile(/*Array*/ frameworks, /*String*/ aFile, /*Boolean*/ symlink, /*Boolean*/ build, /*Boolean*/ force)
@@ -365,6 +367,19 @@ function listFrameworks()
});
}
function executePostInstallScript(/*String*/ destinationProject)
{
var path = FILE.join(destinationProject, "postinstall");
if (FILE.exists(path))
{
stream.print(colorize("Executing postinstall script...", "cyan"));
OS.system(["/bin/sh", path, destinationProject]); // Use sh in case it isn't marked executable
FILE.remove(path);
}
}
function colorize(message, color)
{
return "\0" + color + "(" + message + "\0)";
@@ -0,0 +1,24 @@
/*
* __filename__
* __project.name__
*
* Created by __user.name__ on __project.date__.
*
* Copyright __project.year__, __organization.name__. All rights reserved.
*
* 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 "__project.nameasidentifier__Class.j"
@@ -0,0 +1,46 @@
/*
* __filename__
* __project.name__
*
* Created by __user.name__ on __project.date__.
*
* Copyright __project.year__, __organization.name__. All rights reserved.
*
* 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>
/*!
This class is defined to make it easier to find the bundle,
for example to get an image from the framework like this:
@code
var path = [[CPBundle bundleForClass:__project.nameasidentifier__] pathForResource:@"email-action.png"];
@endcode
You can also use [__project.nameasidentifier__ version] to get the current version.
*/
@implementation __project.nameasidentifier__ : CPObject
+ (CPString)version
{
var bundle = [CPBundle bundleForClass:[self class]];
return [bundle objectForInfoDictionaryKey:@"CPBundleVersion"];
}
@end
@@ -0,0 +1,10 @@
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>CPBundleInfoDictionaryVersion</key>
<string>6.0</string>
<key>CPBundlePackageType</key>
<string>FMWK</string>
</dict>
</plist>
@@ -0,0 +1,217 @@
/*
* Jakefile
* __project.name__
*
* Created by __user.name__ on __project.date__.
*
* Copyright __project.year__, __organization.name__. All rights reserved.
*
* 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
*/
//===========================================================
// DO NOT REMOVE
//===========================================================
var SYS = require("system"),
ENV = SYS.env,
FILE = require("file"),
OS = require("os");
//===========================================================
// USER CONFIGURABLE VARIABLES
//===========================================================
/*
The directory in which the project will be built. By default
it is built in $CAPP_BUILD if that is defined, otherwise
in a "Build" directory within the project directory.
*/
var buildDir = ENV["BUILD_PATH"] || ENV["CAPP_BUILD"] || "Build";
//===========================================================
// AUTOMATICALLY GENERATED
//
// Do not edit! (unless you know what you are doing)
//===========================================================
var stream = require("narwhal/term").stream,
JAKE = require("jake"),
task = JAKE.task,
CLEAN = require("jake/clean").CLEAN,
CLOBBER = require("jake/clean").CLOBBER,
FileList = JAKE.FileList,
filedir = JAKE.filedir,
framework = require("cappuccino/jake").framework,
browserEnvironment = require("objective-j/jake/environment").Browser,
configuration = ENV["CONFIG"] || ENV["CONFIGURATION"] || ENV["c"] || "Debug",
productName = "__project.nameasidentifier__",
buildPath = FILE.canonical(FILE.join(buildDir, productName + ".build")),
packageFrameworksPath = FILE.join(SYS.prefix, "packages", "cappuccino", "Frameworks"),
debugPackagePath = FILE.join(packageFrameworksPath, "Debug", productName);
releasePackagePath = FILE.join(packageFrameworksPath, productName);
var frameworkTask = framework (productName, function(frameworkTask)
{
frameworkTask.setBuildIntermediatesPath(FILE.join(buildPath, configuration));
frameworkTask.setBuildPath(FILE.join(buildDir, configuration));
frameworkTask.setProductName(productName);
frameworkTask.setIdentifier("__project.identifier__");
frameworkTask.setVersion("1.0");
frameworkTask.setAuthor("__organization.name__");
frameworkTask.setEmail("__organization.email__");
frameworkTask.setSummary("__project.name__");
frameworkTask.setSources(new FileList("*.j"));
frameworkTask.setResources(new FileList("Resources/**/*"));
frameworkTask.setFlattensSources(true);
frameworkTask.setInfoPlistPath("Info.plist");
frameworkTask.setLicense(BundleTask.License.LGPL_v2_1);
//frameworkTask.setEnvironments([browserEnvironment]);
if (configuration === "Debug")
frameworkTask.setCompilerFlags("-DDEBUG -g");
else
frameworkTask.setCompilerFlags("-O");
});
task ("debug", function()
{
ENV["CONFIGURATION"] = "Debug";
JAKE.subjake(["."], "build", ENV);
});
task ("release", function()
{
ENV["CONFIGURATION"] = "Release";
JAKE.subjake(["."], "build", ENV);
});
task ("default", ["release"]);
var frameworkCJS = FILE.join(buildDir, configuration, "CommonJS", "cappuccino", "Frameworks", productName);
filedir (frameworkCJS, [productName], function()
{
if (FILE.exists(frameworkCJS))
FILE.rmtree(frameworkCJS);
FILE.copyTree(frameworkTask.buildProductPath(), frameworkCJS);
});
task ("build", [productName, frameworkCJS]);
task ("all", ["debug", "release"]);
task ("install", ["debug", "release"], function()
{
install("copy");
});
task ("install-symlinks", ["debug", "release"], function()
{
install("symlink");
});
task ("help", function()
{
var app = JAKE.application().name();
colorPrint("--------------------------------------------------------------------------", "bold+green");
colorPrint("__project.name__ - Framework", "bold+green");
colorPrint("--------------------------------------------------------------------------", "bold+green");
describeTask(app, "debug", "Builds a debug version at " + FILE.join(buildDir, "Debug", productName));
describeTask(app, "release", "Builds a release version at " + FILE.join(buildDir, "Release", productName));
describeTask(app, "all", "Builds a debug and release version");
describeTask(app, "install", "Builds a debug and release version, then installs in " + packageFrameworksPath);
describeTask(app, "install-symlinks", "Builds a debug and release version, then symlinks the built versions into " + packageFrameworksPath);
describeTask(app, "clean", "Removes the intermediate build files");
describeTask(app, "clobber", "Removes the intermediate build files and the installed frameworks");
colorPrint("--------------------------------------------------------------------------", "bold+green");
});
CLEAN.include(buildPath);
CLOBBER.include(FILE.join(buildDir, "Debug", productName))
.include(FILE.join(buildDir, "Release", productName))
.include(debugPackagePath)
.include(releasePackagePath);
var install = function(action)
{
var packageFrameworksPath = FILE.join(SYS.prefix, "packages", "cappuccino", "Frameworks");
["Release", "Debug"].forEach(function(aConfig)
{
colorPrint((action === "symlink" ? "Symlinking " : "Copying ") + aConfig + "...", "cyan");
if (aConfig === "Debug")
packageFrameworksPath = FILE.join(packageFrameworksPath, aConfig);
if (!FILE.isDirectory(packageFrameworksPath))
sudo(["mkdir", "-p", packageFrameworksPath]);
var buildPath = FILE.absolute(FILE.join(buildDir, aConfig, productName)),
targetPath = FILE.join(packageFrameworksPath, productName);
if (action === "symlink")
directoryOp(["ln", "-sf", buildPath, targetPath]);
else
directoryOp(["cp", "-rf", buildPath, targetPath]);
});
};
var directoryOp = function(cmd)
{
var targetPath = cmd[cmd.length - 1];
if (FILE.isDirectory(targetPath))
sudo(["rm", "-rf", targetPath])
sudo(cmd);
};
var sudo = function(cmd)
{
if (OS.system(cmd))
OS.system(["sudo"].concat(cmd));
};
var describeTask = function(application, task, description)
{
colorPrint("\n" + application + " " + task, "violet");
description.split("\n").forEach(function(line)
{
stream.print(" " + line);
});
}
var colorPrint = function(message, color)
{
var matches = color.match(/(bold(?: |\+))?(.+)/);
if (!matches)
return;
message = "\0" + matches[2] + "(" + message + "\0)";
if (matches[1])
message = "\0bold(" + message + "\0)";
stream.print(message);
}
+6
View File
@@ -0,0 +1,6 @@
#!/bin/sh
mv "$1"/Framework.j "$1/__project.name__.j"
mv "$1"/FrameworkClass.j "$1/__project.name__Class.j"
sed -e 's/__filename__/__project.name__.j/' -i '' "$1/__project.name__.j"
sed -e 's/__filename__/__project.name__Class.j/' -i '' "$1/__project.name__Class.j"
+14
View File
@@ -88,6 +88,20 @@ or
This is a handy shortcut for
.Sy --symlink --build.
.El
.Ss Postinstall scripts
After copying the chosen template, replacing placeholders, and installing the frameworks,
if
.Nm
finds a file named "postinstall", it will be executed using
.Sy sh.
The script receives a single parameter which is the directory of the generated project.
Scripts should reference files in the project relative to that directory.
.Pp
After executing the
.Sy postinstall
script,
.Nm
deletes it from the generated project.
.\"-----------------------------------------------------------------------------------------
.Sh CONFIGURING
.\"-----------------------------------------------------------------------------------------
+1
View File
@@ -46,6 +46,7 @@
@import "NSMenu.j"
@import "NSMenuItem.j"
@import "NSNibConnector.j"
@import "NSNumberFormatter.j"
@import "NSObjectController.j"
@import "NSOutlineView.j"
@import "NSPopUpButton.j"
+2
View File
@@ -99,6 +99,8 @@ var NSButtonIsBorderedMask = 0x00800000,
self.isa = [CPRadio class];
self._radioGroup = [CPRadioGroup new];
}
_themeClass = [[self class] defaultThemeClass];
}
NIB_CONNECTION_EQUIVALENCY_TABLE[[cell UID]] = self;
+3
View File
@@ -48,6 +48,7 @@
id _objectValue @accessors(readonly, getter=objectValue);
CPFont _font @accessors(readonly, getter=font);
int _lineBreakMode @accessors(readonly, getter=lineBreakMode);
CPFormatter _formatter @accessors(readonly, getter=formatter);
}
- (id)initWithCoder:(CPCoder)aCoder
@@ -78,6 +79,8 @@
_objectValue = [aCoder decodeObjectForKey:@"NSContents"];
_font = [aCoder decodeObjectForKey:@"NSSupport"];
_formatter = [aCoder decodeObjectForKey:@"NSFormatter"];
}
return self;
+2
View File
@@ -54,6 +54,8 @@
[self setAction:[aCoder decodeObjectForKey:@"NSAction"]];
[self setLineBreakMode:[cell lineBreakMode]];
[self setFormatter:[cell formatter]];
}
return self;
-6
View File
@@ -3,9 +3,3 @@
}
@end
@implementation NSNumberFormatter : CPObject
{
}
@end
+1
View File
@@ -58,6 +58,7 @@
_numberOfMajorTickMarks = [cell numberOfMajorTickMarks];
[self setEditable:[cell isEditable]];
[self setEnabled:[cell isEnabled]];
[self setContinuous:[cell isContinuous]];
return self;
+54
View File
@@ -0,0 +1,54 @@
/*
* NSNumberFormatter.j
* nib2cib
*
* Created by Alexander Ljungberg.
* Copyright 2011, WireLoad 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 <Foundation/CPNumberFormatter.j>
@implementation CPNumberFormatter (CPCoding)
- (id)NS_initWithCoder:(CPCoder)aCoder
{
self = [super init];
if (self)
{
}
return self;
}
@end
@implementation NSNumberFormatter : CPNumberFormatter
{
}
- (id)initWithCoder:(CPCoder)aCoder
{
return [self NS_initWithCoder:aCoder];
}
- (Class)classForKeyedArchiver
{
return [CPNumberFormatter class];
}
@end
+7 -2
View File
@@ -75,6 +75,8 @@ var FILE = require("file"),
- (void)run
{
var exitValue = 0;
try
{
var options = [self parseOptionsFromArgs:commandLineArgs];
@@ -84,13 +86,16 @@ var FILE = require("file"),
if (options.watch)
[self watchWithOptions:options];
else
[self convertWithOptions:options inputFile:nil];
if (![self convertWithOptions:options inputFile:nil])
exitValue = 2;
}
catch (anException)
{
CPLog.fatal([self exceptionReason:anException]);
OS.exit(1);
exitValue = 1;
}
OS.exit(exitValue);
}
- (BOOL)convertWithOptions:(JSObject)options inputFile:(CPString)inputFile
Regular → Executable
View File