diff --git a/AppKit/CPAlert.j b/AppKit/CPAlert.j index 9f04ec545..4ce109051 100644 --- a/AppKit/CPAlert.j +++ b/AppKit/CPAlert.j @@ -39,26 +39,22 @@ @global @group CPAlertStyle */ -CPWarningAlertStyle = 0; +CPWarningAlertStyle = 0; /* @global @group CPAlertStyle */ -CPInformationalAlertStyle = 1; +CPInformationalAlertStyle = 1; /* @global @group CPAlertStyle */ -CPCriticalAlertStyle = 2; +CPCriticalAlertStyle = 2; -var CPAlertWarningImage, - CPAlertInformationImage, - CPAlertErrorImage; - /*! @ingroup appkit - + CPAlert is an alert panel that can be displayed modally to present the user with a message and one or more options. @@ -74,11 +70,11 @@ var CPAlertWarningImage, @delegate -(void)alertDidEnd:(CPAlert)theAlert returnCode:(int)returnCode; Called when the user dismisses the alert by clicking one of the buttons. @param theAlert the alert panel that the user dismissed - @param returnCode the index of the button that the user clicked (starting from 0, + @param returnCode the index of the button that the user clicked (starting from 0, representing the first button added to the alert which appears on the right, 1 representing the next button to the left and so on) */ -@implementation CPAlert : CPObject +@implementation CPAlert : CPView { CPPanel _alertPanel; @@ -89,27 +85,34 @@ var CPAlertWarningImage, CPAlertStyle _alertStyle; CPString _windowTitle; int _windowStyle; - int _buttonCount; CPArray _buttons; id _delegate; } -+ (void)initialize ++ (CPString)themeClass { - if (self != CPAlert) - return; + return @"alert"; +} - var bundle = [CPBundle bundleForClass:[self class]]; - - CPAlertWarningImage = [[CPImage alloc] initWithContentsOfFile:[bundle pathForResource:@"CPAlert/dialog-warning.png"] - size:CGSizeMake(32.0, 32.0)]; - - CPAlertInformationImage = [[CPImage alloc] initWithContentsOfFile:[bundle pathForResource:@"CPAlert/dialog-information.png"] - size:CGSizeMake(32.0, 32.0)]; - - CPAlertErrorImage = [[CPImage alloc] initWithContentsOfFile:[bundle pathForResource:@"CPAlert/dialog-error.png"] - size:CGSizeMake(32.0, 32.0)]; ++ (id)themeAttributes +{ + return [CPDictionary dictionaryWithObjects:[CGSizeMake(400.0, 110.0), CGInsetMake(15, 15, 15, 50), 6, 10, + CPJustifiedTextAlignment, [CPColor blackColor], [CPFont boldSystemFontOfSize:13.0], [CPNull null], CGSizeMakeZero(), + CPJustifiedTextAlignment, [CPColor blackColor], [CPFont systemFontOfSize:12.0], [CPNull null], CGSizeMakeZero(), + CGPointMake(15, 12), + [CPNull null], + [CPNull null], + [CPNull null] + ] + forKeys:[@"size", @"content-inset", @"informative-offset", @"button-offset", + @"message-text-alignment", @"message-text-color", @"message-text-font", @"message-text-shadow-color", @"message-text-shadow-offset", + @"informative-text-alignment", @"informative-text-color", @"informative-text-font", @"informative-text-shadow-color", @"informative-text-shadow-offset", + @"image-offset", + @"information-image", + @"warning-image", + @"error-image" + ]]; } /*! @@ -119,64 +122,52 @@ var CPAlertWarningImage, { if (self = [super init]) { - _buttonCount = 0; _buttons = [CPArray array]; _alertStyle = CPWarningAlertStyle; + _alertPanel = nil; + _windowStyle = nil; - [self setWindowStyle:nil]; + _messageLabel = [[CPTextField alloc] initWithFrame:CGRectMakeZero()]; + _alertImageView = [[CPImageView alloc] initWithFrame:CGRectMakeZero()]; + _informativeLabel = [[CPTextField alloc] initWithFrame:CGRectMakeZero()]; } - + return self; } /*! - Sets the window appearance. + Sets the window appearance. If CPHUDBackgroundWindowMask is set, the default HUD theme + will be activated. + @param styleMask - Either CPHUDBackgroundWindowMask or nil for standard. */ - (void)setWindowStyle:(int)styleMask { _windowStyle = styleMask; - - _alertPanel = [[CPPanel alloc] initWithContentRect:CGRectMake(0.0, 0.0, 400.0, 110.0) styleMask:styleMask ? styleMask | CPTitledWindowMask : CPTitledWindowMask]; - [_alertPanel setFloatingPanel:YES]; - [_alertPanel center]; - var count = [_buttons count]; - for(var i=0; i < count; i++) - { - var button = _buttons[i]; - [button setTheme:(_windowStyle === CPHUDBackgroundWindowMask) ? [CPTheme themeNamed:"Aristo-HUD"] : [CPTheme defaultTheme]]; + [self setTheme:(_windowStyle === CPHUDBackgroundWindowMask) ? [CPTheme defaultHudTheme] : [CPTheme defaultTheme]]; - [[_alertPanel contentView] addSubview:button]; - } + // We'll need to recreate the panel to get the new window style. + _alertPanel = nil; +} - [self _layoutButtons]; +- (void)_createPanel +{ + var frame = CGRectMakeZero(); + frame.size = [self currentValueForThemeAttribute:@"size"]; + _alertPanel = [[CPPanel alloc] initWithContentRect:frame styleMask:_windowStyle ? _windowStyle | CPTitledWindowMask : CPTitledWindowMask]; - if (!_messageLabel) - { - _messageLabel = [[CPTextField alloc] initWithFrame:CGRectMakeZero()]; - [_messageLabel setFont:[CPFont boldSystemFontOfSize:13.0]]; - [_messageLabel setLineBreakMode:CPLineBreakByWordWrapping]; - [_messageLabel setAlignment:CPJustifiedTextAlignment]; - [_messageLabel setAutoresizingMask:CPViewWidthSizable|CPViewHeightSizable]; + var contentView = [_alertPanel contentView], + count = [_buttons count]; + while (count--) + [contentView addSubview:_buttons[count]]; + [contentView addSubview:_messageLabel]; + [contentView addSubview:_alertImageView]; + [contentView addSubview:_informativeLabel]; - _alertImageView = [[CPImageView alloc] initWithFrame:CGRectMake(15.0, 12.0, 32.0, 32.0)]; - - _informativeLabel = [[CPTextField alloc] initWithFrame:CGRectMakeZero()]; - [_informativeLabel setFont:[CPFont systemFontOfSize:12.0]]; - [_informativeLabel setLineBreakMode:CPLineBreakByWordWrapping]; - [_informativeLabel setAlignment:CPJustifiedTextAlignment]; - [_informativeLabel setAutoresizingMask:CPViewWidthSizable|CPViewHeightSizable]; - } - [_messageLabel setTextColor:(styleMask & CPHUDBackgroundWindowMask) ? [CPColor whiteColor] : [CPColor blackColor]]; - [_informativeLabel setTextColor:(styleMask & CPHUDBackgroundWindowMask) ? [CPColor whiteColor] : [CPColor blackColor]]; - - [[_alertPanel contentView] addSubview:_messageLabel]; - [[_alertPanel contentView] addSubview:_alertImageView]; - [[_alertPanel contentView] addSubview:_informativeLabel]; - - [self _layoutMessage]; + // For reference: does not actually work since this 'view' is not in the hierarchy. + // [self setNeedsLayout]; } /*! @@ -228,6 +219,9 @@ var CPAlertWarningImage, - (void)setAlertStyle:(CPAlertStyle)style { _alertStyle = style; + + // For reference: does not actually work since this 'view' is not in the hierarchy. + // [self setNeedsLayout]; } /*! @@ -245,7 +239,9 @@ var CPAlertWarningImage, - (void)setMessageText:(CPString)messageText { [_messageLabel setStringValue:messageText]; - [self _layoutMessage]; + + // For reference: does not actually work since this 'view' is not in the hierarchy. + // [self setNeedsLayout]; } /*! @@ -288,18 +284,16 @@ var CPAlertWarningImage, - (void)addButtonWithTitle:(CPString)title { var bounds = [[_alertPanel contentView] bounds], - button = [[CPButton alloc] initWithFrame:CGRectMakeZero()]; + button = [[CPButton alloc] initWithFrame:CGRectMakeZero()], + _buttonCount = [_buttons count]; [button setTitle:title]; [button setTarget:self]; [button setTag:_buttonCount]; [button setAction:@selector(_notifyDelegate:)]; - - [button setTheme:(_windowStyle === CPHUDBackgroundWindowMask) ? [CPTheme themeNamed:"Aristo-HUD"] : [CPTheme defaultTheme]]; - [button setAutoresizingMask:CPViewMinXMargin | CPViewMinYMargin]; [[_alertPanel contentView] addSubview:button]; - + if (_buttonCount == 0) [button setKeyEquivalent:CPCarriageReturnCharacter]; else if ([title lowercaseString] === "cancel") @@ -307,42 +301,102 @@ var CPAlertWarningImage, else [button setKeyEquivalent:nil]; - _buttonCount++; [_buttons addObject:button]; - [self _layoutButtons]; + // For reference: does not actually work since this 'view' is not in the hierarchy. + // [self setNeedsLayout]; } -- (void)_layoutButtons +- (void)layoutSubviews { - var bounds = [[_alertPanel contentView] bounds], - count = [_buttons count], - offsetX = CGRectGetWidth(bounds), - offsetY = CGRectGetHeight(bounds) - 34.0; - for(var i=0; i < count; i++) - { - var button = _buttons[i]; + if (!_alertPanel) + [self _createPanel]; + var inset = [self currentValueForThemeAttribute:@"content-inset"], + iconOffset = [self currentValueForThemeAttribute:@"image-offset"], + theTitle, + theImage; + + switch (_alertStyle) + { + case CPWarningAlertStyle: theImage = [self currentValueForThemeAttribute:@"warning-image"]; + theTitle = @"Warning"; + break; + case CPInformationalAlertStyle: theImage = [self currentValueForThemeAttribute:@"information-image"]; + theTitle = @"Information"; + break; + case CPCriticalAlertStyle: theImage = [self currentValueForThemeAttribute:@"error-image"]; + theTitle = @"Error"; + break; + } + + [_alertImageView setImage:theImage]; + var imageSize = theImage ? [theImage size] : CGSizeMakeZero(); + [_alertImageView setFrame:CGRectMake(iconOffset.x, iconOffset.y, imageSize.width, imageSize.height)]; + + [_alertPanel setTitle:_windowTitle ? _windowTitle : theTitle]; + [_alertPanel setFloatingPanel:YES]; + [_alertPanel center]; + + [_messageLabel setTextColor:[self currentValueForThemeAttribute:@"message-text-color"]]; + [_messageLabel setFont:[self currentValueForThemeAttribute:@"message-text-font"]]; + [_messageLabel setTextShadowColor:[self currentValueForThemeAttribute:@"message-text-shadow-color"]]; + [_messageLabel setTextShadowOffset:[self currentValueForThemeAttribute:@"message-text-shadow-offset"]]; + [_messageLabel setAlignment:[self currentValueForThemeAttribute:@"message-text-alignment"]]; + [_messageLabel setLineBreakMode:CPLineBreakByWordWrapping]; + + [_informativeLabel setTextColor:[self currentValueForThemeAttribute:@"informative-text-color"]]; + [_informativeLabel setFont:[self currentValueForThemeAttribute:@"informative-text-font"]]; + [_informativeLabel setTextShadowColor:[self currentValueForThemeAttribute:@"informative-text-shadow-color"]]; + [_informativeLabel setTextShadowOffset:[self currentValueForThemeAttribute:@"informative-text-shadow-offset"]]; + [_informativeLabel setLineBreakMode:CPLineBreakByWordWrapping]; + + // FIXME sizeWithFontCorrection shouldn't be needed. + var bounds = [[_alertPanel contentView] bounds], + offsetX = CGRectGetWidth(bounds) - inset.right, + informativeOffset = [self currentValueForThemeAttribute:@"informative-offset"], + buttonOffset = [self currentValueForThemeAttribute:@"button-offset"], + + textWidth = offsetX - inset.left, + messageSize = [([_messageLabel stringValue] || " ") sizeWithFont:[_messageLabel font] inWidth:textWidth], + informationString = [_informativeLabel stringValue], + informativeSize = [(informationString || " ") sizeWithFont:[_informativeLabel font] inWidth:textWidth], + sizeWithFontCorrection = 6.0; + + [_messageLabel setFrame:CGRectMake(inset.left, inset.top, textWidth, messageSize.height + sizeWithFontCorrection)]; + [_informativeLabel setFrame:CGRectMake(inset.left, CGRectGetMaxY([_messageLabel frame]) + informativeOffset, textWidth, informativeSize.height + sizeWithFontCorrection)]; + // Don't let an empty informative label partially cover the buttons. + [_informativeLabel setHidden:!informationString]; + + var aRepresentativeButton = _buttons[0], + buttonY = MAX(CGRectGetMaxY([_alertImageView frame]), CGRectGetMaxY(informationString ? [_informativeLabel frame] : [_messageLabel frame])) + buttonOffset; // the lower of the bottom of the text and the bottom of the icon. + [aRepresentativeButton setTheme:[self theme]]; + [aRepresentativeButton sizeToFit]; + + // Make the window just tall enough to fit everything. Bit of a hack really. + var minimumSize = [self currentValueForThemeAttribute:@"size"], + desiredHeight = MAX(minimumSize.height, buttonY + CGRectGetHeight([aRepresentativeButton bounds]) + inset.bottom), + deltaY = desiredHeight - CGRectGetHeight(bounds), + frameSize = CGSizeMakeCopy([_alertPanel frame].size); + frameSize.height += deltaY; + [_alertPanel setFrameSize:frameSize]; + + var count = [_buttons count]; + while (count--) + { + var button = _buttons[count]; + + [button setTheme:[self theme]]; [button sizeToFit]; + var buttonBounds = [button bounds], width = MAX(80.0, CGRectGetWidth(buttonBounds)), height = CGRectGetHeight(buttonBounds); - offsetX -= (width + 10); - [button setFrame:CGRectMake(offsetX, offsetY, width, height)]; + offsetX -= width; + [button setFrame:CGRectMake(offsetX, buttonY, width, height)]; + offsetX -= 10; } -} -- (void)_layoutMessage -{ - var bounds = [[_alertPanel contentView] bounds], - width = CGRectGetWidth(bounds) - 73.0, - size = [([_messageLabel stringValue] || " ") sizeWithFont:[_messageLabel currentValueForThemeAttribute:@"font"] inWidth:width], - contentInset = [_messageLabel currentValueForThemeAttribute:@"content-inset"], - height = size.height + contentInset.top + contentInset.bottom; - - [_messageLabel setFrame:CGRectMake(57.0, 10.0, width, height)]; - - [_informativeLabel setFrame:CGRectMake(57.0, 10.0 + height + 6.0, width, CGRectGetHeight(bounds) - height - 50.0)]; } /*! @@ -352,23 +406,7 @@ var CPAlertWarningImage, */ - (void)runModal { - var theTitle; - - switch (_alertStyle) - { - case CPWarningAlertStyle: [_alertImageView setImage:CPAlertWarningImage]; - theTitle = @"Warning"; - break; - case CPInformationalAlertStyle: [_alertImageView setImage:CPAlertInformationImage]; - theTitle = @"Information"; - break; - case CPCriticalAlertStyle: [_alertImageView setImage:CPAlertErrorImage]; - theTitle = @"Error"; - break; - } - - [_alertPanel setTitle:_windowTitle ? _windowTitle : theTitle]; - + [self layoutSubviews]; [CPApp runModalForWindow:_alertPanel]; } diff --git a/AppKit/CPArrayController.j b/AppKit/CPArrayController.j index 3e3dd1778..b04f4e0f9 100644 --- a/AppKit/CPArrayController.j +++ b/AppKit/CPArrayController.j @@ -365,29 +365,29 @@ //Moving selection --(BOOL)canSelectPrevious +- (BOOL)canSelectPrevious { return [[self selectionIndexes] firstIndex] > 0 } --(BOOL)canSelectNext +-(void)selectPrevious:(id)sender { - return [[self selectionIndexes] firstIndex] < [[self arrangedObjects] count] -1; -} + var index = [[self selectionIndexes] firstIndex] - 1; --(void)selectNext:(id)sender -{ - var index = [[self selectionIndexes] firstIndex] + 1 || 0; - - if (index < [[self arrangedObjects] count]) + if (index >= 0) [self setSelectionIndexes:[CPIndexSet indexSetWithIndex:index]]; } --(void)selectPrevious:(id)sender +- (BOOL)canSelectNext { - var index = [[self selectionIndexes] firstIndex] - 1 || [[self arrangedObjects] count] - 1; + return [[self selectionIndexes] firstIndex] < [[self arrangedObjects] count] - 1; +} - if (index >= 0) +- (void)selectNext:(id)sender +{ + var index = [[self selectionIndexes] firstIndex] + 1; + + if (index < [[self arrangedObjects] count]) [self setSelectionIndexes:[CPIndexSet indexSetWithIndex:index]]; } @@ -424,6 +424,33 @@ [self rearrangeObjects]; } +- (void)insertObject:(id)anObject atArrangedObjectIndex:(int)anIndex +{ + if (![self canAdd]) + return; + + [self willChangeValueForKey:@"content"]; + [_contentObject insertObject:anObject atIndex:anIndex]; + [self didChangeValueForKey:@"content"]; + + if (_clearsFilterPredicateOnInsertion) + [self setFilterPredicate:nil]; + + [[self arrangedObjects] insertObject:anObject atIndex:anIndex]; + + if ([self selectsInsertedObjects]) + [self setSelectionIndex:anIndex]; + else + { + [self willChangeValueForKey:@"selectionIndexes"] + [[self selectionIndexes] shiftIndexesStartingAtIndex:anIndex by:1]; + [self didChangeValueForKey:@"selectionIndexes"]; + } + + if ([self avoidsEmptySelection] && [[self selectionIndexes] count] <= 0 && [_contentObject count] > 0) + [self setSelectionIndexes:[CPIndexSet indexSetWithIndex:0]]; +} + - (void)removeObject:(id)object { if (![self canRemove]) @@ -433,12 +460,14 @@ [_contentObject removeObject:object]; [self didChangeValueForKey:@"content"]; - if ([_filterPredicate evaluateWithObject:object]) + if (_filterPredicate === nil || [_filterPredicate evaluateWithObject:object]) { [self willChangeValueForKey:@"selectionIndexes"]; var pos = [_arrangedObjects indexOfObject:object]; + [_arrangedObjects removeObjectAtIndex:pos]; [_selectionIndexes shiftIndexesStartingAtIndex:pos by:-1]; + [self didChangeValueForKey:@"selectionIndexes"]; } } @@ -496,13 +525,34 @@ - (void)_removeObjects:(CPArray)objects { - var contentArray = [self contentArray], - count = [objects count]; + [self willChangeValueForKey:@"content"]; + [_contentObject removeObjectsInArray:objects]; + [self didChangeValueForKey:@"content"]; - for (var i=0; i= objectsCount) + selectionIndexes = [CPIndexSet indexSetWithIndex:objectsCount - 1]; + } + + [self willChangeValueForKey:@"selectionIndexes"]; + _selectionIndexes = selectionIndexes; + [self didChangeValueForKey:@"selectionIndexes"]; } - (BOOL)canInsert diff --git a/AppKit/CPButton.j b/AppKit/CPButton.j index 3510fe6da..01f173f8c 100644 --- a/AppKit/CPButton.j +++ b/AppKit/CPButton.j @@ -145,7 +145,7 @@ CPButtonStateMixed = CPThemeState("mixed"); _controlSize = CPRegularControlSize; - _keyEquivalent = @""; + _keyEquivalent = ""; _keyEquivalentModifierMask = 0; // [self setBezelStyle:CPRoundRectBezelStyle]; @@ -569,28 +569,25 @@ CPButtonStateMixed = CPThemeState("mixed"); */ - (void)setKeyEquivalent:(CPString)aString { - if (_keyEquivalent === aString) - return; - + _keyEquivalent = aString || @""; // Check if the key equivalent is the enter key // Treat \r and \n as the same key equivalent. See issue #710. if (aString === CPNewlineCharacter || aString === CPCarriageReturnCharacter) + { + [[self window] setDefaultButton:self]; [self setDefaultButton:YES]; - else + } + else if ([[self window] defaultButton] === self) + { + [[self window] setDefaultButton:nil]; [self setDefaultButton:NO]; - - _keyEquivalent = aString || @""; + } } - (void)viewWillMoveToWindow:(CPWindow)aWindow { - var selfWindow = [self window]; - - if (selfWindow === aWindow || aWindow === nil) - return; - - if ([selfWindow defaultButton] === self) - [selfWindow setDefaultButton:nil]; + if ([[self window] defaultButton] === self) + [[self window] setDefaultButton:nil]; if ([self keyEquivalent] === CPNewlineCharacter || [self keyEquivalent] === CPCarriageReturnCharacter) [aWindow setDefaultButton:self]; @@ -675,18 +672,18 @@ var CPButtonImageKey = @"CPButtonImageKey", { _controlSize = CPRegularControlSize; - _image = [aCoder decodeObjectForKey:CPButtonImageKey]; - _alternateImage = [aCoder decodeObjectForKey:CPButtonAlternateImageKey]; + [self setImage:[aCoder decodeObjectForKey:CPButtonImageKey]]; + [self setAlternateImage:[aCoder decodeObjectForKey:CPButtonAlternateImageKey]]; - _title = [aCoder decodeObjectForKey:CPButtonTitleKey]; - _alternateTitle = [aCoder decodeObjectForKey:CPButtonAlternateTitleKey]; + [self setTitle:[aCoder decodeObjectForKey:CPButtonTitleKey]]; + [self setAlternateTitle:[aCoder decodeObjectForKey:CPButtonAlternateTitleKey]]; [self setImageDimsWhenDisabled:[aCoder decodeObjectForKey:CPButtonImageDimsWhenDisabledKey]]; if ([aCoder containsValueForKey:CPButtonKeyEquivalentKey]) [self setKeyEquivalent:CFData.decodeBase64ToUtf16String([aCoder decodeObjectForKey:CPButtonKeyEquivalentKey])]; - _keyEquivalentModifierMask = [aCoder decodeObjectForKey:CPButtonKeyEquivalentMaskKey]; + [self setKeyEquivalentModifierMask:[aCoder decodeObjectForKey:CPButtonKeyEquivalentMaskKey]]; [self setNeedsLayout]; [self setNeedsDisplay:YES]; diff --git a/AppKit/CPKeyValueBinding.j b/AppKit/CPKeyValueBinding.j index a926cdd2b..1ea547e5d 100644 --- a/AppKit/CPKeyValueBinding.j +++ b/AppKit/CPKeyValueBinding.j @@ -181,7 +181,19 @@ var CPBindingOperationAnd = 0, valueTransformer; if (valueTransformerName) + { valueTransformer = [CPValueTransformer valueTransformerForName:valueTransformerName]; + + if (!valueTransformer) + { + var valueTransformerClass = CPClassFromString(valueTransformerName); + if (valueTransformerClass) + { + valueTransformer = [[valueTransformerClass alloc] init]; + [valueTransformerClass setValueTransformer:valueTransformer forName:valueTransformerName]; + } + } + } else valueTransformer = [options objectForKey:CPValueTransformerBindingOption]; diff --git a/AppKit/CPTableColumn.j b/AppKit/CPTableColumn.j index a6ebed652..a1d192913 100644 --- a/AppKit/CPTableColumn.j +++ b/AppKit/CPTableColumn.j @@ -226,12 +226,12 @@ CPTableColumnUserResizingMask = 1 << 1; /*! This method set's the "prototype" view which will be used to create all table cells in this column. - - It creates a snapshot of aView, using keyed archiving, which is then copied over and over for each + + It creates a snapshot of aView, using keyed archiving, which is then copied over and over for each individual cell that is shown. As a result, changes made after calling this method won't be reflected. Example: - + [tableColumn setDataView:someView]; // snapshot taken [[tableColumn dataView] setSomething:x]; //won't work @@ -291,7 +291,7 @@ CPTableColumnUserResizingMask = 1 << 1; // make sure only we have control over the size and placement [newDataView setAutoresizingMask:CPViewNotSizable]; - + return newDataView; } @@ -350,9 +350,9 @@ CPTableColumnUserResizingMask = 1 << 1; shouldBeHidden = !!shouldBeHidden if (_isHidden === shouldBeHidden) return; - + _isHidden = shouldBeHidden; - + [[self headerView] setHidden:shouldBeHidden]; [[self tableView] _tableColumnVisibilityDidChange:self]; } @@ -407,7 +407,8 @@ CPTableColumnUserResizingMask = 1 << 1; { var bindingName = keys[i], bindingPath = [aDataView _replacementKeyPathForBinding:bindingName], - bindingInfo = [bindingsDictionary objectForKey:bindingName]._info, + binding = [bindingsDictionary objectForKey:bindingName], + bindingInfo = binding._info, destination = [bindingInfo objectForKey:CPObservedObjectKey], keyPath = [bindingInfo objectForKey:CPObservedKeyPathKey], dotIndex = keyPath.lastIndexOf("."), @@ -438,6 +439,8 @@ CPTableColumnUserResizingMask = 1 << 1; value = [[firstValue valueForKeyPath:secondPart] objectAtIndex:aRow]; } + value = [binding transformValue:value withOptions:[bindingInfo objectForKey:CPOptionsKey]]; + // console.log(bindingName+" : "+keyPath+" : "+aRow+" : "+[[destination valueForKeyPath:keyPath] objectAtIndex:aRow]); [aDataView setValue:value forKey:bindingPath]; } @@ -488,7 +491,7 @@ var CPTableColumnIdentifierKey = @"CPTableColumnIdentifierKey", _resizingMask = [aCoder decodeBoolForKey:CPTableColumnResizingMaskKey]; _isHidden = [aCoder decodeBoolForKey:CPTableColumnIsHiddenKey]; _isEditable = [aCoder decodeBoolForKey:CPTableColumnIsEditableKey]; - + _sortDescriptorPrototype = [aCoder decodeObjectForKey:CPSortDescriptorPrototypeKey]; } @@ -509,7 +512,7 @@ var CPTableColumnIdentifierKey = @"CPTableColumnIdentifierKey", [aCoder encodeObject:_resizingMask forKey:CPTableColumnResizingMaskKey]; [aCoder encodeBool:_isHidden forKey:CPTableColumnIsHiddenKey]; [aCoder encodeBool:_isEditable forKey:CPTableColumnIsEditableKey]; - + [aCoder encodeObject:_sortDescriptorPrototype forKey:CPSortDescriptorPrototypeKey]; } diff --git a/AppKit/CPTableView.j b/AppKit/CPTableView.j index 0f898b516..d5d07e12d 100644 --- a/AppKit/CPTableView.j +++ b/AppKit/CPTableView.j @@ -2474,6 +2474,9 @@ CPTableViewFirstColumnOnlyAutoresizingStyle = 5; [self drawBackgroundInClipRect:exposedRect]; [self drawGridInClipRect:exposedRect]; [self highlightSelectionInClipRect:exposedRect]; + + if (_implementsCustomDrawRow) + [self _drawRows:_exposedRows clipRect:exposedRect]; } - (void)drawBackgroundInClipRect:(CGRect)aRect @@ -3308,6 +3311,10 @@ CPTableViewFirstColumnOnlyAutoresizingStyle = 5; if (![_delegate tableView:self shouldSelectRow:index]) [newSelection removeIndex:index]; } + + // as per cocoa + if ([newSelection count] === 0) + return; } // if empty selection is not allowed and the new selection has nothing selected, abort diff --git a/AppKit/CPTheme.j b/AppKit/CPTheme.j index b8c914273..96757205d 100644 --- a/AppKit/CPTheme.j +++ b/AppKit/CPTheme.j @@ -24,8 +24,9 @@ @import @import -var CPThemesByName = { }, - CPThemeDefaultTheme = nil; +var CPThemesByName = { }, + CPThemeDefaultTheme = nil, + CPThemeDefaultHudTheme = nil; /*! @@ -48,6 +49,27 @@ var CPThemesByName = { }, return CPThemeDefaultTheme; } +/*! + Set the default HUD theme. If set to nil, the default described in defaultHudTheme + will be used. +*/ ++ (void)setDefaultHudTheme:(CPTheme)aTheme +{ + CPThemeDefaultHudTheme = aTheme; +} + +/*! + The default HUD theme is (sometimes) used for windows with the CPHUDBackgroundWindowMask + style mask. The default is theme with the name of the default theme with -HUD appended + at the end. +*/ ++ (CPTheme)defaultHudTheme +{ + if (!CPThemeDefaultHudTheme) + CPThemeDefaultHudTheme = [CPTheme themeNamed:[[self defaultTheme] name] + "-HUD"]; + return CPThemeDefaultHudTheme; +} + + (CPTheme)themeNamed:(CPString)aName { return CPThemesByName[aName]; @@ -474,15 +496,15 @@ CPThemeStateCircular = CPThemeState("circular"); @end -var cachedNumberOfOnes = [ 0 /*000000*/, 1 /*000001*/, 1 /*000010*/, 2 /*000011*/, 1 /*000100*/, 2 /*000101*/, 2 /*000110*/, - 3 /*000111*/, 1 /*001000*/, 2 /*001001*/, 2 /*001010*/, 3 /*001011*/, 2 /*001100*/, 3 /*001101*/, - 3 /*001110*/, 4 /*001111*/, 1 /*010000*/, 2 /*010001*/, 2 /*010010*/, 3 /*010011*/, 2 /*010100*/, - 3 /*010101*/, 3 /*010110*/, 4 /*010111*/, 2 /*011000*/, 3 /*011001*/, 3 /*011010*/, 4 /*011011*/, - 3 /*011100*/, 4 /*011101*/, 4 /*011110*/, 5 /*011111*/, 1 /*100000*/, 2 /*100001*/, 2 /*100010*/, - 3 /*100011*/, 2 /*100100*/, 3 /*100101*/, 3 /*100110*/, 4 /*100111*/, 2 /*101000*/, 3 /*101001*/, - 3 /*101010*/, 4 /*101011*/, 3 /*101100*/, 4 /*101101*/, 4 /*101110*/, 5 /*101111*/, 2 /*110000*/, - 3 /*110001*/, 3 /*110010*/, 4 /*110011*/, 3 /*110100*/, 4 /*110101*/, 4 /*110110*/, 5 /*110111*/, - 3 /*111000*/, 4 /*111001*/, 4 /*111010*/, 5 /*111011*/, 4 /*111100*/, 5 /*111101*/, 5 /*111110*/, +var cachedNumberOfOnes = [ 0 /*000000*/, 1 /*000001*/, 1 /*000010*/, 2 /*000011*/, 1 /*000100*/, 2 /*000101*/, 2 /*000110*/, + 3 /*000111*/, 1 /*001000*/, 2 /*001001*/, 2 /*001010*/, 3 /*001011*/, 2 /*001100*/, 3 /*001101*/, + 3 /*001110*/, 4 /*001111*/, 1 /*010000*/, 2 /*010001*/, 2 /*010010*/, 3 /*010011*/, 2 /*010100*/, + 3 /*010101*/, 3 /*010110*/, 4 /*010111*/, 2 /*011000*/, 3 /*011001*/, 3 /*011010*/, 4 /*011011*/, + 3 /*011100*/, 4 /*011101*/, 4 /*011110*/, 5 /*011111*/, 1 /*100000*/, 2 /*100001*/, 2 /*100010*/, + 3 /*100011*/, 2 /*100100*/, 3 /*100101*/, 3 /*100110*/, 4 /*100111*/, 2 /*101000*/, 3 /*101001*/, + 3 /*101010*/, 4 /*101011*/, 3 /*101100*/, 4 /*101101*/, 4 /*101110*/, 5 /*101111*/, 2 /*110000*/, + 3 /*110001*/, 3 /*110010*/, 4 /*110011*/, 3 /*110100*/, 4 /*110101*/, 4 /*110110*/, 5 /*110111*/, + 3 /*111000*/, 4 /*111001*/, 4 /*111010*/, 5 /*111011*/, 4 /*111100*/, 5 /*111101*/, 5 /*111110*/, 6 /*111111*/ ]; var numberOfOnes = function(aNumber) diff --git a/AppKit/CPViewController.j b/AppKit/CPViewController.j index 3fffe261d..ab8c3a22e 100644 --- a/AppKit/CPViewController.j +++ b/AppKit/CPViewController.j @@ -162,8 +162,6 @@ var CPViewControllerCachedCibs; if ([cibOwner respondsToSelector:@selector(viewControllerDidLoadCib:)]) [cibOwner viewControllerDidLoadCib:self]; - - [self viewDidLoad]; } return _view; @@ -189,7 +187,13 @@ var CPViewControllerCachedCibs; */ - (void)setView:(CPView)aView { + var viewWasLoaded = !_view; + _view = aView; + + // Make sure the viewDidLoad method is called if the view is set directly + if (viewWasLoaded) + [self viewDidLoad]; } @end diff --git a/AppKit/CPWebView.j b/AppKit/CPWebView.j index bf36a7773..dca2637ab 100644 --- a/AppKit/CPWebView.j +++ b/AppKit/CPWebView.j @@ -50,23 +50,23 @@ CPWebViewScrollNative = 2; { CPScrollView _scrollView; CPView _frameView; - + IFrame _iframe; CPString _mainFrameURL; CPArray _backwardStack; CPArray _forwardStack; - + BOOL _ignoreLoadStart; BOOL _ignoreLoadEnd; - + id _downloadDelegate; id _frameLoadDelegate; id _policyDelegate; id _resourceLoadDelegate; id _UIDelegate; - + CPWebScriptObject _wso; - + CPString _url; CPString _html; @@ -95,10 +95,10 @@ CPWebViewScrollNative = 2; _backwardStack = []; _forwardStack = []; _scrollMode = CPWebViewScrollNative; - + [self _initDOMWithFrame:aFrame]; } - + return self; } @@ -106,52 +106,52 @@ CPWebViewScrollNative = 2; { _ignoreLoadStart = YES; _ignoreLoadEnd = YES; - + _iframe = document.createElement("iframe"); _iframe.name = "iframe_" + Math.floor(Math.random()*10000); _iframe.style.width = "100%"; _iframe.style.height = "100%"; _iframe.style.borderWidth = "0px"; _iframe.frameBorder = "0"; - + [self setDrawsBackground:YES]; - + _loadCallback = function() { // HACK: this block handles the case where we don't know about loads initiated by the user clicking a link if (!_ignoreLoadStart) { // post the start load notification [self _startedLoading]; - + if (_mainFrameURL) [_backwardStack addObject:_mainFrameURL]; - + // FIXME: this doesn't actually get the right URL for different domains. Not possible due to browser security restrictions. _mainFrameURL = _iframe.src; _mainFrameURL = _iframe.src; - + // clear the forward [_forwardStack removeAllObjects]; } else _ignoreLoadStart = NO; - + if (!_ignoreLoadEnd) { [self _finishedLoading]; } else _ignoreLoadEnd = NO; - + [[CPRunLoop currentRunLoop] limitDateForMode:CPDefaultRunLoopMode]; } - + if (_iframe.addEventListener) _iframe.addEventListener("load", _loadCallback, false); else if (_iframe.attachEvent) _iframe.attachEvent("onload", _loadCallback); - - + + _frameView = [[CPView alloc] initWithFrame:[self bounds]]; [_frameView setAutoresizingMask:CPViewWidthSizable|CPViewHeightSizable]; @@ -159,9 +159,9 @@ CPWebViewScrollNative = 2; [_scrollView setAutohidesScrollers:YES]; [_scrollView setAutoresizingMask:CPViewWidthSizable|CPViewHeightSizable]; [_scrollView setDocumentView:_frameView]; - + _frameView._DOMElement.appendChild(_iframe); - + [self _setScrollMode:_scrollMode]; [self addSubview:_scrollView]; @@ -169,7 +169,7 @@ CPWebViewScrollNative = 2; - (void)setFrameSize:(CPSize)aSize -{ +{ [super setFrameSize:aSize]; [self _resizeWebFrame]; } @@ -211,7 +211,7 @@ CPWebViewScrollNative = 2; { var visibleRect = [_frameView visibleRect]; [_frameView setFrameSize:CGSizeMake(CGRectGetMaxX(visibleRect), CGRectGetMaxY(visibleRect))]; - + // try to get the document size so we can correctly set the frame var win = null; try { win = [self DOMWindow]; } catch (e) {} @@ -229,7 +229,7 @@ CPWebViewScrollNative = 2; else { CPLog.warn("using default size 800*1600"); - + [_frameView setFrameSize:CGSizeMake(800, 1600)]; } @@ -242,7 +242,7 @@ CPWebViewScrollNative = 2; { if (_scrollMode == aScrollMode) return; - + [self _setScrollMode:aScrollMode]; } @@ -252,7 +252,7 @@ CPWebViewScrollNative = 2; _scrollMode = CPWebViewScrollNative; else _scrollMode = aScrollMode; - + _ignoreLoadStart = YES; _ignoreLoadEnd = YES; @@ -263,16 +263,16 @@ CPWebViewScrollNative = 2; { [_scrollView setHasHorizontalScroller:YES]; [_scrollView setHasVerticalScroller:YES]; - + _iframe.setAttribute("scrolling", "no"); } else { [_scrollView setHasHorizontalScroller:NO]; [_scrollView setHasVerticalScroller:NO]; - + _iframe.setAttribute("scrolling", "auto"); - + [_frameView setFrameSize:[_scrollView bounds].size]; } @@ -293,13 +293,13 @@ CPWebViewScrollNative = 2; [_frameView setFrameSize:[_scrollView contentSize]]; [self _startedLoading]; - + _ignoreLoadStart = YES; _ignoreLoadEnd = NO; - + _url = null; _html = aString; - + [self _load]; } @@ -308,13 +308,13 @@ CPWebViewScrollNative = 2; [self _setScrollMode:CPWebViewScrollNative]; [self _startedLoading]; - + _ignoreLoadStart = YES; _ignoreLoadEnd = NO; - + _url = _mainFrameURL; _html = null; - + [self _load]; } @@ -335,12 +335,13 @@ CPWebViewScrollNative = 2; _loadHTMLStringTimer = nil; } - // need to give the browser a chance to reset iframe, otherwise we'll be document.write()-ing the previous document + // need to give the browser a chance to reset iframe, otherwise we'll be document.write()-ing the previous document _loadHTMLStringTimer = window.setTimeout(function() { var win = [self DOMWindow]; - - win.document.write(_html); + + if (win) + win.document.write(_html); window.setTimeout(_loadCallback, 1); }, 0); @@ -372,7 +373,7 @@ CPWebViewScrollNative = 2; } - (void)setMainFrameURL:(CPString)URLString -{ +{ if (_mainFrameURL) [_backwardStack addObject:_mainFrameURL]; _mainFrameURL = URLString; @@ -389,9 +390,9 @@ CPWebViewScrollNative = 2; [_forwardStack addObject:_mainFrameURL]; _mainFrameURL = [_backwardStack lastObject]; [_backwardStack removeLastObject]; - + [self _loadMainFrameURL]; - + return YES; } return NO; @@ -405,9 +406,9 @@ CPWebViewScrollNative = 2; [_backwardStack addObject:_mainFrameURL]; _mainFrameURL = [_forwardStack lastObject]; [_forwardStack removeLastObject]; - + [self _loadMainFrameURL]; - + return YES; } return NO; @@ -630,7 +631,7 @@ CPWebViewScrollNative = 2; - (id)initWithCoder:(CPCoder)aCoder { self = [super initWithCoder:aCoder]; - + if (self) { // FIXME: encode/decode these? @@ -638,14 +639,14 @@ CPWebViewScrollNative = 2; _backwardStack = []; _forwardStack = []; _scrollMode = CPWebViewScrollNative; - + #if PLATFORM(DOM) [self _initDOMWithFrame:[self frame]]; #endif [self setBackgroundColor:[CPColor whiteColor]]; } - + return self; } diff --git a/AppKit/CPWindow/CPWindow.j b/AppKit/CPWindow/CPWindow.j index b3dee6f49..70e7e9128 100644 --- a/AppKit/CPWindow/CPWindow.j +++ b/AppKit/CPWindow/CPWindow.j @@ -2413,7 +2413,8 @@ CPTexturedBackgroundWindowMask _defaultButton = aButton; - [_defaultButton setKeyEquivalent:CPCarriageReturnCharacter]; + if ([_defaultButton keyEquivalent] !== CPCarriageReturnCharacter) + [_defaultButton setKeyEquivalent:CPCarriageReturnCharacter]; } - (CPButton)defaultButton diff --git a/AppKit/Resources/CPAlert/LICENSE b/AppKit/Resources/CPAlert/LICENSE deleted file mode 100644 index 4fa7dc4fb..000000000 --- a/AppKit/Resources/CPAlert/LICENSE +++ /dev/null @@ -1,58 +0,0 @@ -These graphics come from the Tango Desktop Project (http://tango.freedesktop.org/Tango_Desktop_Project). -They are released under the Creative Commons Attribution Share-Alike license, full text below. - - -(license available from CC here: http://creativecommons.org/licenses/by-sa/2.5/) - -License - -THE WORK (AS DEFINED BELOW) IS PROVIDED UNDER THE TERMS OF THIS CREATIVE COMMONS PUBLIC LICENSE ("CCPL" OR "LICENSE"). THE WORK IS PROTECTED BY COPYRIGHT AND/OR OTHER APPLICABLE LAW. ANY USE OF THE WORK OTHER THAN AS AUTHORIZED UNDER THIS LICENSE OR COPYRIGHT LAW IS PROHIBITED. - -BY EXERCISING ANY RIGHTS TO THE WORK PROVIDED HERE, YOU ACCEPT AND AGREE TO BE BOUND BY THE TERMS OF THIS LICENSE. THE LICENSOR GRANTS YOU THE RIGHTS CONTAINED HERE IN CONSIDERATION OF YOUR ACCEPTANCE OF SUCH TERMS AND CONDITIONS. - -1. Definitions - -"Collective Work" means a work, such as a periodical issue, anthology or encyclopedia, in which the Work in its entirety in unmodified form, along with a number of other contributions, constituting separate and independent works in themselves, are assembled into a collective whole. A work that constitutes a Collective Work will not be considered a Derivative Work (as defined below) for the purposes of this License. -"Derivative Work" means a work based upon the Work or upon the Work and other pre-existing works, such as a translation, musical arrangement, dramatization, fictionalization, motion picture version, sound recording, art reproduction, abridgment, condensation, or any other form in which the Work may be recast, transformed, or adapted, except that a work that constitutes a Collective Work will not be considered a Derivative Work for the purpose of this License. For the avoidance of doubt, where the Work is a musical composition or sound recording, the synchronization of the Work in timed-relation with a moving image ("synching") will be considered a Derivative Work for the purpose of this License. -"Licensor" means the individual or entity that offers the Work under the terms of this License. -"Original Author" means the individual or entity who created the Work. -"Work" means the copyrightable work of authorship offered under the terms of this License. -"You" means an individual or entity exercising rights under this License who has not previously violated the terms of this License with respect to the Work, or who has received express permission from the Licensor to exercise rights under this License despite a previous violation. -"License Elements" means the following high-level license attributes as selected by Licensor and indicated in the title of this License: Attribution, ShareAlike. -2. Fair Use Rights. Nothing in this license is intended to reduce, limit, or restrict any rights arising from fair use, first sale or other limitations on the exclusive rights of the copyright owner under copyright law or other applicable laws. - -3. License Grant. Subject to the terms and conditions of this License, Licensor hereby grants You a worldwide, royalty-free, non-exclusive, perpetual (for the duration of the applicable copyright) license to exercise the rights in the Work as stated below: - -to reproduce the Work, to incorporate the Work into one or more Collective Works, and to reproduce the Work as incorporated in the Collective Works; -to create and reproduce Derivative Works; -to distribute copies or phonorecords of, display publicly, perform publicly, and perform publicly by means of a digital audio transmission the Work including as incorporated in Collective Works; -to distribute copies or phonorecords of, display publicly, perform publicly, and perform publicly by means of a digital audio transmission Derivative Works. -For the avoidance of doubt, where the work is a musical composition: - -Performance Royalties Under Blanket Licenses. Licensor waives the exclusive right to collect, whether individually or via a performance rights society (e.g. ASCAP, BMI, SESAC), royalties for the public performance or public digital performance (e.g. webcast) of the Work. -Mechanical Rights and Statutory Royalties. Licensor waives the exclusive right to collect, whether individually or via a music rights society or designated agent (e.g. Harry Fox Agency), royalties for any phonorecord You create from the Work ("cover version") and distribute, subject to the compulsory license created by 17 USC Section 115 of the US Copyright Act (or the equivalent in other jurisdictions). -Webcasting Rights and Statutory Royalties. For the avoidance of doubt, where the Work is a sound recording, Licensor waives the exclusive right to collect, whether individually or via a performance-rights society (e.g. SoundExchange), royalties for the public digital performance (e.g. webcast) of the Work, subject to the compulsory license created by 17 USC Section 114 of the US Copyright Act (or the equivalent in other jurisdictions). -The above rights may be exercised in all media and formats whether now known or hereafter devised. The above rights include the right to make such modifications as are technically necessary to exercise the rights in other media and formats. All rights not expressly granted by Licensor are hereby reserved. - -4. Restrictions.The license granted in Section 3 above is expressly made subject to and limited by the following restrictions: - -You may distribute, publicly display, publicly perform, or publicly digitally perform the Work only under the terms of this License, and You must include a copy of, or the Uniform Resource Identifier for, this License with every copy or phonorecord of the Work You distribute, publicly display, publicly perform, or publicly digitally perform. You may not offer or impose any terms on the Work that alter or restrict the terms of this License or the recipients' exercise of the rights granted hereunder. You may not sublicense the Work. You must keep intact all notices that refer to this License and to the disclaimer of warranties. You may not distribute, publicly display, publicly perform, or publicly digitally perform the Work with any technological measures that control access or use of the Work in a manner inconsistent with the terms of this License Agreement. The above applies to the Work as incorporated in a Collective Work, but this does not require the Collective Work apart from the Work itself to be made subject to the terms of this License. If You create a Collective Work, upon notice from any Licensor You must, to the extent practicable, remove from the Collective Work any credit as required by clause 4(c), as requested. If You create a Derivative Work, upon notice from any Licensor You must, to the extent practicable, remove from the Derivative Work any credit as required by clause 4(c), as requested. -You may distribute, publicly display, publicly perform, or publicly digitally perform a Derivative Work only under the terms of this License, a later version of this License with the same License Elements as this License, or a Creative Commons iCommons license that contains the same License Elements as this License (e.g. Attribution-ShareAlike 2.5 Japan). You must include a copy of, or the Uniform Resource Identifier for, this License or other license specified in the previous sentence with every copy or phonorecord of each Derivative Work You distribute, publicly display, publicly perform, or publicly digitally perform. You may not offer or impose any terms on the Derivative Works that alter or restrict the terms of this License or the recipients' exercise of the rights granted hereunder, and You must keep intact all notices that refer to this License and to the disclaimer of warranties. You may not distribute, publicly display, publicly perform, or publicly digitally perform the Derivative Work with any technological measures that control access or use of the Work in a manner inconsistent with the terms of this License Agreement. The above applies to the Derivative Work as incorporated in a Collective Work, but this does not require the Collective Work apart from the Derivative Work itself to be made subject to the terms of this License. -If you distribute, publicly display, publicly perform, or publicly digitally perform the Work or any Derivative Works or Collective Works, You must keep intact all copyright notices for the Work and provide, reasonable to the medium or means You are utilizing: (i) the name of the Original Author (or pseudonym, if applicable) if supplied, and/or (ii) if the Original Author and/or Licensor designate another party or parties (e.g. a sponsor institute, publishing entity, journal) for attribution in Licensor's copyright notice, terms of service or by other reasonable means, the name of such party or parties; the title of the Work if supplied; to the extent reasonably practicable, the Uniform Resource Identifier, if any, that Licensor specifies to be associated with the Work, unless such URI does not refer to the copyright notice or licensing information for the Work; and in the case of a Derivative Work, a credit identifying the use of the Work in the Derivative Work (e.g., "French translation of the Work by Original Author," or "Screenplay based on original Work by Original Author"). Such credit may be implemented in any reasonable manner; provided, however, that in the case of a Derivative Work or Collective Work, at a minimum such credit will appear where any other comparable authorship credit appears and in a manner at least as prominent as such other comparable authorship credit. -5. Representations, Warranties and Disclaimer - -UNLESS OTHERWISE AGREED TO BY THE PARTIES IN WRITING, LICENSOR OFFERS THE WORK AS-IS AND MAKES NO REPRESENTATIONS OR WARRANTIES OF ANY KIND CONCERNING THE MATERIALS, EXPRESS, IMPLIED, STATUTORY OR OTHERWISE, INCLUDING, WITHOUT LIMITATION, WARRANTIES OF TITLE, MERCHANTIBILITY, FITNESS FOR A PARTICULAR PURPOSE, NONINFRINGEMENT, OR THE ABSENCE OF LATENT OR OTHER DEFECTS, ACCURACY, OR THE PRESENCE OF ABSENCE OF ERRORS, WHETHER OR NOT DISCOVERABLE. SOME JURISDICTIONS DO NOT ALLOW THE EXCLUSION OF IMPLIED WARRANTIES, SO SUCH EXCLUSION MAY NOT APPLY TO YOU. - -6. Limitation on Liability. EXCEPT TO THE EXTENT REQUIRED BY APPLICABLE LAW, IN NO EVENT WILL LICENSOR BE LIABLE TO YOU ON ANY LEGAL THEORY FOR ANY SPECIAL, INCIDENTAL, CONSEQUENTIAL, PUNITIVE OR EXEMPLARY DAMAGES ARISING OUT OF THIS LICENSE OR THE USE OF THE WORK, EVEN IF LICENSOR HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES. - -7. Termination - -This License and the rights granted hereunder will terminate automatically upon any breach by You of the terms of this License. Individuals or entities who have received Derivative Works or Collective Works from You under this License, however, will not have their licenses terminated provided such individuals or entities remain in full compliance with those licenses. Sections 1, 2, 5, 6, 7, and 8 will survive any termination of this License. -Subject to the above terms and conditions, the license granted here is perpetual (for the duration of the applicable copyright in the Work). Notwithstanding the above, Licensor reserves the right to release the Work under different license terms or to stop distributing the Work at any time; provided, however that any such election will not serve to withdraw this License (or any other license that has been, or is required to be, granted under the terms of this License), and this License will continue in full force and effect unless terminated as stated above. -8. Miscellaneous - -Each time You distribute or publicly digitally perform the Work or a Collective Work, the Licensor offers to the recipient a license to the Work on the same terms and conditions as the license granted to You under this License. -Each time You distribute or publicly digitally perform a Derivative Work, Licensor offers to the recipient a license to the original Work on the same terms and conditions as the license granted to You under this License. -If any provision of this License is invalid or unenforceable under applicable law, it shall not affect the validity or enforceability of the remainder of the terms of this License, and without further action by the parties to this agreement, such provision shall be reformed to the minimum extent necessary to make such provision valid and enforceable. -No term or provision of this License shall be deemed waived and no breach consented to unless such waiver or consent shall be in writing and signed by the party to be charged with such waiver or consent. -This License constitutes the entire agreement between the parties with respect to the Work licensed here. There are no understandings, agreements or representations with respect to the Work not specified here. Licensor shall not be bound by any additional provisions that may appear in any communication from You. This License may not be modified without the mutual written agreement of the Licensor and You. diff --git a/AppKit/Resources/CPAlert/dialog-error.png b/AppKit/Resources/CPAlert/dialog-error.png deleted file mode 100644 index aa4ae7509..000000000 Binary files a/AppKit/Resources/CPAlert/dialog-error.png and /dev/null differ diff --git a/AppKit/Resources/CPAlert/dialog-information.png b/AppKit/Resources/CPAlert/dialog-information.png deleted file mode 100644 index cbaf6dbc4..000000000 Binary files a/AppKit/Resources/CPAlert/dialog-information.png and /dev/null differ diff --git a/AppKit/Resources/CPAlert/dialog-warning.png b/AppKit/Resources/CPAlert/dialog-warning.png deleted file mode 100644 index 47d84e49d..000000000 Binary files a/AppKit/Resources/CPAlert/dialog-warning.png and /dev/null differ diff --git a/AppKit/Themes/Aristo/Resources/alert-error.png b/AppKit/Themes/Aristo/Resources/alert-error.png new file mode 100644 index 000000000..edded8310 Binary files /dev/null and b/AppKit/Themes/Aristo/Resources/alert-error.png differ diff --git a/AppKit/Themes/Aristo/Resources/alert-info.png b/AppKit/Themes/Aristo/Resources/alert-info.png new file mode 100644 index 000000000..8ebf63789 Binary files /dev/null and b/AppKit/Themes/Aristo/Resources/alert-info.png differ diff --git a/AppKit/Themes/Aristo/Resources/alert-warning.png b/AppKit/Themes/Aristo/Resources/alert-warning.png new file mode 100644 index 000000000..c5e480421 Binary files /dev/null and b/AppKit/Themes/Aristo/Resources/alert-warning.png differ diff --git a/AppKit/Themes/Aristo/ThemeDescriptors.j b/AppKit/Themes/Aristo/ThemeDescriptors.j index 98b7af5ad..e235d5bea 100755 --- a/AppKit/Themes/Aristo/ThemeDescriptors.j +++ b/AppKit/Themes/Aristo/ThemeDescriptors.j @@ -10,56 +10,66 @@ @import @import +var themedButtonValues = nil, + themedVerticalScrollerValues = nil, + themedHorizontalScrollerValues = nil, + themedSegmentedControlValues = nil, + themedHorizontalSliderValues = nil, + themedVerticalSliderValues = nil, + themedCircularSliderValues = nil, + themedButtonBarValues = nil, + themedAlertValues = nil; + /* HOW TO ADD OR MODIFY THEMED ELEMENTS - + This file serves both as a means of defining default theme values and of defining an interface for the automatically generated theme showcase, so you have to keep both in mind. - + Let's say you define a new view class and you want to theme it. Here's how to do it with minimum work. - + 1. Define +themeAttributes in your class if you define custom attributes. - + 2. If you want a custom name to appear in the theme showcase, define +themeClass and return a string with the name all in lowercase and words separated by dashes. If you don't provide +themeClass, Cappuccino will separate the themed class name into words at each uppercased letter followed by lowercase letters. - + 3. Use the following template for defining your themed view (using a calendar view as an example). Note that we make a separate method for creating and configuring the view. This is to more clearly separate the two functions of ThemeDescriptors.j (defining themes and defining the showcase interface), and also to allow us to easily reuse the creation/configuration code if we make a HUD version of our themed view. - + // Create a private variable to hold the theme values, we can share this with the HUD theme var themedCalendarViewValues = nil; - + + (CPCalendarView)makeCalendarView { - // Define the size of the sample that will appear in the theme showcase. + // Define the size of the sample that will appear in the theme showcase. // The maximum width/height is around 180 pixels. - + var calendar = [[CPCalendarView alloc] initWithFrame:CGRectMake(0.0, 0.0, 150.0, 100.0)]; - + // Do any other initialization of the calendar you want to do here. In this case // we want the calendar to show our birthdate instead of the current date. [calendar setDate:new Date(1961, 2, 30)]; - + return calendar; } - + + (CPCalendarView)themedCalendarView { var calendar = [self makeCalendarView], - + // Now define some pattern colors. We want to define the bezel for the calendar // using a nine part image, which consists of nine slices in the order top/left, // top, top/right, left, center, right, bottom/left, bottom, bottom/right. // We can do this declaritively using the PatternColor function, which takes an array // of slice declarations, with each declaration being an array of [filename, width, height]. - + bezelColor = PatternColor( [ [@"calendar-bezel-0.png", 10.0, 10.0], @@ -72,9 +82,9 @@ [@"calendar-bezel-7.png", 1.0, 10.0], [@"calendar-bezel-8.png", 10.0, 10.0] ]), - + // Define an alternate bezel for when the calendar is disabled. - + disabledBezelColor = PatternColor( [ [@"calendar-disabled-bezel-0.png", 10.0, 10.0], @@ -87,40 +97,40 @@ [@"calendar-disabled-bezel-7.png", 1.0, 10.0], [@"calendar-disabled-bezel-8.png", 10.0, 10.0] ]), - + // We would like the font to be dark blue and lighter blue when disabled - + textColor = [CPColor colorWithHexString:@"001B48"], disabledTextColor = [textColor colorWithAlphaComponent:0.6]; - + // Now we will define our theme values. These are done declaratively in an array, // where each element is an array of 2 or 3 values: attribute name, value, and optional state. - + themedCalendarViewValues = [ [@"bezel-color", bezelColor], [@"bezel-color", disabledBezelColor, CPThemeStateDisabled], - + [@"text-color", textColor], [@"text-color", disabledTextColor, CPThemeStateDisabled], - + // We will also define a minimum size [@"min-size", CGSizeMake(100.0, 100.0)] ]; - + // Now we just register our values - + [self registerThemeValues:themedCalendarViewValues forView:calendar]; } - + That's all there is to it. Note that PatternColor can also be used to create a simple patterned color: - + color = PatternColor(filename, width, height); - + It can also be used to create a three part image by specifying three images slices with [filename, width, height] and an orientation: - + trackColor = PatternColor( [ ["horizontal-track-left.png", 4.0, 5.0], @@ -128,7 +138,7 @@ ["horizontal-track-right.png", 4.0, 5.0] ], PatternIsHorizontal); - + trackColor = PatternColor( [ ["vertical-track-top.png", 5.0, 6.0], @@ -137,44 +147,44 @@ ], PatternIsVertical); - + SUBTHEMES - + If you want to create a theme that inherits from another theme, for example the way the Aristo-HUD theme inherits from Aristo, you can do this easily by following these steps: - + 1. Your subtheme should be named "-", where is the name of the theme from which you are inheriting, and is the subtheme identifier. For example, "Aristo-HUD" inherits from "Aristo". - + 2. Decide which elements in the subtheme will be changed and which will be inherited. For example, in a HUD theme you may want to use a dark or black background and white text. - + 3. If your subtheme will replace pattern colors in the inherited theme, use the same filename as in the inherited theme and put the pattern images in a subdirectory of the inherited directory. The subdirectory's name should be the same as the subtheme identifier. For example, Aristo-HUD's pattern images are in a "HUD" subdirectory of the "Aristo" directory. - + 4. In the subtheme class (such as AristoHUDThemeDescriptor), add a method with this template: - + + (CPCalendarView)themedCalendarView { var calendar = [AristoThemeDescriptor makeCalendarView], subthemeValues = nil; // This may change according to your needs, see below - + [self registerThemeValues:subthemeValues forView:calendar inherit:themedCalendarViewValues]; - + return calendar; } - + NOTE: If you pass nil or an empty array for the subthemeValues, ALL pattern images will be automatically inherited and MUST be present in the subtheme directory. - + Depending on your subtheme, you will change subthemeValues to indicate which values you wish to override or remove from the inherited theme. - + For example, let's say in your subtheme you want to use white text instead of dark blue, and slightly gray text when it is disabled. In addition, you decide not to inherit the disabled bezel color pattern. Here is how your updated theme method would look: @@ -182,10 +192,10 @@ + (CPCalendarView)themedCalendarView { var calendar = [AristoThemeDescriptor makeCalendarView], - + textColor = [CPColor whiteColor], disabledTextColor = [textColor colorWithAlphaComponent:0.6], - subthemeValues = + subthemeValues = [ [@"bezel-color", nil, CPThemeStateDisabled], [@"text-color", textColor], @@ -196,15 +206,15 @@ return calendar; } - + If your subtheme consistently applies the same overrides to all themed views, you may want to create a separate method that supplies those overrides. For example, Aristo-HUD uses the following method: - + + (CPArray)defaultThemeOverridesAddedTo:(CPArray)themeValues - + For the example we used above, we could define such a method as follows: - + + (CPArray)defaultThemeOverridesAddedTo:(CPArray)themeValues { var textColor = [CPColor whiteColor], @@ -220,23 +230,23 @@ return overrides; } - + Note that the values in the themeValues parameter override the defaults provided by the method. - + Our themed view method becomes: + (CPCalendarView)themedCalendarView { var calendar = [AristoThemeDescriptor makeCalendarView]; - [self registerThemeValues:[self defaultThemeOverridesAddedTo:nil] - forView:calendar + [self registerThemeValues:[self defaultThemeOverridesAddedTo:nil] + forView:calendar inherit:themedCalendarViewValues]; return calendar; } - + As a last example, let's assume another type of themed view in your subtheme wants to add some new theme values to the default overrides. The theme method might look like this: @@ -250,36 +260,35 @@ ["calendar-header-right.png", 5.0, 23.0] ], PatternIsHorizontal), - subthemeValues = + subthemeValues = [ [@"background-color", headerColor] ]; - [self registerThemeValues:[self defaultThemeOverridesAddedTo:subthemeValues] - forView:calendar - inherit:themedCalendarHeaderViewValues];; + [self registerThemeValues:[self defaultThemeOverridesAddedTo:subthemeValues] + forView:calendar + inherit:themedCalendarHeaderViewValues]; return calendar; } - + ADDING ARISTO-HUD CONTROLS - + If you want to add a new themed control to Aristo-HUD, you should use the +defaultThemeOverridesAddedTo: method to ensure visual consistency with the rest of Aristo-HUD. The overrides returned by this method do the following: - + - Sets the "text-color" attribute to white in the normal state and [CPColor colorWithCalibratedWhite:1.0 alpha:0.6] in the disabled state. - + - Set "text-shadow-color" to black. - + - Sets "text-shadow-offset" to (-1.0, -1.0). - + These are the standards used by Aristo-HUD. If you want to override these defaults, simply add those overrides to the values you pass to +defaultThemeOverridesAddedTo:. */ - @implementation AristoThemeDescriptor : BKThemeDescriptor { } @@ -289,7 +298,6 @@ return @"Aristo"; } -var themedButtonValues = nil; + (CPButton)makeButton { @@ -339,7 +347,7 @@ var themedButtonValues = nil; ["default-button-bezel-disabled-right.png", 4.0, 24.0] ], PatternIsHorizontal), - + disabledBezelColor = PatternColor( [ ["button-bezel-disabled-left.png", 4.0, 24.0], @@ -347,11 +355,11 @@ var themedButtonValues = nil; ["button-bezel-disabled-right.png", 4.0, 24.0] ], PatternIsHorizontal), - + defaultTextColor = [CPColor colorWithCalibratedRed:13.0/255.0 green:51.0/255.0 blue:70.0/255.0 alpha:1.0], defaultDisabledTextColor = [CPColor colorWithCalibratedRed:13.0/255.0 green:51.0/255.0 blue:70.0/255.0 alpha:0.6]; - - themedButtonValues = + + themedButtonValues = [ [@"font", [CPFont boldSystemFontOfSize:12.0], CPThemeStateBordered], [@"text-color", [CPColor colorWithCalibratedWhite:79.0 / 255.0 alpha:1.0]], @@ -360,25 +368,25 @@ var themedButtonValues = nil; [@"text-shadow-offset", CGSizeMake(0.0, 1.0), CPThemeStateBordered], [@"line-break-mode", CPLineBreakByTruncatingTail], [@"content-inset", CGInsetMake(0.0, 5.0, 0.0, 5.0), CPThemeStateBordered], - + [@"bezel-color", bezelColor, CPThemeStateBordered], [@"bezel-color", highlightedBezelColor, CPThemeStateBordered | CPThemeStateHighlighted], - + [@"text-color", [CPColor colorWithCalibratedWhite:0.6 alpha:1.0], CPThemeStateDisabled], [@"bezel-color", disabledBezelColor, CPThemeStateBordered | CPThemeStateDisabled], [@"bezel-color", defaultDisabledBezelColor, CPThemeStateBordered | CPThemeStateDefault | CPThemeStateDisabled], - + [@"text-color", defaultTextColor, CPThemeStateDefault], [@"text-color", defaultDisabledTextColor, CPThemeStateDefault | CPThemeStateDisabled], [@"bezel-color", defaultBezelColor, CPThemeStateBordered | CPThemeStateDefault], [@"bezel-color", defaultHighlightedBezelColor, CPThemeStateBordered | CPThemeStateHighlighted | CPThemeStateDefault], - + [@"min-size", CGSizeMake(0.0, 24.0)], [@"max-size", CGSizeMake(-1.0, 24.0)] ]; - + [self registerThemeValues:themedButtonValues forView:button]; - + return button; } @@ -419,12 +427,12 @@ var themedButtonValues = nil; ["popup-bezel-disabled-right.png", 27.0, 24.0] ], PatternIsHorizontal), - - themeValues = + + themeValues = [ [@"bezel-color", color, CPThemeStateBordered], [@"bezel-color", disabledColor, CPThemeStateBordered | CPThemeStateDisabled], - + [@"content-inset", CGInsetMake(0, 27.0 + 5.0, 0, 5.0), CPThemeStateBordered], [@"font", [CPFont boldSystemFontOfSize:12.0]], [@"text-color", [CPColor colorWithCalibratedWhite:79.0 / 255.0 alpha:1.0]], @@ -432,16 +440,16 @@ var themedButtonValues = nil; [@"text-color", [CPColor colorWithCalibratedWhite:0.6 alpha:1.0], CPThemeStateBordered | CPThemeStateDisabled], [@"text-shadow-color", [CPColor colorWithCalibratedWhite:240.0 / 255.0 alpha:0.6], CPThemeStateBordered | CPThemeStateDisabled], - + [@"min-size", CGSizeMake(32.0, 24.0)], [@"max-size", CGSizeMake(-1.0, 24.0)] ]; - + [self registerThemeValues:themeValues forView:button]; - + [button setTitle:@"Pop Up"]; [button addItemWithTitle:@"item"]; - + return button; } @@ -463,12 +471,12 @@ var themedButtonValues = nil; ["popup-bezel-disabled-right-pullsdown.png", 27.0, 24.0] ], PatternIsHorizontal), - - themeValues = + + themeValues = [ [@"bezel-color", color, CPPopUpButtonStatePullsDown | CPThemeStateBordered], [@"bezel-color", disabledColor, CPPopUpButtonStatePullsDown | CPThemeStateBordered | CPThemeStateDisabled], - + [@"content-inset", CGInsetMake(0, 27.0 + 5.0, 0, 5.0), CPThemeStateBordered], [@"font", [CPFont boldSystemFontOfSize:12.0]], [@"text-color", [CPColor colorWithCalibratedWhite:79.0 / 255.0 alpha:1.0]], @@ -476,28 +484,26 @@ var themedButtonValues = nil; [@"text-color", [CPColor colorWithCalibratedWhite:0.6 alpha:1.0], CPThemeStateBordered | CPThemeStateDisabled], [@"text-shadow-color", [CPColor colorWithCalibratedWhite:240.0 / 255.0 alpha:0.6], CPThemeStateBordered | CPThemeStateDisabled], - + [@"min-size", CGSizeMake(32.0, 24.0)], [@"max-size", CGSizeMake(-1.0, 24.0)] ]; [self registerThemeValues:themeValues forView:button]; - + [button setTitle:@"Pull Down"]; [button addItemWithTitle:@"item"]; return button; } -var themedVerticalScrollerValues = nil; - + (CPScroller)makeVerticalScroller { var scroller = [[CPScroller alloc] initWithFrame:CGRectMake(0.0, 0.0, 15.0, 170.0)]; - + [scroller setFloatValue:0.1]; [scroller setKnobProportion:0.5]; - + return scroller; } @@ -506,15 +512,15 @@ var themedVerticalScrollerValues = nil; var scroller = [self makeVerticalScroller], trackColor = PatternColor("scroller-vertical-track.png", 15.0, 1.0), disabledTrackColor = PatternColor("scroller-vertical-track-disabled.png", 15.0, 1.0), - + upArrowColor = PatternColor("scroller-up-arrow.png", 15.0, 24.0), highlightedUpArrowColor = PatternColor("scroller-up-arrow-highlighted.png", 15.0, 24.0), disabledUpArrowColor = PatternColor("scroller-up-arrow-disabled.png", 15.0, 24.0), - + downArrowColor = PatternColor("scroller-down-arrow.png", 15.0, 24.0), highlightedDownArrowColor = PatternColor("scroller-down-arrow-highlighted.png", 15.0, 24.0), disabledDownArrowColor = PatternColor("scroller-down-arrow-disabled.png", 15.0, 24.0), - + knobColor = PatternColor( [ ["scroller-vertical-knob-top.png", 15.0, 10.0], @@ -522,7 +528,7 @@ var themedVerticalScrollerValues = nil; ["scroller-vertical-knob-bottom.png", 15.0, 10.0] ], PatternIsVertical), - + disabledKnobColor = PatternColor( [ ["scroller-vertical-knob-disabled-top.png", 15.0, 10.0], @@ -530,44 +536,42 @@ var themedVerticalScrollerValues = nil; ["scroller-vertical-knob-disabled-bottom.png", 15.0, 10.0] ], PatternIsVertical); - + themedVerticalScrollerValues = [ [@"minimum-knob-length", 21.0, CPThemeStateVertical], [@"knob-inset", CGInsetMake(0.0, 0.0, 0.0, 0.0), CPThemeStateVertical], [@"track-inset", CGInsetMake(-10.0, 0.0, -10.0, 0.0), CPThemeStateVertical], - + [@"knob-color", knobColor, CPThemeStateVertical], [@"knob-color", disabledKnobColor, CPThemeStateVertical | CPThemeStateDisabled], - + [@"knob-slot-color", trackColor, CPThemeStateVertical], [@"knob-slot-color", disabledTrackColor, CPThemeStateVertical | CPThemeStateDisabled], - + [@"decrement-line-size", CGSizeMake(15.0, 24.0), CPThemeStateVertical], [@"decrement-line-color", upArrowColor, CPThemeStateVertical], [@"decrement-line-color", highlightedUpArrowColor, CPThemeStateVertical | CPThemeStateHighlighted], [@"decrement-line-color", disabledUpArrowColor, CPThemeStateVertical | CPThemeStateDisabled], - + [@"increment-line-size", CGSizeMake(15.0, 24.0), CPThemeStateVertical], [@"increment-line-color", downArrowColor, CPThemeStateVertical], [@"increment-line-color", highlightedDownArrowColor, CPThemeStateVertical | CPThemeStateHighlighted], [@"increment-line-color", disabledDownArrowColor, CPThemeStateVertical | CPThemeStateDisabled] ]; - + [self registerThemeValues:themedVerticalScrollerValues forView:scroller]; - + return scroller; } -var themedHorizontalScrollerValues = nil; - + (CPScroller)makeHorizontalScroller { var scroller = [[CPScroller alloc] initWithFrame:CGRectMake(0.0, 0.0, 170.0, 15.0)]; - + [scroller setFloatValue:0.1]; [scroller setKnobProportion:0.5]; - + return scroller; } @@ -576,15 +580,15 @@ var themedHorizontalScrollerValues = nil; var scroller = [self makeHorizontalScroller], trackColor = PatternColor("scroller-horizontal-track.png", 1.0, 15.0), disabledTrackColor = PatternColor("scroller-horizontal-track-disabled.png", 1.0, 15.0), - + leftArrowColor = PatternColor("scroller-left-arrow.png", 24.0, 15.0), highlightedLeftArrowColor = PatternColor("scroller-left-arrow-highlighted.png", 24.0, 15.0), disabledLeftArrowColor = PatternColor("scroller-left-arrow-disabled.png", 24.0, 15.0), - + rightArrowColor = PatternColor("scroller-right-arrow.png", 24.0, 15.0), highlightedRightArrowColor = PatternColor("scroller-right-arrow-highlighted.png", 24.0, 15.0), disabledRightArrowColor = PatternColor("scroller-right-arrow-disabled.png", 24.0, 15.0), - + knobColor = PatternColor( [ ["scroller-horizontal-knob-left.png", 10.0, 15.0], @@ -592,7 +596,7 @@ var themedHorizontalScrollerValues = nil; ["scroller-horizontal-knob-right.png", 10.0, 15.0] ], PatternIsHorizontal), - + disabledKnobColor = PatternColor( [ ["scroller-horizontal-knob-disabled-left.png", 10.0, 15.0], @@ -600,24 +604,24 @@ var themedHorizontalScrollerValues = nil; ["scroller-horizontal-knob-disabled-right.png", 10.0, 15.0] ], PatternIsHorizontal); - - themedHorizontalScrollerValues = + + themedHorizontalScrollerValues = [ [@"minimum-knob-length", 21.0], [@"knob-inset", CGInsetMake(0.0, 0.0, 0.0, 0.0)], [@"track-inset", CGInsetMake(0.0, -10.0, 0.0, -11.0)], - + [@"knob-color", knobColor], [@"knob-color", disabledKnobColor, CPThemeStateDisabled], - + [@"knob-slot-color", trackColor], [@"knob-slot-color", disabledTrackColor, CPThemeStateDisabled], - + [@"decrement-line-size", CGSizeMake(24.0, 15.0)], [@"decrement-line-color", leftArrowColor], [@"decrement-line-color", highlightedLeftArrowColor, CPThemeStateHighlighted], [@"decrement-line-color", disabledLeftArrowColor, CPThemeStateDisabled], - + [@"increment-line-size", CGSizeMake(24.0, 15.0)], [@"increment-line-color", rightArrowColor], [@"increment-line-color", highlightedRightArrowColor, CPThemeStateHighlighted], @@ -625,14 +629,14 @@ var themedHorizontalScrollerValues = nil; ]; [self registerThemeValues:themedHorizontalScrollerValues forView:scroller]; - + return scroller; } + (CPTextField)themedStandardTextField { var textfield = [[CPTextField alloc] initWithFrame:CGRectMake(0.0, 0.0, 60.0, 29.0)], - + bezelColor = PatternColor( [ ["textfield-bezel-square-0.png", 2.0, 3.0], @@ -658,21 +662,21 @@ var themedHorizontalScrollerValues = nil; ["textfield-bezel-square-focused-7.png", 1.0, 5.0], ["textfield-bezel-square-focused-8.png", 6.0, 5.0] ]), - + placeholderColor = [CPColor colorWithCalibratedRed:189.0 / 255.0 green:199.0 / 255.0 blue:211.0 / 255.0 alpha:1.0], - + themeValues = [ [@"bezel-color", bezelColor, CPThemeStateBezeled], [@"bezel-color", bezelFocusedColor, CPThemeStateBezeled | CPThemeStateEditing], [@"font", [CPFont systemFontOfSize:12.0], CPThemeStateBezeled], - + [@"content-inset", CGInsetMake(9.0, 7.0, 5.0, 8.0), CPThemeStateBezeled], [@"bezel-inset", CGInsetMake(4.0, 4.0, 3.0, 4.0), CPThemeStateBezeled], [@"bezel-inset", CGInsetMake(0.0, 0.0, 0.0, 0.0), CPThemeStateBezeled | CPThemeStateEditing], - + [@"text-color", placeholderColor, CPTextFieldStatePlaceholder], - + [@"line-break-mode", CPLineBreakByTruncatingTail, CPThemeStateTableDataView], [@"vertical-alignment", CPCenterVerticalTextAlignment, CPThemeStateTableDataView], [@"content-inset", CGInsetMake(0.0, 0.0, 0.0, 5.0), CPThemeStateTableDataView], @@ -683,18 +687,18 @@ var themedHorizontalScrollerValues = nil; ]; [self registerThemeValues:themeValues forView:textfield]; - + [textfield setBezeled:YES]; [textfield setPlaceholderString:"placeholder"]; [textfield setStringValue:""]; [textfield setEditable:YES]; - + return textfield; } + (CPTextField)themedRoundedTextField -{ +{ var textfield = [[CPTextField alloc] initWithFrame:CGRectMake(0.0, 0.0, 60.0, 30.0)], bezelColor = PatternColor( [ @@ -711,7 +715,7 @@ var themedHorizontalScrollerValues = nil; ["textfield-bezel-rounded-focused-right.png", 17.0, 30.0] ], PatternIsHorizontal), - + placeholderColor = [CPColor colorWithCalibratedRed:189.0 / 255.0 green:199.0 / 255.0 blue:211.0 / 255.0 alpha:1.0], themeValues = @@ -725,13 +729,13 @@ var themedHorizontalScrollerValues = nil; [@"bezel-inset", CGInsetMake(0.0, 0.0, 0.0, 0.0), CPTextFieldStateRounded | CPThemeStateBezeled | CPThemeStateEditing], [@"text-color", placeholderColor, CPTextFieldStateRounded | CPTextFieldStatePlaceholder], - + [@"min-size", CGSizeMake(0.0, 30.0), CPTextFieldStateRounded | CPThemeStateBezeled], [@"max-size", CGSizeMake(-1.0, 30.0), CPTextFieldStateRounded | CPThemeStateBezeled] ]; [self registerThemeValues:themeValues forView:textfield]; - + [textfield setBezeled:YES]; [textfield setBezelStyle:CPTextFieldRoundedBezel]; @@ -751,37 +755,37 @@ var themedHorizontalScrollerValues = nil; ["radio-bezel.png", 17.0, 17.0], nil, nil ], PatternIsHorizontal), - + bezelColorSelected = PatternColor( [ ["radio-bezel-selected.png", 17.0, 17.0], nil, nil ], PatternIsHorizontal), - + bezelColorSelectedHighlighted = PatternColor( [ ["radio-bezel-selected-highlighted.png", 17.0, 17.0], nil, nil ], PatternIsHorizontal), - + bezelColorSelectedDisabled = PatternColor( [ ["radio-bezel-selected-disabled.png", 17.0, 17.0], nil, nil ], PatternIsHorizontal), - + bezelColorDisabled = PatternColor( [ ["radio-bezel-disabled.png", 17.0, 17.0], nil, nil ], PatternIsHorizontal), - + bezelColorHighlighted = PatternColor( [ ["radio-bezel-highlighted.png", 17.0, 17.0], nil, nil ], PatternIsHorizontal), - + themeValues = [ [@"alignment", CPLeftTextAlignment, CPThemeStateBordered], @@ -793,12 +797,12 @@ var themedHorizontalScrollerValues = nil; [@"bezel-color", bezelColorHighlighted, CPThemeStateBordered | CPThemeStateHighlighted], [@"bezel-color", bezelColorDisabled, CPThemeStateBordered | CPThemeStateDisabled], [@"bezel-color", bezelColorSelectedDisabled, CPThemeStateBordered | CPThemeStateSelected | CPThemeStateDisabled], - + [@"text-color", [CPColor colorWithCalibratedWhite:79.0 / 255.0 alpha:1.0], CPThemeStateDisabled], - + [@"min-size", CGSizeMake(0.0, 17.0)] ]; - + [self registerThemeValues:themeValues forView:button]; [button setTitle:@"Hello Friend!"]; @@ -809,43 +813,43 @@ var themedHorizontalScrollerValues = nil; + (CPRadioButton)themedCheckBoxButton { var button = [[CPCheckBox alloc] initWithFrame:CGRectMake(0.0, 0.0, 120.0, 17.0)], - + bezelColor = PatternColor( [ ["check-box-bezel.png", 15.0, 16.0], nil, nil ], PatternIsHorizontal), - + bezelColorSelected = PatternColor( [ ["check-box-bezel-selected.png", 15.0, 16.0], nil, nil ], PatternIsHorizontal), - + bezelColorSelectedHighlighted = PatternColor( [ ["check-box-bezel-selected-highlighted.png", 15.0, 16.0], nil, nil ], PatternIsHorizontal), - + bezelColorSelectedDisabled = PatternColor( [ ["check-box-bezel-selected-disabled.png", 15.0, 16.0], nil, nil ], PatternIsHorizontal), - + bezelColorDisabled = PatternColor( [ ["check-box-bezel-disabled.png", 15.0, 16.0], nil, nil ], PatternIsHorizontal), - + bezelColorHighlighted = PatternColor( [ ["check-box-bezel-highlighted.png", 15.0, 16.0], nil, nil ], PatternIsHorizontal), - + themeValues = [ [@"alignment", CPLeftTextAlignment, CPThemeStateBordered], @@ -857,14 +861,14 @@ var themedHorizontalScrollerValues = nil; [@"bezel-color", bezelColorHighlighted, CPThemeStateBordered | CPThemeStateHighlighted], [@"bezel-color", bezelColorDisabled, CPThemeStateBordered | CPThemeStateDisabled], [@"bezel-color", bezelColorSelectedDisabled, CPThemeStateBordered | CPThemeStateSelected | CPThemeStateDisabled], - + [@"text-color", [CPColor colorWithCalibratedWhite:79.0 / 255.0 alpha:1.0], CPThemeStateDisabled], - + [@"min-size", CGSizeMake(0.0, 17.0)] ]; [self registerThemeValues:themeValues forView:button]; - + [button setTitle:@"Another option"]; return button; @@ -882,20 +886,20 @@ var themedHorizontalScrollerValues = nil; ["check-box-bezel-mixed-highlighted.png", 15.0, 16.0], nil, nil ], PatternIsHorizontal), - + mixedDisabledColor = PatternColor( [ ["check-box-bezel-mixed-disabled.png", 15.0, 16.0], nil, nil ], PatternIsHorizontal), - + mixedColor = PatternColor( [ ["check-box-bezel-mixed.png", 15.0, 16.0], nil, nil ], PatternIsHorizontal), - - themeValues = + + themeValues = [ [@"bezel-color", mixedColor, CPButtonStateMixed | CPThemeStateBordered], [@"bezel-color", mixedSelectedColor, CPButtonStateMixed | CPThemeStateBordered | CPThemeStateSelected], @@ -903,19 +907,17 @@ var themedHorizontalScrollerValues = nil; ]; [self registerThemeValues:themeValues forView:button]; - + return button; } -var themedSegmentedControlValues = nil; - + (CPSegmentedControl)makeSegmentedControl { var segmentedControl = [[CPSegmentedControl alloc] initWithFrame:CGRectMake(0.0, 0.0, 0.0, 24.0)]; - + [segmentedControl setTrackingMode:CPSegmentSwitchTrackingSelectAny]; [segmentedControl setSegmentCount:3]; - + [segmentedControl setWidth:40.0 forSegment:0]; [segmentedControl setLabel:@"foo" forSegment:0]; [segmentedControl setTag:1 forSegment:0]; @@ -927,14 +929,14 @@ var themedSegmentedControlValues = nil; [segmentedControl setWidth:35.0 forSegment:2]; [segmentedControl setLabel:@"1" forSegment:2]; [segmentedControl setTag:3 forSegment:2]; - + return segmentedControl; }; + (CPSegmentedControl)themedSegmentedControl { var segmentedControl = [self makeSegmentedControl], - + centerBezelColor = PatternColor("segmented-control-bezel-center.png", 1.0, 24.0), dividerBezelColor = PatternColor("segmented-control-bezel-divider.png", 1.0, 24.0), centerHighlightedBezelColor = PatternColor("segmented-control-bezel-highlighted-center.png", 1.0, 24.0), @@ -957,7 +959,7 @@ var themedSegmentedControlValues = nil; pushedHighlightedCenterBezelColor = PatternColor("segmented-control-bezel-pushed-highlighted-center.png", 1.0, 24.0), pushedHighlightedLeftBezelColor = PatternColor("segmented-control-bezel-pushed-highlighted-left.png", 4.0, 24.0), pushedHighlightedRightBezelColor = PatternColor("segmented-control-bezel-pushed-highlighted-right.png", 4.0, 24.0); - + themedSegmentedControlValues = [ [@"center-segment-bezel-color", centerBezelColor, CPThemeStateNormal], @@ -966,29 +968,29 @@ var themedSegmentedControlValues = nil; [@"center-segment-bezel-color", centerHighlightedBezelColor, CPThemeStateSelected], [@"center-segment-bezel-color", pushedCenterBezelColor, CPThemeStateHighlighted], [@"center-segment-bezel-color", pushedHighlightedCenterBezelColor, CPThemeStateHighlighted | CPThemeStateSelected], - + [@"divider-bezel-color", dividerBezelColor, CPThemeStateNormal], [@"divider-bezel-color", inactiveDividerBezelColor, CPThemeStateDisabled], [@"divider-bezel-color", inactiveHighlightedDividerBezelColor, CPThemeStateSelected | CPThemeStateDisabled], [@"divider-bezel-color", dividerHighlightedBezelColor, CPThemeStateSelected], - + [@"left-segment-bezel-color", leftBezelColor, CPThemeStateNormal], [@"left-segment-bezel-color", inactiveLeftBezelColor, CPThemeStateDisabled], [@"left-segment-bezel-color", inactiveHighlightedLeftBezelColor, CPThemeStateSelected | CPThemeStateDisabled], [@"left-segment-bezel-color", leftHighlightedBezelColor, CPThemeStateSelected], [@"left-segment-bezel-color", pushedLeftBezelColor, CPThemeStateHighlighted], [@"left-segment-bezel-color", pushedHighlightedLeftBezelColor, CPThemeStateHighlighted | CPThemeStateSelected], - + [@"right-segment-bezel-color", rightBezelColor, CPThemeStateNormal], [@"right-segment-bezel-color", inactiveRightBezelColor, CPThemeStateDisabled], [@"right-segment-bezel-color", inactiveHighlightedRightBezelColor, CPThemeStateSelected | CPThemeStateDisabled], [@"right-segment-bezel-color", rightHighlightedBezelColor, CPThemeStateSelected], [@"right-segment-bezel-color", pushedRightBezelColor, CPThemeStateHighlighted], [@"right-segment-bezel-color", pushedHighlightedRightBezelColor, CPThemeStateHighlighted | CPThemeStateSelected], - + [@"content-inset", CGInsetMake(0.0, 4.0, 0.0, 4.0), CPThemeStateNormal], [@"bezel-inset", CGInsetMake(0.0, 0.0, 0.0, 0.0), CPThemeStateNormal], - + [@"font", [CPFont boldSystemFontOfSize:12.0]], [@"text-color", [CPColor colorWithCalibratedWhite:79.0 / 255.0 alpha:1.0]], [@"text-color", [CPColor colorWithCalibratedWhite:0.6 alpha:1.0], CPThemeStateDisabled], @@ -1002,12 +1004,10 @@ var themedSegmentedControlValues = nil; ]; [self registerThemeValues:themedSegmentedControlValues forView:segmentedControl]; - + return segmentedControl; } -var themedHorizontalSliderValues = nil; - + (CPSlider)makeHorizontalSlider { return [[CPSlider alloc] initWithFrame:CGRectMake(0.0, 0.0, 50.0, 24.0)]; @@ -1016,7 +1016,7 @@ var themedHorizontalSliderValues = nil; + (CPSlider)themedHorizontalSlider { var slider = [self makeHorizontalSlider], - + trackColor = PatternColor( [ ["horizontal-track-left.png", 4.0, 5.0], @@ -1024,7 +1024,7 @@ var themedHorizontalSliderValues = nil; ["horizontal-track-right.png", 4.0, 5.0] ], PatternIsHorizontal), - + trackDisabledColor = PatternColor( [ ["horizontal-track-disabled-left.png", 4.0, 5.0], @@ -1032,30 +1032,28 @@ var themedHorizontalSliderValues = nil; ["horizontal-track-disabled-right.png", 4.0, 5.0] ], PatternIsHorizontal), - + knobColor = PatternColor("knob.png", 23.0, 24.0), knobHighlightedColor = PatternColor("knob-highlighted.png", 23.0, 24.0), knobDisabledColor = PatternColor("knob-disabled.png", 23.0, 24.0); - - themedHorizontalSliderValues = + + themedHorizontalSliderValues = [ [@"track-width", 5.0], [@"track-color", trackColor], [@"track-color", trackDisabledColor, CPThemeStateDisabled], - + [@"knob-size", CGSizeMake(23.0, 24.0)], [@"knob-color", knobColor], [@"knob-color", knobHighlightedColor, CPThemeStateHighlighted], [@"knob-color", knobDisabledColor, CPThemeStateDisabled] ]; - + [self registerThemeValues:themedHorizontalSliderValues forView:slider]; - + return slider; } -var themedVerticalSliderValues = nil; - + (CPSlider)makeVerticalSlider { return [[CPSlider alloc] initWithFrame:CGRectMake(0.0, 0.0, 24.0, 50.0)]; @@ -1064,7 +1062,7 @@ var themedVerticalSliderValues = nil; + (CPSlider)themedVerticalSlider { var slider = [self makeVerticalSlider], - + trackColor = PatternColor( [ ["vertical-track-top.png", 5.0, 6.0], @@ -1072,7 +1070,7 @@ var themedVerticalSliderValues = nil; ["vertical-track-bottom.png", 5.0, 4.0] ], PatternIsVertical), - + trackDisabledColor = PatternColor( [ ["vertical-track-disabled-top.png", 5.0, 6.0], @@ -1080,49 +1078,47 @@ var themedVerticalSliderValues = nil; ["vertical-track-disabled-bottom.png", 5.0, 4.0] ], PatternIsVertical), - + knobColor = PatternColor("knob.png", 23.0, 24.0), knobHighlightedColor = PatternColor("knob-highlighted.png", 23.0, 24.0), knobDisabledColor = PatternColor("knob-disabled.png", 23.0, 24.0); - - themedVerticalSliderValues = + + themedVerticalSliderValues = [ [@"track-width", 5.0], [@"track-color", trackColor, CPThemeStateVertical], [@"track-color", trackDisabledColor, CPThemeStateDisabled], - + [@"knob-size", CGSizeMake(23.0, 24.0)], [@"knob-color", knobColor], [@"knob-color", knobHighlightedColor, CPThemeStateHighlighted], [@"knob-color", knobDisabledColor, CPThemeStateDisabled] ] - + [self registerThemeValues:themedVerticalSliderValues forView:slider]; - + return slider; } -var themedCircularSliderValues = nil; - + (CPSlider)makeCircularSlider { var slider = [[CPSlider alloc] initWithFrame:CGRectMake(0.0, 0.0, 34.0, 34.0)]; - + [slider setSliderType:CPCircularSlider]; - + return slider; } + (CPSlider)themedCircularSlider { var slider = [self makeCircularSlider], - + trackColor = PatternColor("slider-circular-bezel.png", 34.0, 34.0), trackDisabledColor = PatternColor("slider-circular-disabled-bezel.png", 34.0, 34.0) knobColor = PatternColor("slider-circular-knob.png", 5.0, 5.0), knobDisabledColor = PatternColor("slider-circular-disabled-knob.png", 5.0, 5.0), knobHighlightedColor = knobColor; - + themedCircularSliderValues = [ [@"track-color", trackColor, CPThemeStateCircular], @@ -1135,31 +1131,29 @@ var themedCircularSliderValues = nil; ]; [self registerThemeValues:themedCircularSliderValues forView:slider]; - + return slider; } -var themedButtonBarValues = nil; - + (CPButtonBar)makeButtonBar { var buttonBar = [[CPButtonBar alloc] initWithFrame:CGRectMake(0.0, 0.0, 147.0, 26.0)]; - + [buttonBar setHasResizeControl:YES]; - + var popup = [CPButtonBar actionPopupButton]; [popup addItemWithTitle:"Item 1"]; [popup addItemWithTitle:"Item 2"]; [buttonBar setButtons:[[CPButtonBar plusButton], [CPButtonBar minusButton], popup]]; - + return buttonBar; } + (CPButtonBar)themedButtonBar { var buttonBar = [self makeButtonBar], - + color = PatternColor("buttonbar-bezel.png", 1.0, 26.0), resizeColor = PatternColor("buttonbar-resize-control.png", 5.0, 10.0), buttonBezelColor = PatternColor( @@ -1189,28 +1183,28 @@ var themedButtonBarValues = nil; themedButtonBarValues = [ [@"bezel-color", color], - + [@"resize-control-size", CGSizeMake(5.0, 10.0)], [@"resize-control-inset", CGInsetMake(9.0, 4.0, 7.0, 4.0)], [@"resize-control-color", resizeColor], - + [@"button-bezel-color", buttonBezelColor], [@"button-bezel-color", buttonBezelHighlightedColor, CPThemeStateHighlighted], [@"button-bezel-color", buttonBezelDisabledColor, CPThemeStateDisabled], [@"button-text-color", [CPColor blackColor]] ]; - + [self registerThemeValues:themedButtonBarValues forView:buttonBar]; - + return buttonBar; } + (_CPTableColumnHeaderView)makeColumnHeader { var header = [[_CPTableColumnHeaderView alloc] initWithFrame:CGRectMake(0.0, 0.0, 100.0, 24.0)]; - + [header setStringValue:@"Table Header"]; - + return header; } @@ -1221,25 +1215,25 @@ var themedButtonBarValues = nil; highlighted = PatternColor("tableview-headerview-highlighted.png", 1.0, 23.0), pressed = PatternColor("tableview-headerview-pressed.png", 1.0, 23.0), normal = PatternColor("tableview-headerview.png", 1.0, 23.0), - + themedColumnHeaderValues = [ [@"background-color", normal], - + [@"text-inset", CGInsetMake(0, 5, 0, 5)], [@"text-color", [CPColor colorWithCalibratedWhite:51.0 / 255.0 alpha:1.0]], [@"text-font", [CPFont boldSystemFontOfSize:12.0]], [@"text-shadow-color", [CPColor whiteColor]], [@"text-shadow-offset", CGSizeMake(0.0, 1.0)], [@"text-alignment", CPLeftTextAlignment], - + [@"background-color", pressed, CPThemeStateHighlighted], [@"background-color", highlighted, CPThemeStateSelected], [@"background-color", highlightedPressed, CPThemeStateHighlighted | CPThemeStateSelected] ]; [self registerThemeValues:themedColumnHeaderValues forView:header]; - + return header; } @@ -1269,18 +1263,18 @@ var themedButtonBarValues = nil; // This is a bit more complicated than the rest because we actually set theme values for several different (table related) controls in this method var tableview = [[CPTableView alloc] initWithFrame:CGRectMake(0.0, 0.0, 150.0, 150.0)], - + sortImage = PatternColor("tableview-headerview-ascending.png", 9.0, 8.0), sortImageReversed = PatternColor("tableview-headerview-descending.png", 9.0, 8.0), alternatingRowColors = [[CPColor whiteColor], [CPColor colorWithRed:245.0 / 255.0 green:249.0 / 255.0 blue:252.0 / 255.0 alpha:1.0]], gridColor = [CPColor colorWithHexString:@"dce0e2"], selectionColor = [CPColor colorWithHexString:@"5f83b9"], - sourceListSelectionColor = [CPDictionary dictionaryWithObjects: [CGGradientCreateWithColorComponents(CGColorSpaceCreateDeviceRGB(), [89.0/255.0, 153.0/255.0, 209.0/255.0,1.0, 33.0/255.0, 94.0/255.0, 208.0/255.0,1.0], [0,1], 2), + sourceListSelectionColor = [CPDictionary dictionaryWithObjects: [CGGradientCreateWithColorComponents(CGColorSpaceCreateDeviceRGB(), [89.0/255.0, 153.0/255.0, 209.0/255.0,1.0, 33.0/255.0, 94.0/255.0, 208.0/255.0,1.0], [0,1], 2), [CPColor colorWithCalibratedRed:(61.0/255.0) green:(123.0/255.0) blue:(218.0/255.0) alpha:1.0], [CPColor colorWithCalibratedRed:(31.0/255.0) green:(92.0/255.0) blue:(207.0/255.0) alpha:1.0] ] forKeys: [CPSourceListGradient, CPSourceListTopLineColor, CPSourceListBottomLineColor]], - + themedTableViewValues = [ [@"alternating-row-colors", alternatingRowColors], @@ -1308,6 +1302,39 @@ var themedButtonBarValues = nil; return view; } ++ (CPAlert)themedAlert +{ + var alert = [CPAlert new], + size = CGSizeMake(400.0, 110.0), + inset = CGInsetMake(15, 15, 15, 80), + imageOffset = CGPointMake(15, 18), + messageFont = [CPFont boldSystemFontOfSize:13.0], + informativeFont = [CPFont systemFontOfSize:12.0], + informationIcon = PatternImage("alert-info.png", 53.0, 46.0), + warningIcon = PatternImage("alert-warning.png", 53.0, 46.0), + errorIcon = PatternImage("alert-error.png", 53.0, 46.0); + + themedAlertValues = + [ + [@"size", size], + [@"content-inset", inset], + [@"message-text-alignment", CPJustifiedTextAlignment], + [@"message-text-color", [CPColor blackColor]], + [@"message-text-font", messageFont], + [@"informative-text-alignment", CPJustifiedTextAlignment], + [@"informative-text-color", [CPColor blackColor]], + [@"informative-text-font", informativeFont], + [@"image-offset", imageOffset], + [@"information-image", informationIcon], + [@"warning-image", warningIcon], + [@"error-image", errorIcon] + ]; + + [self registerThemeValues:themedAlertValues forView:alert]; + + return alert; +} + @end @implementation AristoHUDThemeDescriptor : BKThemeDescriptor @@ -1333,29 +1360,28 @@ var themedButtonBarValues = nil; [@"text-shadow-color", [CPColor blackColor], CPThemeStateDisabled], [@"text-shadow-offset", CGSizeMake(-1.0, -1.0)] ]; - + if (themeValues) [overrides addObjectsFromArray:themeValues]; - + return overrides; } + (CPPopUpButton)themedSegmentedControl { var segmentedControl = [AristoThemeDescriptor makeSegmentedControl]; - + [self registerThemeValues:[self defaultThemeOverridesAddedTo:nil] forView:segmentedControl inherit:themedSegmentedControlValues]; return segmentedControl; } -var + (CPButton)button { var button = [AristoThemeDescriptor makeButton]; - [self registerThemeValues:[self defaultThemeOverridesAddedTo:nil] forView:button inherit:themedButtonValues];; - + [self registerThemeValues:[self defaultThemeOverridesAddedTo:nil] forView:button inherit:themedButtonValues]; + return button; } @@ -1385,9 +1411,9 @@ var [ [@"knob-color", nil, CPThemeStateVertical | CPThemeStateDisabled] ]; - - [self registerThemeValues:[self defaultThemeOverridesAddedTo:overrides] forView:scroller inherit:themedVerticalScrollerValues];; - + + [self registerThemeValues:[self defaultThemeOverridesAddedTo:overrides] forView:scroller inherit:themedVerticalScrollerValues]; + return scroller; } @@ -1398,8 +1424,8 @@ var [ [@"knob-color", nil, CPThemeStateDisabled] ]; - - [self registerThemeValues:[self defaultThemeOverridesAddedTo:overrides] forView:scroller inherit:themedHorizontalScrollerValues];; + + [self registerThemeValues:[self defaultThemeOverridesAddedTo:overrides] forView:scroller inherit:themedHorizontalScrollerValues]; return scroller; } @@ -1407,28 +1433,43 @@ var + (CPSlider)themedHorizontalSlider { var slider = [AristoThemeDescriptor makeHorizontalSlider]; - - [self registerThemeValues:[self defaultThemeOverridesAddedTo:nil] forView:slider inherit:themedHorizontalSliderValues];; - + + [self registerThemeValues:[self defaultThemeOverridesAddedTo:nil] forView:slider inherit:themedHorizontalSliderValues]; + return slider; } + (CPSlider)themedVerticalSlider { var slider = [AristoThemeDescriptor makeVerticalSlider]; - - [self registerThemeValues:[self defaultThemeOverridesAddedTo:nil] forView:slider inherit:themedVerticalSliderValues];; - + + [self registerThemeValues:[self defaultThemeOverridesAddedTo:nil] forView:slider inherit:themedVerticalSliderValues]; + return slider; } + (CPSlider)themedCircularSlider { var slider = [AristoThemeDescriptor makeCircularSlider]; - - [self registerThemeValues:[self defaultThemeOverridesAddedTo:nil] forView:slider inherit:themedCircularSliderValues];; - + + [self registerThemeValues:[self defaultThemeOverridesAddedTo:nil] forView:slider inherit:themedCircularSliderValues]; + return slider; } ++ (CPAlert)themedAlert +{ + var alert = [CPAlert new], + + hudSpecificValues = + [ + [@"message-text-color", [CPColor whiteColor]], + [@"informative-text-color", [CPColor whiteColor]], + ]; + + [self registerThemeValues:hudSpecificValues forView:alert inherit:themedAlertValues]; + + return alert; +} + @end diff --git a/AppKit/Themes/BlendKit/BKThemeDescriptor.j b/AppKit/Themes/BlendKit/BKThemeDescriptor.j index 6d0f49cb3..4841fb21e 100644 --- a/AppKit/Themes/BlendKit/BKThemeDescriptor.j +++ b/AppKit/Themes/BlendKit/BKThemeDescriptor.j @@ -177,14 +177,14 @@ var ItemSizes = { }, } + (void)registerThemeValues:(CPArray)themeValues forView:(CPView)aView -{ +{ for (var i = 0; i < themeValues.length; ++i) { var attributeValueState = themeValues[i], attribute = attributeValueState[0], value = attributeValueState[1], state = attributeValueState[2]; - + if (state) [aView setValue:value forThemeAttribute:attribute inState:state]; else @@ -218,9 +218,9 @@ var ItemSizes = { }, value = attributeValueState[1], state = attributeValueState[2], pattern = nil; - - if (typeof(value) === "object" && - value.hasOwnProperty("isa") && + + if (typeof(value) === "object" && + value.hasOwnProperty("isa") && [value isKindOfClass:CPColor] && (pattern = [value patternImage])) { @@ -228,16 +228,16 @@ var ItemSizes = { }, { var slices = [pattern imageSlices], newSlices = []; - + for (var sliceIndex = 0; sliceIndex < slices.length; ++sliceIndex) { var slice = slices[sliceIndex], filename = themePath + [[slice filename] lastPathComponent], size = [slice size]; - + newSlices.push([filename, size.width, size.height]); } - + if ([pattern isThreePartImage]) value = PatternColor(newSlices, [pattern isVertical]); else @@ -247,11 +247,11 @@ var ItemSizes = { }, { var filename = themePath + [[pattern filename] lastPathComponent], size = [pattern size]; - + value = PatternColor(filename, size.width, size.height); } } - + if (state) [aView setValue:value forThemeAttribute:attribute inState:state]; else @@ -259,7 +259,7 @@ var ItemSizes = { }, } } } - + if (themeValues) [self registerThemeValues:themeValues forView:aView]; } @@ -268,7 +268,7 @@ var ItemSizes = { }, function BKLabelFromIdentifier(anIdentifier) { - var string = anIdentifier.substr("themed".length); + var string = anIdentifier.substr("themed".length), index = 0, count = string.length, label = "", @@ -281,7 +281,7 @@ function BKLabelFromIdentifier(anIdentifier) isCapital = /^[A-Z]/.test(character); if (isCapital) - { + { if (!isLeadingCapital) { if (lastCapital === null) @@ -311,21 +311,21 @@ function BKLabelFromIdentifier(anIdentifier) PatternIsVertical = YES, PatternIsHorizontal = NO; - + /* To create a simple color with a pattern image: PatternColor(name, width, height) - + To create a color with a three part pattern image: PatternColor(slices, orientation) - + where slices is an array of three [name, width, height] arrays, and orientation is PatternIsVertical or PatternIsHorizontal. - + To create a color with a nine part pattern image: PatternColor(slices); - - where slices is an array of nine [name, width, height] arrays. + + where slices is an array of nine [name, width, height] arrays. */ function PatternColor() { @@ -333,14 +333,14 @@ function PatternColor() { var slices = arguments[0], imageSlices = []; - + for (var i = 0; i < slices.length; ++i) { var slice = slices[i]; - + imageSlices.push(slice ? [_CPCibCustomResource imageResourceWithName:slice[0] size:CGSizeMake(slice[1], slice[2])] : nil); } - + if (arguments.length == 2) return [CPColor colorWithPatternImage:[[CPThreePartImage alloc] initWithImageSlices:imageSlices isVertical:arguments[1]]]; else @@ -348,10 +348,18 @@ function PatternColor() } else if (arguments.length == 3) { - return [CPColor colorWithPatternImage:[_CPCibCustomResource imageResourceWithName:arguments[0] size:CGSizeMake(arguments[1], arguments[2])]]; + return [CPColor colorWithPatternImage:PatternImage(arguments[0], arguments[1], arguments[2])]; } else { return nil; } } + +/* + Like the 3 argument PatternColor, but return an image instead of a color. +*/ +function PatternImage(name, width, height) +{ + return [_CPCibCustomResource imageResourceWithName:name size:CGSizeMake(width, height)]; +} diff --git a/Foundation/CPIndexSet.j b/Foundation/CPIndexSet.j index bd3be224e..157cad253 100644 --- a/Foundation/CPIndexSet.j +++ b/Foundation/CPIndexSet.j @@ -130,6 +130,17 @@ return self; } +- (BOOL)isEqual:(id)anObject +{ + if (self === anObject) + return YES; + + if (!anObject || ![anObject isKindOfClass:[CPIndexSet class]]) + return NO; + + return [self isEqualToIndexSet:anObject]; +} + // Querying an Index Set /*! Compares the receiver with the provided index set. diff --git a/Jakefile b/Jakefile index 0fbd1a3b7..9d7cdd150 100644 --- a/Jakefile +++ b/Jakefile @@ -90,41 +90,28 @@ task ("docs", ["documentation"]); task ("documentation", function() { - var doxygen = null, - DoxygenAppPath = "/Applications/Doxygen.app/Contents/Resources"; - + // try to find a doxygen executable in the PATH; + var doxygen = executableExists("doxygen"); + // If the Doxygen application is installed on Mac OS X, use that - if (executableExists("mdfind")) + if (!doxygen && executableExists("mdfind")) { - OS.system("mdfind \"kMDItemContentType == 'com.apple.application-bundle' && kMDItemCFBundleIdentifier == 'org.doxygen'\" > doxygen_app"); - - if (FILE.exists("doxygen_app")) + var p = OS.popen(["mdfind", "kMDItemContentType == 'com.apple.application-bundle' && kMDItemCFBundleIdentifier == 'org.doxygen'"]); + if (p.wait() === 0) { - doxygen = FILE.join(UTIL.trimEnd(FILE.read("doxygen_app")), "Contents/Resources/doxygen"); - FILE.remove("doxygen_app"); + var doxygenApps = p.stdout.read().split("\n"); + if (doxygenApps[0]) + doxygen = FILE.join(doxygenApps[0], "Contents/Resources/doxygen"); } } - else + + if (doxygen && FILE.exists(doxygen)) { - SYSTEM.env["PATH"].split(':').some(function(/*String*/ aPath) - { - var path = FILE.join(aPath, "doxygen"); - - if (FILE.exists(path)) - { - doxygen = path; - return true; - } - }); - } - - if (doxygen) - { - print("Using " + doxygen + " for doxygen binary."); - + stream.print("\0green(Using " + doxygen + " for doxygen binary.\0)"); + var documentationDir = FILE.join("Tools", "Documentation"); - - if (OS.system(["ruby", FILE.join(documentationDir, "make_headers.sh")])) + + if (OS.system(["ruby", FILE.join(documentationDir, "make_headers")])) OS.exit(1); //rake abort if ($? != 0) if (!OS.system([doxygen, FILE.join(documentationDir, "Cappuccino.doxygen")])) @@ -133,11 +120,11 @@ task ("documentation", function() mv("debug.txt", FILE.join("Documentation", "debug.txt")); mv("Documentation", $DOCUMENTATION_BUILD); } - + OS.system(["ruby", FILE.join(documentationDir, "cleanup_headers")]); } else - print("doxygen not installed, skipping documentation generation."); + stream.print("\0yellow(Doxygen not installed, skipping documentation generation.\0)"); }); // Downloads @@ -225,17 +212,17 @@ task ("demos", function() return this._plist.valueForKey(key); return this._plist; } - + Demo.prototype.name = function() { return this.plist("CPBundleName"); } - + Demo.prototype.path = function() { return this._path; } - + Demo.prototype.excluded = function() { return !!this.plist("CPDemoExcluded"); @@ -245,7 +232,7 @@ task ("demos", function() { return this.name(); } - + FILE.glob(FILE.join(demosDir, "demos", "**/Info.plist")).map(function(demoPath){ return new Demo(FILE.dirname(demoPath)) }).filter(function(demo){ @@ -259,7 +246,7 @@ task ("demos", function() var outputPath = demo.name().replace(/\s/g, "-")+".zip"; OS.system("cd "+OS.enquote(FILE.dirname(demo.path()))+" && zip -ry -8 "+OS.enquote(outputPath)+" "+OS.enquote(FILE.basename(demo.path()))); - // remove the frameworks + // remove the frameworks rm_rf(FILE.join(demo.path(), "Frameworks")); }); }); @@ -343,7 +330,7 @@ function pushPackage(path, remote, branch) cmd.push(["git", "tag", "rev-"+pkg["cappuccino-revision"].slice(0,6)]); OS.system(buildCmd(cmd)); - + if (OS.system(buildCmd([ ["cd", packagePath], ["git", "push", "--tags", "origin", "HEAD:"+branch] diff --git a/Objective-J/Runtime.js b/Objective-J/Runtime.js index 7c4566b4e..551113132 100644 --- a/Objective-J/Runtime.js +++ b/Objective-J/Runtime.js @@ -53,20 +53,20 @@ DISPLAY_NAME(objj_method); GLOBAL(objj_class) = function(displayName) { this.isa = NULL; - + this.super_class = NULL; this.sub_classes = []; - + this.name = NULL; this.info = 0; this.ivars = []; - + this.method_list = []; this.method_hash = {}; - + this.method_store = function() { }; this.method_dtable = this.method_store.prototype; - + #if DEBUG // naming the allocator allows the WebKit heap snapshot tool to display object class names correctly // HACK: displayName property is not respected so we must eval a function to name it @@ -94,7 +94,7 @@ GLOBAL(class_getName) = function(/*Class*/ aClass) { if (aClass == Nil) return ""; - + return aClass.name; } @@ -104,7 +104,7 @@ GLOBAL(class_isMetaClass) = function(/*Class*/ aClass) { if (!aClass) return NO; - + return ISMETA(aClass); } @@ -114,7 +114,7 @@ GLOBAL(class_getSuperclass) = function(/*Class*/ aClass) { if (aClass == Nil) return Nil; - + return aClass.super_class; } @@ -132,13 +132,13 @@ DISPLAY_NAME(class_setSuperclass); GLOBAL(class_addIvar) = function(/*Class*/ aClass, /*String*/ aName, /*String*/ aType) { var thePrototype = aClass.allocator.prototype; - + if (typeof thePrototype[aName] != "undefined") return NO; - - aClass.ivars.push(new objj_ivar(aName, aType)); + + aClass.ivars.push(new objj_ivar(aName, aType)); thePrototype[aName] = NULL; - + return YES; } @@ -149,7 +149,7 @@ GLOBAL(class_addIvars) = function(/*Class*/ aClass, /*Array*/ivars) var index = 0, count = ivars.length, thePrototype = aClass.allocator.prototype; - + for (; index < count; ++index) { var ivar = ivars[index], @@ -157,7 +157,7 @@ GLOBAL(class_addIvars) = function(/*Class*/ aClass, /*Array*/ivars) if (typeof thePrototype[name] === "undefined") { - aClass.ivars.push(ivar); + aClass.ivars.push(ivar); thePrototype[name] = NULL; } } @@ -180,10 +180,10 @@ GLOBAL(class_addMethod) = function(/*Class*/ aClass, /*SEL*/ aName, /*IMP*/ anIm { if (aClass.method_hash[aName]) return NO; - + var method = new objj_method(aName, anImplementation, types); - - aClass.method_list.push(method); + + aClass.method_list.push(method); aClass.method_dtable[aName] = method; #if DEBUG @@ -205,18 +205,18 @@ GLOBAL(class_addMethods) = function(/*Class*/ aClass, /*Array*/ methods) { var index = 0, count = methods.length, - + method_list = aClass.method_list, method_dtable = aClass.method_dtable; - + for (; index < count; ++index) { var method = methods[index]; - + if (aClass.method_hash[method.name]) continue; - - method_list.push(method); + + method_list.push(method); method_dtable[method.name] = method; #if DEBUG @@ -236,9 +236,9 @@ GLOBAL(class_getInstanceMethod) = function(/*Class*/ aClass, /*SEL*/ aSelector) { if (!aClass || !aSelector) return NULL; - + var method = aClass.method_dtable[aSelector]; - + return method ? method : NULL; } @@ -250,7 +250,7 @@ GLOBAL(class_getClassMethod) = function(/*Class*/ aClass, /*SEL*/ aSelector) return NULL; var method = GETMETA(aClass).method_dtable[aSelector]; - + return method ? method : NULL; } @@ -284,17 +284,17 @@ DISPLAY_NAME(class_replaceMethod); var _class_initialize = function(/*Class*/ aClass) { var meta = GETMETA(aClass); - + if (GETINFO(aClass, CLS_META)) aClass = objj_getClass(aClass.name); - + if (aClass.super_class && !ISINITIALIZED(aClass.super_class)) _class_initialize(aClass.super_class); - + if (!GETINFO(meta, CLS_INITIALIZED) && !GETINFO(meta, CLS_INITIALIZING)) { SETINFO(meta, CLS_INITIALIZING); - + objj_msgSend(aClass, "initialize"); CHANGEINFO(meta, CLS_INITIALIZED, CLS_INITIALIZING); @@ -321,7 +321,7 @@ var _objj_forward = new objj_method("forward", function(self, _cmd) GLOBAL(class_getMethodImplementation) = function(/*Class*/ aClass, /*SEL*/ aSelector) { CLASS_GET_METHOD_IMPLEMENTATION(var implementation, aClass, aSelector); - + return implementation; } @@ -335,25 +335,25 @@ GLOBAL(objj_allocateClassPair) = function(/*Class*/ superclass, /*String*/ aName var classObject = new objj_class(aName), metaClassObject = new objj_class(aName), rootClassObject = classObject; - + // If we don't have a superclass, we are the root class. if (superclass) { rootClassObject = superclass; - + while (rootClassObject.superclass) rootClassObject = rootClassObject.superclass; - + // Give our current allocator all the instance variables of our super class' allocator. classObject.allocator.prototype = new superclass.allocator; - + // "Inheret" parent methods. classObject.method_store.prototype = new superclass.method_store; classObject.method_dtable = classObject.method_store.prototype; - + metaClassObject.method_store.prototype = new superclass.isa.method_store; metaClassObject.method_dtable = metaClassObject.method_store.prototype; - + // Set up the actual class hierarchy. classObject.super_class = superclass; metaClassObject.super_class = superclass.isa; @@ -406,7 +406,7 @@ GLOBAL(class_createInstance) = function(/*Class*/ aClass) DISPLAY_NAME(class_createInstance); // Opera 9.5.1 has a bug where prototypes "inheret" members from instances when "with" is used. -// Given that the Opera team is so fond of bug-testing instead of version-testing, we'll go +// Given that the Opera team is so fond of bug-testing instead of version-testing, we'll go // ahead and do that. var prototype_bug = function() { } @@ -417,7 +417,7 @@ with (new prototype_bug()) member = true; // If the bug exists, go down the slow path. -if (new prototype_bug().member) +if (new prototype_bug().member) { var fast_class_createInstance = class_createInstance; @@ -425,23 +425,23 @@ var fast_class_createInstance = class_createInstance; class_createInstance = function(/*Class*/ aClass) { var object = fast_class_createInstance(aClass); - + if (object) { var theClass = object.isa, actualClass = theClass; - + while (theClass) { var ivars = theClass.ivars; count = ivars.length; - + while (count--) object[ivars[count].name] = NULL; - + theClass = theClass.super_class; } - + object.isa = actualClass; } @@ -464,11 +464,11 @@ GLOBAL(object_getClassName) = function(/*id*/ anObject) DISPLAY_NAME(object_getClassName); -//objc_getClassList +//objc_getClassList GLOBAL(objj_lookUpClass) = function(/*String*/ aName) { var theClass = REGISTERED_CLASSES[aName]; - + return theClass ? theClass : Nil; } @@ -477,22 +477,22 @@ DISPLAY_NAME(objj_lookUpClass); GLOBAL(objj_getClass) = function(/*String*/ aName) { var theClass = REGISTERED_CLASSES[aName]; - + if (!theClass) { // class handler callback??? } - + return theClass ? theClass : Nil; } DISPLAY_NAME(objj_getClass); -//objc_getRequiredClass +//objc_getRequiredClass GLOBAL(objj_getMetaClass) = function(/*String*/ aName) { var theClass = objj_getClass(aName); - + return GETMETA(theClass); } @@ -569,9 +569,9 @@ DISPLAY_NAME(method_getImplementation); GLOBAL(method_setImplementation) = function(/*Method*/ aMethod, /*IMP*/ anImplementation) { var oldImplementation = aMethod.method_imp; - + aMethod.method_imp = anImplementation; - + return oldImplementation; } diff --git a/Tests/AppKit/CPArrayControllerTest.j b/Tests/AppKit/CPArrayControllerTest.j new file mode 100644 index 000000000..d311ccd56 --- /dev/null +++ b/Tests/AppKit/CPArrayControllerTest.j @@ -0,0 +1,157 @@ +@implementation CPArrayControllerTest : OJTestCase +{ + CPArrayController _arrayController @accessors(property=arrayController); + CPArray _contentArray @accessors(property=contentArray); +} + +- (void)setUp +{ + _contentArray = []; + + [_contentArray addObject:[Person personWithName:@"Francisco" age:21]]; + [_contentArray addObject:[Person personWithName:@"Ross" age:30]]; + [_contentArray addObject:[Person personWithName:@"Tom" age:15]]; + + // Copy the array since we'll reuse the original array later. Also see issue #795. + _arrayController = [[CPArrayController alloc] initWithContent:[[self contentArray] copy]]; +} + +- (void)testInitWithContent +{ + [self assert:[self contentArray] equals:[[self arrayController] contentArray]]; + [self assert:[_CPObservableArray class] equals:[[[self arrayController] arrangedObjects] class]]; +} + +- (void)testSetContent +{ + otherContent = [@"5", @"6"]; + [[self arrayController] setContent:otherContent]; + + // This has not been decided on yet. See Issue #795. + // [self assertFalse:otherContent === [[self arrayController] contentArray] message:@"array controller should copy it's content"]; + [self assert:otherContent equals:[[self arrayController] contentArray]]; + [self assert:[_CPObservableArray class] equals:[[[self arrayController] arrangedObjects] class]]; +} + +- (void)testInsertObjectAtArrangedObjectIndex +{ + var object = [Person personWithName:@"Klaas Pieter" age:24], + arrayController = [self arrayController]; + + [arrayController setSortDescriptors:[[CPSortDescriptor sortDescriptorWithKey:@"age" ascending:YES]]]; + [arrayController insertObject:object atArrangedObjectIndex:1]; + + [self assert:object equals:[[arrayController arrangedObjects] objectAtIndex:1]]; +} + +- (void)testSelectPrevious +{ + var arrayController = [self arrayController]; + + // Selection index: 1 + [arrayController setSelectionIndexes:[CPIndexSet indexSetWithIndex:1]]; + [self assertTrue:[arrayController canSelectPrevious] message:@"index > 0; canSelectPrevious should return YES"] + + [arrayController selectPrevious:self]; + [self assert:[CPIndexSet indexSetWithIndex:0] equals:[arrayController selectionIndexes]]; + + // Selection index: 0 + [self assertFalse:[arrayController canSelectPrevious] message:@"index <= 0; canSelectPrevious should return NO"]; + + [arrayController selectPrevious:self]; + [self assert:[CPIndexSet indexSetWithIndex:0] equals:[arrayController selectionIndexes]]; +} + +- (void)testSelectNext +{ + var arrayController = [self arrayController], + count = [[arrayController arrangedObjects] count]; + + // Selection index: count - 2 + [arrayController setSelectionIndexes:[CPIndexSet indexSetWithIndex:count - 2]]; + [self assertTrue:[arrayController canSelectNext] message:@"index < (count - 1); canSelectNext should return YES"]; + + [arrayController selectNext:self]; + [self assert:[CPIndexSet indexSetWithIndex:count - 1] equals:[arrayController selectionIndexes]]; + + // Selection index: count - 1 + [self assertFalse:[arrayController canSelectNext] message:@"index >= (count - 1) canSelectNext should return NO"]; + + [arrayController selectNext:self]; + [self assert:[CPIndexSet indexSetWithIndex:count - 1] equals:[arrayController selectionIndexes]]; +} + +- (void)testRemoveObjects +{ + var arrayController = [self arrayController]; + [arrayController setPreservesSelection:NO]; + + [arrayController setSelectionIndexes:[CPIndexSet indexSetWithIndexesInRange:CPMakeRange(1, 2)]]; + [arrayController removeObjects:[arrayController selectedObjects]] + + [self assert:[CPIndexSet indexSet] equals:[arrayController selectionIndexes] + message:@"selection should be empty if arraycontroller doesn't preserve selection"]; + + arrayController = [[CPArrayController alloc] initWithContent:[self contentArray]]; + [arrayController setPreservesSelection:YES]; + + // Remove from middle + var selectionIndexes = [CPIndexSet indexSetWithIndex:1]; + [arrayController setSelectionIndexes:selectionIndexes]; + [arrayController removeObjects:[arrayController selectedObjects]]; + [self assert:selectionIndexes equals:[arrayController selectionIndexes] message:@"selection should stay the same"]; + + // Remove from end + [arrayController removeObjects:[[[arrayController content] objectAtIndex:1]]]; + [self assert:[CPIndexSet indexSetWithIndex:0] equals:[arrayController selectionIndexes] + message:@"last object removed; selection should shift to first available index"]; + + // Remove from all + [arrayController removeObjects:[[[arrayController content] objectAtIndex:0]]]; + [self assert:[CPIndexSet indexSet] equals:[arrayController selectionIndexes] message:@"no objects left, selection should disappear"]; +} + +- (void)testContentBinding +{ + [[self arrayController] bind:@"contentArray" toObject:self withKeyPath:@"contentArray" options:0]; + + [self assert:[[self arrayController] contentArray] equals:[self contentArray]]; + + [[self mutableArrayValueForKey:@"contentArray"] addObject:@"4"]; + [self assert:[self contentArray] equals:[[self arrayController] contentArray] + message:@"object 4 was added; contentArray should reflect this"]; + + [[self arrayController] insertObject:@"2" atArrangedObjectIndex:1]; + [self assert:[[self arrayController] contentArray] equals:[self contentArray]]; +} + +@end + +@implementation Person : CPObject +{ + CPString _name @accessors(property=name); + int _age @accessors(property=age); +} + ++ (id)personWithName:(CPString)aName age:(int)anAge +{ + return [[self alloc] initWithName:aName age:anAge]; +} + +- (id)initWithName:(CPString)aName age:(int)anAge +{ + if (self = [super init]) + { + _name = aName; + _age = anAge; + } + + return self; +} + +- (CPString)description +{ + return [CPString stringWithFormat:@"%@ : %@", [self name], [self age]]; +} + +@end \ No newline at end of file diff --git a/Tests/Foundation/CPArrayPerformanceTest.j b/Tests/Foundation/CPArrayPerformanceTest.j new file mode 100644 index 000000000..7ac75328b --- /dev/null +++ b/Tests/Foundation/CPArrayPerformanceTest.j @@ -0,0 +1,93 @@ +@import +@import +@import +@import + +var ELEMENTS = 100, + REPEATS = 10; + +@implementation CPArrayPerformanceTest : OJTestCase + +- (void)testSortUsingDescriptorsSpeed +{ + var array = [self makeUnsorted]; + + var descriptors = [ + [CPSortDescriptor sortDescriptorWithKey:"a" ascending:NO], + [CPSortDescriptor sortDescriptorWithKey:"b" ascending:YES] + ]; + + var start = (new Date).getTime(); + + for (var i=0; i + + +@implementation AppController : CPObject +{ + CPTextField label; + CPArray variations; + CPArray messages; + int messageIndex; +} + +- (void)applicationDidFinishLaunching:(CPNotification)aNotification +{ + messages = [ + [@"Are you sure you want to theorise before you have data?", + @"Invariably, you end up twisting facts to suit theories, instead of theories to suit facts.", + "Theorise", "Cancel"], + [@"Snakes. Why did it have to be snakes?", + nil, + "Torch", "Cancel"], + [@"Sometimes a message can be really long and just appear to go on and on. It could be a speech. It could be the television.", + nil, + "Off", "Cancel"] + ]; + messageIndex = 0; + + variations = [ + [nil, CPWarningAlertStyle], + [nil, CPInformationalAlertStyle], + [nil, CPCriticalAlertStyle], + [CPHUDBackgroundWindowMask, CPWarningAlertStyle], + [CPHUDBackgroundWindowMask, CPInformationalAlertStyle], + [CPHUDBackgroundWindowMask, CPCriticalAlertStyle], + ]; + + var theWindow = [[CPWindow alloc] initWithContentRect:CGRectMakeZero() styleMask:CPBorderlessBridgeWindowMask], + contentView = [theWindow contentView]; + + label = [[CPTextField alloc] initWithFrame:CGRectMake(15, 15, 400, 24)]; + + [label setStringValue:"Respond to the alert dialog with the mouse or the keyboard."]; + [contentView addSubview:label]; + + [theWindow orderFront:self]; + + [self showNextAlertVariation]; + + // Uncomment the following line to turn on the standard menu bar. + //[CPMenu setMenuBarVisible:YES]; +} + +- (void)alertDidEnd:(CPAlert)anAlert returnCode:(id)returnCode +{ + if (returnCode === 0) + [label setStringValue:"You chose the default action."]; + else + [label setStringValue:"You cancelled the dialog."]; + + [self showNextAlertVariation]; +} + +- (void)showNextAlertVariation +{ + if (![variations count]) + return; + + var variation = variations[0], + message = messages[messageIndex], + alert = [[CPAlert alloc] init]; + + messageIndex = (messageIndex + 1) % messages.length; + [variations removeObjectAtIndex:0]; + + [alert setDelegate:self]; + [alert setMessageText:message[0]]; + [alert setInformativeText:message[1]]; + [alert addButtonWithTitle:message[2]]; + [alert addButtonWithTitle:message[3]]; + [alert setWindowStyle:variation[0]]; + [alert setAlertStyle:variation[1]]; + + [alert runModal]; +} + +@end diff --git a/Tests/Manual/CPAlertTest/Info.plist b/Tests/Manual/CPAlertTest/Info.plist new file mode 100644 index 000000000..72c8e59a8 --- /dev/null +++ b/Tests/Manual/CPAlertTest/Info.plist @@ -0,0 +1,12 @@ + + + + + CPApplicationDelegateClass + AppController + CPBundleName + CPAlertTest + CPPrincipalClass + CPApplication + + diff --git a/Tests/Manual/CPAlertTest/Jakefile b/Tests/Manual/CPAlertTest/Jakefile new file mode 100644 index 000000000..a776e282b --- /dev/null +++ b/Tests/Manual/CPAlertTest/Jakefile @@ -0,0 +1,93 @@ +/* + * Jakefile + * CPAlertTest + * + * Created by Alexander Ljungberg on August 19, 2010. + * Copyright 2010, WireLoad, LLC 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 ("CPAlertTest", function(task) +{ + task.setBuildIntermediatesPath(FILE.join("Build", "CPAlertTest.build", configuration)); + task.setBuildPath(FILE.join("Build", configuration)); + + task.setProductName("CPAlertTest"); + task.setIdentifier("com.yourcompany.CPAlertTest"); + task.setVersion("1.0"); + task.setAuthor("WireLoad, LLC"); + task.setEmail("feedback @nospam@ yourcompany.com"); + task.setSummary("CPAlertTest"); + task.setSources((new FileList("**/*.j")).exclude(FILE.join("Build", "**"))); + task.setResources(new FileList("Resources/**")); + task.setIndexFilePath("index.html"); + task.setInfoPlistPath("Info.plist"); + + if (configuration === "Debug") + task.setCompilerFlags("-DDEBUG -g"); + else + task.setCompilerFlags("-O"); +}); + +task ("default", ["CPAlertTest"], 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", "CPAlertTest", "index.html")]); +}); + +task ("run-release", ["release"], function() +{ + OS.system(["open", FILE.join("Build", "Release", "CPAlertTest", "index.html")]); +}); + +task ("deploy", ["release"], function() +{ + FILE.mkdirs(FILE.join("Build", "Deployment", "CPAlertTest")); + OS.system(["press", "-f", FILE.join("Build", "Release", "CPAlertTest"), FILE.join("Build", "Deployment", "CPAlertTest")]); + printResults("Deployment") +}); + +task ("desktop", ["release"], function() +{ + FILE.mkdirs(FILE.join("Build", "Desktop", "CPAlertTest")); + require("cappuccino/nativehost").buildNativeHost(FILE.join("Build", "Release", "CPAlertTest"), FILE.join("Build", "Desktop", "CPAlertTest", "CPAlertTest.app")); + printResults("Desktop") +}); + +task ("run-desktop", ["desktop"], function() +{ + OS.system([FILE.join("Build", "Desktop", "CPAlertTest", "CPAlertTest.app", "Contents", "MacOS", "NativeHost"), "-i"]); +}); + +function printResults(configuration) +{ + print("----------------------------"); + print(configuration+" app built at path: "+FILE.join("Build", configuration, "CPAlertTest")); + print("----------------------------"); +} diff --git a/Tests/Manual/CPAlertTest/Resources/spinner.gif b/Tests/Manual/CPAlertTest/Resources/spinner.gif new file mode 100644 index 000000000..06dbc2bc2 Binary files /dev/null and b/Tests/Manual/CPAlertTest/Resources/spinner.gif differ diff --git a/Tests/Manual/CPAlertTest/index-debug.html b/Tests/Manual/CPAlertTest/index-debug.html new file mode 100644 index 000000000..d5cebcf0d --- /dev/null +++ b/Tests/Manual/CPAlertTest/index-debug.html @@ -0,0 +1,104 @@ + + + + + + + + + + + + + + + + CPAlertTest + + + + + + + + + + + + + + +
+
+ + + +
+
+ + + diff --git a/Tests/Manual/CPAlertTest/index.html b/Tests/Manual/CPAlertTest/index.html new file mode 100644 index 000000000..71ac40da9 --- /dev/null +++ b/Tests/Manual/CPAlertTest/index.html @@ -0,0 +1,79 @@ + + + + + + + + + + + + + + + + CPAlertTest + + + + + + + + + + + + +
+
+ + + +
+
+ + + + diff --git a/Tests/Manual/CPAlertTest/main.j b/Tests/Manual/CPAlertTest/main.j new file mode 100644 index 000000000..a589c11ea --- /dev/null +++ b/Tests/Manual/CPAlertTest/main.j @@ -0,0 +1,18 @@ +/* + * AppController.j + * CPAlertTest + * + * Created by Alexander Ljungberg on August 19, 2010. + * Copyright 2010, WireLoad, LLC All rights reserved. + */ + +@import +@import + +@import "AppController.j" + + +function main(args, namedArgs) +{ + CPApplicationMain(args, namedArgs); +} diff --git a/Tools/Documentation/Cappuccino.doxygen b/Tools/Documentation/Cappuccino.doxygen index dcd2832f7..52988fd8b 100644 --- a/Tools/Documentation/Cappuccino.doxygen +++ b/Tools/Documentation/Cappuccino.doxygen @@ -217,14 +217,15 @@ OPTIMIZE_FOR_FORTRAN = NO OPTIMIZE_OUTPUT_VHDL = NO -# Doxygen selects the parser to use depending on the extension of the files it parses. -# With this tag you can assign which parser to use for a given extension. -# Doxygen has a built-in mapping, but you can override or extend it using this tag. -# The format is ext=language, where ext is a file extension, and language is one of -# the parsers supported by doxygen: IDL, Java, Javascript, C#, C, C++, D, PHP, -# Objective-C, Python, Fortran, VHDL, C, C++. For instance to make doxygen treat -# .inc files as Fortran files (default is PHP), and .f files as C (default is Fortran), -# use: inc=Fortran f=C. Note that for custom extensions you also need to set FILE_PATTERNS otherwise the files are not read by doxygen. +# Doxygen selects the parser to use depending on the extension of the files it +# parses. With this tag you can assign which parser to use for a given extension. +# Doxygen has a built-in mapping, but you can override or extend it using this +# tag. The format is ext=language, where ext is a file extension, and language +# is one of the parsers supported by doxygen: IDL, Java, Javascript, CSharp, C, +# C++, D, PHP, Objective-C, Python, Fortran, VHDL, C, C++. For instance to make +# doxygen treat .inc files as Fortran files (default is PHP), and .f files as C +# (default is Fortran), use: inc=Fortran f=C. Note that for custom extensions +# you also need to set FILE_PATTERNS otherwise the files are not read by doxygen. EXTENSION_MAPPING = j=Objective-C @@ -255,7 +256,7 @@ SIP_SUPPORT = NO # setting a simple type. If this is not the case, or you want to show the # methods anyway, you should set this option to NO. -IDL_PROPERTY_SUPPORT = YES +IDL_PROPERTY_SUPPORT = NO # If member grouping is used in the documentation and the DISTRIBUTE_GROUP_DOC # tag is set to YES, then doxygen will reuse the documentation of the first @@ -396,13 +397,13 @@ HIDE_SCOPE_NAMES = NO SHOW_INCLUDE_FILES = YES -# If the FORCE_LOCAL_INCLUDES tag is set to YES then Doxygen -# will list include files with double quotes in the documentation +# If the FORCE_LOCAL_INCLUDES tag is set to YES then Doxygen +# will list include files with double quotes in the documentation # rather than with sharp brackets. FORCE_LOCAL_INCLUDES = NO -# If the INLINE_INFO tag is set to YES (the default) then a tag [inline] +# If the INLINE_INFO tag is set to YES (the default) then a tag [inline] # is inserted in the documentation for inline members. INLINE_INFO = YES @@ -421,12 +422,13 @@ SORT_MEMBER_DOCS = YES SORT_BRIEF_DOCS = YES -# If the SORT_MEMBERS_CTORS_1ST tag is set to YES then doxygen will sort the -# (brief and detailed) documentation of class members so that constructors and -# destructors are listed first. If set to NO (the default) the constructors will -# appear in the respective orders defined by SORT_MEMBER_DOCS and -# SORT_BRIEF_DOCS. This tag will be ignored for brief docs if SORT_BRIEF_DOCS is -# set to NO and ignored for detailed docs if SORT_MEMBER_DOCS is set to NO. +# If the SORT_MEMBERS_CTORS_1ST tag is set to YES then doxygen +# will sort the (brief and detailed) documentation of class members so that +# constructors and destructors are listed first. If set to NO (the default) +# the constructors will appear in the respective orders defined by +# SORT_MEMBER_DOCS and SORT_BRIEF_DOCS. +# This tag will be ignored for brief docs if SORT_BRIEF_DOCS is set to NO +# and ignored for detailed docs if SORT_MEMBER_DOCS is set to NO. SORT_MEMBERS_CTORS_1ST = NO @@ -504,8 +506,7 @@ SHOW_DIRECTORIES = NO SHOW_FILES = YES # Set the SHOW_NAMESPACES tag to NO to disable the generation of the -# Namespaces page. -# This will remove the Namespaces entry from the Quick Index +# Namespaces page. This will remove the Namespaces entry from the Quick Index # and from the Folder Tree View (if specified). The default is YES. SHOW_NAMESPACES = YES @@ -520,12 +521,12 @@ SHOW_NAMESPACES = YES FILE_VERSION_FILTER = -# The LAYOUT_FILE tag can be used to specify a layout file which will be parsed by -# doxygen. The layout file controls the global structure of the generated output files -# in an output format independent way. The create the layout file that represents -# doxygen's defaults, run doxygen with the -l option. You can optionally specify a -# file name after the option, if omitted DoxygenLayout.xml will be used as the name -# of the layout file. +# The LAYOUT_FILE tag can be used to specify a layout file which will be parsed +# by doxygen. The layout file controls the global structure of the generated +# output files in an output format independent way. The create the layout file +# that represents doxygen's defaults, run doxygen with the -l option. +# You can optionally specify a file name after the option, if omitted +# DoxygenLayout.xml will be used as the name of the layout file. LAYOUT_FILE = @@ -589,8 +590,8 @@ WARN_LOGFILE = debug.txt # directories like "/usr/src/myproject". Separate the files or directories # with spaces. -INPUT = AppKit.doc \ - Foundation.doc +INPUT = AppKit \ + Foundation # This tag can be used to specify the character encoding of the source files # that doxygen parses. Internally doxygen uses the UTF-8 encoding, which is @@ -650,7 +651,7 @@ RECURSIVE = YES # excluded from the INPUT source files. This way you can easily exclude a # subdirectory from a directory tree whose root is specified with the INPUT tag. -EXCLUDE = AppKit.doc/Themes +EXCLUDE = AppKit/Themes # The EXCLUDE_SYMLINKS tag can be used select whether or not files or # directories that are symbolic links (a Unix filesystem feature) are excluded @@ -706,17 +707,14 @@ IMAGE_PATH = # by executing (via popen()) the command , where # is the value of the INPUT_FILTER tag, and is the name of an # input file. Doxygen will then use the output that the filter program writes -# to standard output. -# If FILTER_PATTERNS is specified, this tag will be +# to standard output. If FILTER_PATTERNS is specified, this tag will be # ignored. INPUT_FILTER = # The FILTER_PATTERNS tag can be used to specify filters on a per file pattern -# basis. -# Doxygen will compare the file name with each pattern and apply the -# filter if there is a match. -# The filters are a list of the form: +# basis. Doxygen will compare the file name with each pattern and apply the +# filter if there is a match. The filters are a list of the form: # pattern=filter (like *.cpp=my_cpp_filter). See INPUT_FILTER for further # info on how filters are used. If FILTER_PATTERNS is empty, INPUT_FILTER # is applied to all files. @@ -766,8 +764,7 @@ REFERENCES_RELATION = NO # If the REFERENCES_LINK_SOURCE tag is set to YES (the default) # and SOURCE_BROWSER tag is set to YES, then the hyperlinks from # functions in REFERENCES_RELATION and REFERENCED_BY_RELATION lists will -# link to the source code. -# Otherwise they will link to the documentation. +# link to the source code. Otherwise they will link to the documentation. REFERENCES_LINK_SOURCE = YES @@ -839,7 +836,7 @@ HTML_HEADER = # each generated HTML page. If it is left blank doxygen will generate a # standard footer. -HTML_FOOTER = +HTML_FOOTER = # The HTML_STYLESHEET tag can be used to specify a user-defined cascading # style sheet that is used by each HTML page. It can be used to @@ -848,41 +845,41 @@ HTML_FOOTER = # the style sheet file to the HTML output directory, so don't put your own # stylesheet in the HTML output directory as well, or it will be erased! -HTML_STYLESHEET = +HTML_STYLESHEET = -# The HTML_COLORSTYLE_HUE tag controls the color of the HTML output. -# Doxygen will adjust the colors in the stylesheet and background images -# according to this color. Hue is specified as an angle on a colorwheel, -# see http://en.wikipedia.org/wiki/Hue for more information. -# For instance the value 0 represents red, 60 is yellow, 120 is green, -# 180 is cyan, 240 is blue, 300 purple, and 360 is red again. +# The HTML_COLORSTYLE_HUE tag controls the color of the HTML output. +# Doxygen will adjust the colors in the stylesheet and background images +# according to this color. Hue is specified as an angle on a colorwheel, +# see http://en.wikipedia.org/wiki/Hue for more information. +# For instance the value 0 represents red, 60 is yellow, 120 is green, +# 180 is cyan, 240 is blue, 300 purple, and 360 is red again. # The allowed range is 0 to 359. HTML_COLORSTYLE_HUE = 220 -# The HTML_COLORSTYLE_SAT tag controls the purity (or saturation) of -# the colors in the HTML output. For a value of 0 the output will use +# The HTML_COLORSTYLE_SAT tag controls the purity (or saturation) of +# the colors in the HTML output. For a value of 0 the output will use # grayscales only. A value of 255 will produce the most vivid colors. HTML_COLORSTYLE_SAT = 98 -# The HTML_COLORSTYLE_GAMMA tag controls the gamma correction applied to -# the luminance component of the colors in the HTML output. Values below -# 100 gradually make the output lighter, whereas values above 100 make -# the output darker. The value divided by 100 is the actual gamma applied, -# so 80 represents a gamma of 0.8, The value 220 represents a gamma of 2.2, +# The HTML_COLORSTYLE_GAMMA tag controls the gamma correction applied to +# the luminance component of the colors in the HTML output. Values below +# 100 gradually make the output lighter, whereas values above 100 make +# the output darker. The value divided by 100 is the actual gamma applied, +# so 80 represents a gamma of 0.8, The value 220 represents a gamma of 2.2, # and 100 does not change the gamma. HTML_COLORSTYLE_GAMMA = 80 -# If the HTML_TIMESTAMP tag is set to YES then the footer of each generated HTML -# page will contain the date and time when the page was generated. Setting +# If the HTML_TIMESTAMP tag is set to YES then the footer of each generated HTML +# page will contain the date and time when the page was generated. Setting # this to NO can help when comparing the output of multiple runs. HTML_TIMESTAMP = YES -# If the HTML_ALIGN_MEMBERS tag is set to YES, the members of classes, -# files or namespaces will be aligned in HTML using tables. If set to +# If the HTML_ALIGN_MEMBERS tag is set to YES, the members of classes, +# files or namespaces will be aligned in HTML using tables. If set to # NO a bullet list will be used. HTML_ALIGN_MEMBERS = YES @@ -903,7 +900,8 @@ HTML_DYNAMIC_SECTIONS = YES # directory and running "make install" will install the docset in # ~/Library/Developer/Shared/Documentation/DocSets so that Xcode will find # it at startup. -# See http://developer.apple.com/tools/creatingdocsetswithdoxygen.html for more information. +# See http://developer.apple.com/tools/creatingdocsetswithdoxygen.html +# for more information. GENERATE_DOCSET = NO @@ -921,8 +919,8 @@ DOCSET_FEEDNAME = "Doxygen generated docs" DOCSET_BUNDLE_ID = org.doxygen.Project -# When GENERATE_PUBLISHER_ID tag specifies a string that should uniquely identify -# the documentation publisher. This should be a reverse domain-name style +# When GENERATE_PUBLISHER_ID tag specifies a string that should uniquely identify +# the documentation publisher. This should be a reverse domain-name style # string, e.g. com.mycompany.MyDocSet.documentation. DOCSET_PUBLISHER_ID = org.doxygen.Publisher @@ -931,9 +929,9 @@ DOCSET_PUBLISHER_ID = org.doxygen.Publisher DOCSET_PUBLISHER_NAME = Publisher -# If the GENERATE_HTMLHELP tag is set to YES, additional index files -# will be generated that can be used as input for tools like the -# Microsoft HTML help workshop to generate a compiled HTML help file (.chm) +# If the GENERATE_HTMLHELP tag is set to YES, additional index files +# will be generated that can be used as input for tools like the +# Microsoft HTML help workshop to generate a compiled HTML help file (.chm) # of the generated HTML documentation. GENERATE_HTMLHELP = NO @@ -975,10 +973,10 @@ BINARY_TOC = NO TOC_EXPAND = NO -# If the GENERATE_QHP tag is set to YES and both QHP_NAMESPACE and QHP_VIRTUAL_FOLDER -# are set, an additional index file will be generated that can be used as input for -# Qt's qhelpgenerator to generate a Qt Compressed Help (.qch) of the generated -# HTML documentation. +# If the GENERATE_QHP tag is set to YES and both QHP_NAMESPACE and +# QHP_VIRTUAL_FOLDER are set, an additional index file will be generated +# that can be used as input for Qt's qhelpgenerator to generate a +# Qt Compressed Help (.qch) of the generated HTML documentation. GENERATE_QHP = NO @@ -992,7 +990,7 @@ QCH_FILE = # Qt Help Project output. For more information please see # http://doc.trolltech.com/qthelpproject.html#namespace -QHP_NAMESPACE = +QHP_NAMESPACE = org.doxygen.Project # The QHP_VIRTUAL_FOLDER tag specifies the namespace to use when generating # Qt Help Project output. For more information please see @@ -1000,47 +998,47 @@ QHP_NAMESPACE = QHP_VIRTUAL_FOLDER = doc -# If QHP_CUST_FILTER_NAME is set, it specifies the name of a custom filter to add. -# For more information please see +# If QHP_CUST_FILTER_NAME is set, it specifies the name of a custom filter to +# add. For more information please see # http://doc.trolltech.com/qthelpproject.html#custom-filters QHP_CUST_FILTER_NAME = -# The QHP_CUST_FILT_ATTRS tag specifies the list of the attributes of the -# custom filter to add. For more information please see -# +# The QHP_CUST_FILT_ATTRS tag specifies the list of the attributes of the +# custom filter to add. For more information please see +# # Qt Help Project / Custom Filters. -QHP_CUST_FILTER_ATTRS = +QHP_CUST_FILTER_ATTRS = -# The QHP_SECT_FILTER_ATTRS tag specifies the list of the attributes this -# project's -# filter section matches. -# +# The QHP_SECT_FILTER_ATTRS tag specifies the list of the attributes this +# project's +# filter section matches. +# # Qt Help Project / Filter Attributes. -QHP_SECT_FILTER_ATTRS = +QHP_SECT_FILTER_ATTRS = -# If the GENERATE_QHP tag is set to YES, the QHG_LOCATION tag can -# be used to specify the location of Qt's qhelpgenerator. -# If non-empty doxygen will try to run qhelpgenerator on the generated +# If the GENERATE_QHP tag is set to YES, the QHG_LOCATION tag can +# be used to specify the location of Qt's qhelpgenerator. +# If non-empty doxygen will try to run qhelpgenerator on the generated # .qhp file. -QHG_LOCATION = +QHG_LOCATION = -# If the GENERATE_ECLIPSEHELP tag is set to YES, additional index files -# will be generated, which together with the HTML files, form an Eclipse help -# plugin. To install this plugin and make it available under the help contents -# menu in Eclipse, the contents of the directory containing the HTML and XML -# files needs to be copied into the plugins directory of eclipse. The name of -# the directory within the plugins directory should be the same as -# the ECLIPSE_DOC_ID value. After copying Eclipse needs to be restarted before +# If the GENERATE_ECLIPSEHELP tag is set to YES, additional index files +# will be generated, which together with the HTML files, form an Eclipse help +# plugin. To install this plugin and make it available under the help contents +# menu in Eclipse, the contents of the directory containing the HTML and XML +# files needs to be copied into the plugins directory of eclipse. The name of +# the directory within the plugins directory should be the same as +# the ECLIPSE_DOC_ID value. After copying Eclipse needs to be restarted before # the help appears. GENERATE_ECLIPSEHELP = NO -# A unique identifier for the eclipse help plugin. When installing the plugin -# the directory name containing the HTML and XML files should also have +# A unique identifier for the eclipse help plugin. When installing the plugin +# the directory name containing the HTML and XML files should also have # this name. ECLIPSE_DOC_ID = org.doxygen.Project @@ -1077,7 +1075,7 @@ USE_INLINE_TREES = NO TREEVIEW_WIDTH = 250 -# When the EXT_LINKS_IN_WINDOW option is set to YES doxygen will open +# When the EXT_LINKS_IN_WINDOW option is set to YES doxygen will open # links to external symbols imported via tag files in a separate window. EXT_LINKS_IN_WINDOW = NO @@ -1090,30 +1088,30 @@ EXT_LINKS_IN_WINDOW = NO FORMULA_FONTSIZE = 10 -# Use the FORMULA_TRANPARENT tag to determine whether or not the images -# generated for formulas are transparent PNGs. Transparent PNGs are -# not supported properly for IE 6.0, but are supported on all modern browsers. -# Note that when changing this option you need to delete any form_*.png files +# Use the FORMULA_TRANPARENT tag to determine whether or not the images +# generated for formulas are transparent PNGs. Transparent PNGs are +# not supported properly for IE 6.0, but are supported on all modern browsers. +# Note that when changing this option you need to delete any form_*.png files # in the HTML output before the changes have effect. FORMULA_TRANSPARENT = YES -# When the SEARCHENGINE tag is enabled doxygen will generate a search box -# for the HTML output. The underlying search engine uses javascript -# and DHTML and should work on any modern browser. Note that when using -# HTML help (GENERATE_HTMLHELP), Qt help (GENERATE_QHP), or docsets -# (GENERATE_DOCSET) there is already a search function so this one should -# typically be disabled. For large projects the javascript based search engine +# When the SEARCHENGINE tag is enabled doxygen will generate a search box +# for the HTML output. The underlying search engine uses javascript +# and DHTML and should work on any modern browser. Note that when using +# HTML help (GENERATE_HTMLHELP), Qt help (GENERATE_QHP), or docsets +# (GENERATE_DOCSET) there is already a search function so this one should +# typically be disabled. For large projects the javascript based search engine # can be slow, then enabling SERVER_BASED_SEARCH may provide a better solution. SEARCHENGINE = YES -# When the SERVER_BASED_SEARCH tag is enabled the search engine will be -# implemented using a PHP enabled web server instead of at the web client -# using Javascript. Doxygen will generate the search PHP script and index -# file to put on the web server. The advantage of the server -# based approach is that it scales better to large projects and allows -# full text search. The disadvances is that it is more difficult to setup +# When the SERVER_BASED_SEARCH tag is enabled the search engine will be +# implemented using a PHP enabled web server instead of at the web client +# using Javascript. Doxygen will generate the search PHP script and index +# file to put on the web server. The advantage of the server +# based approach is that it scales better to large projects and allows +# full text search. The disadvances is that it is more difficult to setup # and does not have live searching capabilities. SERVER_BASED_SEARCH = NO @@ -1135,6 +1133,9 @@ LATEX_OUTPUT = latex # The LATEX_CMD_NAME tag can be used to specify the LaTeX command name to be # invoked. If left blank `latex' will be used as the default command name. +# Note that when enabling USE_PDFLATEX this option is only used for +# generating bitmaps for formulas in the HTML output, but not in the +# Makefile that is written to the output directory. LATEX_CMD_NAME = latex @@ -1194,7 +1195,10 @@ LATEX_BATCHMODE = NO LATEX_HIDE_INDICES = NO -# If LATEX_SOURCE_CODE is set to YES then doxygen will include source code with syntax highlighting in the LaTeX output. Note that which sources are shown also depends on other settings such as SOURCE_BROWSER. +# If LATEX_SOURCE_CODE is set to YES then doxygen will include +# source code with syntax highlighting in the LaTeX output. +# Note that which sources are shown also depends on other settings +# such as SOURCE_BROWSER. LATEX_SOURCE_CODE = NO @@ -1333,10 +1337,10 @@ GENERATE_PERLMOD = NO PERLMOD_LATEX = NO -# If the PERLMOD_PRETTY tag is set to YES the Perl module output will be -# nicely formatted so it can be parsed by a human reader. This is useful -# if you want to understand what is going on. On the other hand, if this -# tag is set to NO the size of the Perl module output will be much smaller +# If the PERLMOD_PRETTY tag is set to YES the Perl module output will be +# nicely formatted so it can be parsed by a human reader. This is useful +# if you want to understand what is going on. On the other hand, if this +# tag is set to NO the size of the Perl module output will be much smaller # and Perl will parse it just the same. PERLMOD_PRETTY = YES @@ -1363,7 +1367,7 @@ ENABLE_PREPROCESSING = YES # compilation will be performed. Macro expansion can be done in a controlled # way by setting EXPAND_ONLY_PREDEF to YES. -MACRO_EXPANSION = YES +MACRO_EXPANSION = NO # If the EXPAND_ONLY_PREDEF and MACRO_EXPANSION tags are both set to YES # then the macro expansion is limited to the macros specified with the @@ -1422,11 +1426,9 @@ SKIP_FUNCTION_MACROS = YES # Optionally an initial location of the external documentation # can be added for each tagfile. The format of a tag file without # this location is as follows: -# -# TAGFILES = file1 file2 ... +# TAGFILES = file1 file2 ... # Adding location for the tag files is done as follows: -# -# TAGFILES = file1=loc1 "file2 = loc2" ... +# TAGFILES = file1=loc1 "file2 = loc2" ... # where "loc1" and "loc2" can be relative or absolute paths or # URLs. If a location is present for each tag, the installdox tool # does not have to be run to correct the links. @@ -1494,10 +1496,10 @@ HIDE_UNDOC_RELATIONS = YES HAVE_DOT = YES -# The DOT_NUM_THREADS specifies the number of dot invocations doxygen is -# allowed to run in parallel. When set to 0 (the default) doxygen will -# base this on the number of processors available in the system. You can set it -# explicitly to a value larger than 0 to get control over the balance +# The DOT_NUM_THREADS specifies the number of dot invocations doxygen is +# allowed to run in parallel. When set to 0 (the default) doxygen will +# base this on the number of processors available in the system. You can set it +# explicitly to a value larger than 0 to get control over the balance # between CPU load and processing speed. DOT_NUM_THREADS = 0 diff --git a/Tools/Documentation/make_headers b/Tools/Documentation/make_headers index 70ad68aec..6213468e2 100755 --- a/Tools/Documentation/make_headers +++ b/Tools/Documentation/make_headers @@ -3,7 +3,7 @@ def makeHeaderFileFrom(fileName) # Grab the entire file (text) data = File.new(fileName).read - + # Extract out all the "implementation" lines. Note, there may be # more than on in a given .j file. m = data.scan(/.*?(@implementation.*?\{.*?\})/m) @@ -11,16 +11,16 @@ def makeHeaderFileFrom(fileName) if m.length > 0 for i in 0...m.length line = m[i][0] - + # Skip category definitions if not /.*?\(.*?\).*?\{/m.match(line) - + # Extract the class name m2 = line.scan(/.*?@implementation(.*?):.*/m) if !m2.nil? && !m2[0].nil? className = m2[0][0].strip() - + # Change "implementation" to "interface", create the .h file, and write the interface newLine = line.sub("implementation", "interface") newFileName = File.dirname(fileName) + "/" + className + ".h" diff --git a/common.jake b/common.jake index 3faaba09c..c600261ae 100644 --- a/common.jake +++ b/common.jake @@ -266,12 +266,15 @@ global.subjake = function(/*Array*/ directories, /*String*/ aTaskName) }); } -global.executableExists = function(/*String*/ aFileName) +global.executableExists = function(/*String*/ executableName) { - return SYSTEM.env["PATH"].split(':').some(function(/*String*/ aPath) - { - return FILE.exists(FILE.join(aPath, aFileName)); - }); + var paths = SYSTEM.env["PATH"].split(':'); + for (var i = 0; i < paths.length; i++) { + var path = FILE.join(paths[i], executableName); + if (FILE.exists(path)) + return path; + } + return null; } $OBJJ_TEMPLATE_EXECUTABLE = FILE.join($HOME_DIR, "Objective-J", "CommonJS", "objj-executable");