diff --git a/AppKit/AppKit.j b/AppKit/AppKit.j index def0987dd..9c820be65 100644 --- a/AppKit/AppKit.j +++ b/AppKit/AppKit.j @@ -55,6 +55,7 @@ @import "CPGeometry.j" @import "CPImage.j" @import "CPImageView.j" +@import "CPKeyBinding.j" @import "CPMenu.j" @import "CPMenuItem.j" @import "CPOpenPanel.j" @@ -76,6 +77,7 @@ @import "CPTabView.j" @import "CPTableColumn.j" @import "CPTableView.j" +@import "CPText.j" @import "CPTextField.j" @import "CPToolbar.j" @import "CPToolbarItem.j" diff --git a/AppKit/CPAlert.j b/AppKit/CPAlert.j index 97a2f1518..9f04ec545 100644 --- a/AppKit/CPAlert.j +++ b/AppKit/CPAlert.j @@ -83,6 +83,7 @@ var CPAlertWarningImage, CPPanel _alertPanel; CPTextField _messageLabel; + CPTextField _informativeLabel; CPImageView _alertImageView; CPAlertStyle _alertStyle; @@ -140,35 +141,42 @@ var CPAlertWarningImage, [_alertPanel setFloatingPanel:YES]; [_alertPanel center]; - [_messageLabel setTextColor:(styleMask & CPHUDBackgroundWindowMask) ? [CPColor whiteColor] : [CPColor blackColor]]; - var count = [_buttons count]; for(var i=0; i < count; i++) { var button = _buttons[i]; - - [button setFrameSize:CGSizeMake([button frame].size.width, (styleMask == CPHUDBackgroundWindowMask) ? 20.0 : 24.0)]; - [button setTheme:(_windowStyle === CPHUDBackgroundWindowMask) ? [CPTheme themeNamed:"Aristo-HUD"] : [CPTheme defaultTheme]]; [[_alertPanel contentView] addSubview:button]; } - + + [self _layoutButtons]; + if (!_messageLabel) { - var bounds = [[_alertPanel contentView] bounds]; - - _messageLabel = [[CPTextField alloc] initWithFrame:CGRectMake(57.0, 10.0, CGRectGetWidth(bounds) - 73.0, 62.0)]; + _messageLabel = [[CPTextField alloc] initWithFrame:CGRectMakeZero()]; [_messageLabel setFont:[CPFont boldSystemFontOfSize:13.0]]; [_messageLabel setLineBreakMode:CPLineBreakByWordWrapping]; [_messageLabel setAlignment:CPJustifiedTextAlignment]; [_messageLabel setAutoresizingMask:CPViewWidthSizable|CPViewHeightSizable]; + _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]; } /*! @@ -231,34 +239,57 @@ var CPAlertWarningImage, } /*! - Set’s the receiver’s message text, or title, to a given text. + Sets the receiver’s message text, or title, to a given text. @param messageText - Message text for the alert. */ - (void)setMessageText:(CPString)messageText { [_messageLabel setStringValue:messageText]; + [self _layoutMessage]; } -/*! - Return's the receiver's message text body. +/*! + Returns the receiver's message text body. */ - (CPString)messageText { return [_messageLabel stringValue]; } +/*! + Sets the receiver's informative text, shown below the message text. + @param informativeText - The informative text. +*/ +- (void)setInformativeText:(CPString)informativeText +{ + [_informativeLabel setStringValue:informativeText]; + // No need to call _layoutMessage - only the length of the messageText + // can affect anything there. +} + +/*! + Returns the receiver's informative text. +*/ +- (CPString)informativeText +{ + return [_informativeLabel stringValue]; +} + /*! Adds a button with a given title to the receiver. Buttons will be added starting from the right hand side of the \c CPAlert panel. The first button will have the index 0, the second button 1 and so on. + The first button will automatically be given a key equivalent of Return, + and any button titled "Cancel" will be given a key equivalent of Escape. + You really shouldn't need more than 3 buttons. */ - (void)addButtonWithTitle:(CPString)title { var bounds = [[_alertPanel contentView] bounds], - button = [[CPButton alloc] initWithFrame:CGRectMake(CGRectGetWidth(bounds) - ((_buttonCount + 1) * 90.0), CGRectGetHeight(bounds) - 34.0, 80.0, (_windowStyle == CPHUDBackgroundWindowMask) ? 20.0 : 24.0)]; - + button = [[CPButton alloc] initWithFrame:CGRectMakeZero()]; + [button setTitle:title]; [button setTarget:self]; [button setTag:_buttonCount]; @@ -270,10 +301,48 @@ var CPAlertWarningImage, [[_alertPanel contentView] addSubview:button]; if (_buttonCount == 0) - [_alertPanel setDefaultButton:button]; + [button setKeyEquivalent:CPCarriageReturnCharacter]; + else if ([title lowercaseString] === "cancel") + [button setKeyEquivalent:CPEscapeFunctionKey]; + else + [button setKeyEquivalent:nil]; _buttonCount++; [_buttons addObject:button]; + + [self _layoutButtons]; +} + +- (void)_layoutButtons +{ + 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]; + + [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)]; + } +} + +- (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)]; } /*! diff --git a/AppKit/CPApplication.j b/AppKit/CPApplication.j index 41d8d6dee..7958fc45f 100644 --- a/AppKit/CPApplication.j +++ b/AppKit/CPApplication.j @@ -325,7 +325,9 @@ CPRunContinuesResponse = -1002; applicationVersion = [options objectForKey:@"ApplicationVersion"] || [mainInfo objectForKey:@"CPBundleShortVersionString"], copyright = [options objectForKey:@"Copyright"] || [mainInfo objectForKey:@"CPHumanReadableCopyright"]; - var aboutPanelController = [[CPWindowController alloc] initWithWindowCibName:@"AboutPanel"], + var aboutPanelPath = [[CPBundle bundleForClass:[CPWindowController class]] pathForResource:@"AboutPanel.cib"], + aboutPanelController = [CPWindowController alloc], + aboutPanelController = [aboutPanelController initWithWindowCibPath:aboutPanelPath owner:aboutPanelController], aboutPanel = [aboutPanelController window], contentView = [aboutPanel contentView], imageView = [contentView viewWithTag:1], diff --git a/AppKit/CPButton.j b/AppKit/CPButton.j index 57a105bd5..3bc21844b 100644 --- a/AppKit/CPButton.j +++ b/AppKit/CPButton.j @@ -99,6 +99,9 @@ CPButtonStateMixed = CPThemeState("mixed"); // NS-style Display Properties CPBezelStyle _bezelStyle; CPControlSize _controlSize; + + CPString _keyEquivalent; + unsigned _keyEquivalentModifierMask; } + (id)buttonWithTitle:(CPString)aTitle @@ -142,6 +145,9 @@ CPButtonStateMixed = CPThemeState("mixed"); _controlSize = CPRegularControlSize; + _keyEquivalent = ""; + _keyEquivalentModifierMask = 0; + // [self setBezelStyle:CPRoundRectBezelStyle]; [self setBordered:YES]; } @@ -555,6 +561,74 @@ CPButtonStateMixed = CPThemeState("mixed"); return [self hasThemeState:CPThemeStateBordered]; } +/*! + Sets the keyboard shortcut for this button. For special keys see + CPEvent.j CP...FunctionKey and CPText.j CP...Character. + + @param aString the keyboard shortcut as a string +*/ +- (void)setKeyEquivalent:(CPString)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]; + else if ([[self window] defaultButton] === self) + [[self window] setDefaultButton:NO]; + + _keyEquivalent = aString || @""; +} + +- (void)viewWillMoveToWindow:(CPWindow)aWindow +{ + if ([[self window] defaultButton] === self) + [[self window] setDefaultButton:nil]; + + if ([self keyEquivalent] === CPNewlineCharacter || [self keyEquivalent] === CPCarriageReturnCharacter) + [aWindow setDefaultButton:self]; +} + +/*! + Returns the keyboard shortcut for this button. +*/ +- (CPString)keyEquivalent +{ + return _keyEquivalent; +} + +/*! + Returns the mask used with this button's key equivalent. +*/ +- (void)setKeyEquivalentModifierMask:(unsigned)aMask +{ + _keyEquivalentModifierMask = aMask; +} + +/*! + Sets the mask to be used with this button's key equivalent. +*/ +- (unsigned)keyEquivalentModifierMask +{ + return _keyEquivalentModifierMask; +} + +/*! + Checks the button's key equivalent against that in the event, and if they + match simulates a button click. +*/ +- (BOOL)performKeyEquivalent:(CPEvent)anEvent +{ + // Don't handle the key equivalent for the default window because the window will handle it for us + if ([[self window] defaultButton] === self) + return NO; + + if (![anEvent _triggersKeyEquivalent:[self keyEquivalent] withModifierMask:[self keyEquivalentModifierMask]]) + return NO; + + [self performClick:nil]; + return YES; +} + @end @implementation CPButton (NS) @@ -575,7 +649,9 @@ var CPButtonImageKey = @"CPButtonImageKey", CPButtonTitleKey = @"CPButtonTitleKey", CPButtonAlternateTitleKey = @"CPButtonAlternateTitleKey", CPButtonIsBorderedKey = @"CPButtonIsBorderedKey", - CPButtonImageDimsWhenDisabledKey = @"CPButtonImageDimsWhenDisabledKey"; + CPButtonImageDimsWhenDisabledKey = @"CPButtonImageDimsWhenDisabledKey", + CPButtonKeyEquivalentKey = @"CPButtonKeyEquivalentKey", + CPButtonKeyEquivalentMaskKey = @"CPButtonKeyEquivalentMaskKey"; @implementation CPButton (CPCoding) @@ -599,6 +675,11 @@ var CPButtonImageKey = @"CPButtonImageKey", [self setImageDimsWhenDisabled:[aCoder decodeObjectForKey:CPButtonImageDimsWhenDisabledKey]]; + if ([aCoder containsValueForKey:CPButtonKeyEquivalentKey]) + [self setKeyEquivalent:CFData.decodeBase64ToUtf16String([aCoder decodeObjectForKey:CPButtonKeyEquivalentKey])]; + + [self setKeyEquivalentModifierMask:[aCoder decodeObjectForKey:CPButtonKeyEquivalentMaskKey]]; + [self setNeedsLayout]; [self setNeedsDisplay:YES]; } @@ -621,6 +702,11 @@ var CPButtonImageKey = @"CPButtonImageKey", [aCoder encodeObject:_alternateTitle forKey:CPButtonAlternateTitleKey]; [aCoder encodeObject:[self imageDimsWhenDisabled] forKey:CPButtonImageDimsWhenDisabledKey]; + + if (_keyEquivalent) + [aCoder encodeObject:CFData.encodeBase64Utf16String(_keyEquivalent) forKey:CPButtonKeyEquivalentKey]; + + [aCoder encodeInt:_keyEquivalentModifierMask forKey:CPButtonKeyEquivalentMaskKey]; } @end diff --git a/AppKit/CPCollectionView.j b/AppKit/CPCollectionView.j index 11d4b2ed6..93022f119 100644 --- a/AppKit/CPCollectionView.j +++ b/AppKit/CPCollectionView.j @@ -312,8 +312,10 @@ while ((index = [_selectionIndexes indexGreaterThanIndex:index]) != CPNotFound) [_items[index] setSelected:YES]; + [[CPKeyValueBinding getBinding:@"selectionIndexes" forObject:self] reverseSetValueFor:@"selectionIndexes"]; + if ([_delegate respondsToSelector:@selector(collectionViewDidChangeSelection:)]) - [_delegate collectionViewDidChangeSelection:self] + [_delegate collectionViewDidChangeSelection:self]; } /*! @@ -737,9 +739,12 @@ @end @implementation CPCollectionView (KeyboardInteraction) -- (CPIndexSet)_selectionForEvent:(CPEvent)anEvent withNewIndex:(int)anIndex direction:(int)aDirection + +- (void)_modifySelectionWithNewIndex:(int)anIndex direction:(int)aDirection expand:(BOOL)shouldExpand { - if (_allowsMultipleSelection && [anEvent modifierFlags] & CPShiftKeyMask) + anIndex = MIN(MAX(anIndex, 0), [[self items] count]-1); + + if (_allowsMultipleSelection && shouldExpand) { var indexes = [_selectionIndexes copy], bottomAnchor = [indexes firstIndex], @@ -754,7 +759,8 @@ else indexes = [CPIndexSet indexSetWithIndex:anIndex]; - return indexes; + [self setSelectionIndexes:indexes]; + [self _scrollToSelection]; } - (void)_scrollToSelection @@ -771,26 +777,36 @@ if (index === CPNotFound) index = [[self items] count]; - index = MAX(index - 1, 0); + [self _modifySelectionWithNewIndex:index - 1 direction:-1 expand:NO]; +} - [self setSelectionIndexes:[self _selectionForEvent:[CPApp currentEvent] withNewIndex:index direction:-1]]; - [self _scrollToSelection]; +- (void)moveLeftAndModifySelection:(id)sender +{ + var index = [[self selectionIndexes] firstIndex]; + if (index === CPNotFound) + index = [[self items] count]; + + [self _modifySelectionWithNewIndex:index - 1 direction:-1 expand:YES]; } - (void)moveRight:(id)sender { - var index = MIN([[self selectionIndexes] lastIndex] + 1, [[self items] count]-1); + [self _modifySelectionWithNewIndex:[[self selectionIndexes] lastIndex] + 1 direction:1 expand:NO]; +} - [self setSelectionIndexes:[self _selectionForEvent:[CPApp currentEvent] withNewIndex:index direction:1]]; - [self _scrollToSelection]; +- (void)moveRightAndModifySelection:(id)sender +{ + [self _modifySelectionWithNewIndex:[[self selectionIndexes] lastIndex] + 1 direction:1 expand:YES]; } - (void)moveDown:(id)sender { - var index = MIN([[self selectionIndexes] lastIndex] + [self numberOfColumns], [[self items] count]-1); + [self _modifySelectionWithNewIndex:[[self selectionIndexes] lastIndex] + [self numberOfColumns] direction:1 expand:NO]; +} - [self setSelectionIndexes:[self _selectionForEvent:[CPApp currentEvent] withNewIndex:index direction:1]]; - [self _scrollToSelection]; +- (void)moveDownAndModifySelection:(id)sender +{ + [self _modifySelectionWithNewIndex:[[self selectionIndexes] lastIndex] + [self numberOfColumns] direction:1 expand:YES]; } - (void)moveUp:(id)sender @@ -799,10 +815,16 @@ if (index == CPNotFound) index = [[self items] count]; - index = MAX(0, index - [self numberOfColumns]); + [self _modifySelectionWithNewIndex:index - [self numberOfColumns] direction:-1 expand:NO]; +} - [self setSelectionIndexes:[self _selectionForEvent:[CPApp currentEvent] withNewIndex:index direction:-1]]; - [self _scrollToSelection]; +- (void)moveUpAndModifySelection:(id)sender +{ + var index = [[self selectionIndexes] firstIndex]; + if (index == CPNotFound) + index = [[self items] count]; + + [self _modifySelectionWithNewIndex:index - [self numberOfColumns] direction:-1 expand:YES]; } - (void)deleteBackward:(id)sender @@ -847,11 +869,13 @@ @end -var CPCollectionViewMinItemSizeKey = @"CPCollectionViewMinItemSizeKey", - CPCollectionViewMaxItemSizeKey = @"CPCollectionViewMaxItemSizeKey", - CPCollectionViewVerticalMarginKey = @"CPCollectionViewVerticalMarginKey", - CPCollectionViewSelectableKey = @"CPCollectionViewSelectableKey", - CPCollectionViewBackgroundColorsKey = @"CPCollectionViewBackgroundColorsKey"; +var CPCollectionViewMinItemSizeKey = @"CPCollectionViewMinItemSizeKey", + CPCollectionViewMaxItemSizeKey = @"CPCollectionViewMaxItemSizeKey", + CPCollectionViewVerticalMarginKey = @"CPCollectionViewVerticalMarginKey", + CPCollectionViewMaxNumberOfRowsKey = @"CPCollectionViewMaxNumberOfRowsKey", + CPCollectionViewMaxNumberOfColumnsKey = @"CPCollectionViewMaxNumberOfColumnsKey", + CPCollectionViewSelectableKey = @"CPCollectionViewSelectableKey", + CPCollectionViewBackgroundColorsKey = @"CPCollectionViewBackgroundColorsKey"; @implementation CPCollectionView (CPCoding) @@ -872,6 +896,9 @@ var CPCollectionViewMinItemSizeKey = @"CPCollectionViewMinItemSizeKey", _minItemSize = [aCoder decodeSizeForKey:CPCollectionViewMinItemSizeKey] || CGSizeMakeZero(); _maxItemSize = [aCoder decodeSizeForKey:CPCollectionViewMaxItemSizeKey] || CGSizeMakeZero(); + _maxNumberOfRows = [aCoder decodeIntForKey:CPCollectionViewMaxNumberOfRowsKey] || 0; + _maxNumberOfColumns = [aCoder decodeIntForKey:CPCollectionViewMaxNumberOfColumnsKey] || 0; + _verticalMargin = [aCoder decodeFloatForKey:CPCollectionViewVerticalMarginKey]; _isSelectable = [aCoder decodeBoolForKey:CPCollectionViewSelectableKey]; @@ -898,6 +925,9 @@ var CPCollectionViewMinItemSizeKey = @"CPCollectionViewMinItemSizeKey", if (!CGSizeEqualToSize(_maxItemSize, CGSizeMakeZero())) [aCoder encodeSize:_maxItemSize forKey:CPCollectionViewMaxItemSizeKey]; + [aCoder encodeInt:_maxNumberOfRows forKey:CPCollectionViewMaxNumberOfRowsKey]; + [aCoder encodeInt:_maxNumberOfColumns forKey:CPCollectionViewMaxNumberOfColumnsKey]; + [aCoder encodeBool:_isSelectable forKey:CPCollectionViewSelectableKey]; [aCoder encodeFloat:_verticalMargin forKey:CPCollectionViewVerticalMarginKey]; diff --git a/AppKit/CPColor.j b/AppKit/CPColor.j index b6eda360c..3203257bf 100644 --- a/AppKit/CPColor.j +++ b/AppKit/CPColor.j @@ -422,7 +422,7 @@ var cachedBlackColor, parseInt(parts[1], 10) / 255.0, parseInt(parts[2], 10) / 255.0, parts[3] ? parseInt(parts[3], 10) / 255.0 : 1.0 - ] + ]; _cssString = aString; diff --git a/AppKit/CPCursor.j b/AppKit/CPCursor.j index 45b3f1ab8..d550fb10c 100755 --- a/AppKit/CPCursor.j +++ b/AppKit/CPCursor.j @@ -197,7 +197,7 @@ var currentCursor = nil, + (void)unhide { - [self _setCursorCSS:[currentCursor _cssString]] + [self _setCursorCSS:[currentCursor _cssString]]; } + (void)setHiddenUntilMouseMoves:(BOOL)flag diff --git a/AppKit/CPEvent.j b/AppKit/CPEvent.j index a60a05114..6c05c96d6 100644 --- a/AppKit/CPEvent.j +++ b/AppKit/CPEvent.j @@ -21,6 +21,7 @@ */ @import +@import "CPText.j" #include "CoreGraphics/CGGeometry.h" @@ -41,7 +42,7 @@ CPAppKitDefined = 13; CPSystemDefined = 14; CPApplicationDefined = 15; CPPeriodic = 16; -CPCursorUpdate = 17; +CPCursorUpdate = 17; CPScrollWheel = 22; CPOtherMouseDown = 25; CPOtherMouseUp = 26; @@ -52,8 +53,7 @@ CPTouchStart = 28; CPTouchMove = 29; CPTouchEnd = 30; CPTouchCancel = 31; - - + CPAlphaShiftKeyMask = 1 << 16; CPShiftKeyMask = 1 << 17; CPControlKeyMask = 1 << 18; @@ -87,26 +87,102 @@ CPPeriodicMask = 1 << CPPeriodic; CPScrollWheelMask = 1 << CPScrollWheel; CPAnyEventMask = 0xffffffff; -CPDOMEventDoubleClick = "dblclick", -CPDOMEventMouseDown = "mousedown", -CPDOMEventMouseUp = "mouseup", -CPDOMEventMouseMoved = "mousemove", -CPDOMEventMouseDragged = "mousedrag", -CPDOMEventKeyUp = "keyup", -CPDOMEventKeyDown = "keydown", -CPDOMEventKeyPress = "keypress"; -CPDOMEventCopy = "copy"; -CPDOMEventPaste = "paste"; -CPDOMEventScrollWheel = "mousewheel"; -CPDOMEventTouchStart = "touchstart"; -CPDOMEventTouchMove = "touchmove"; -CPDOMEventTouchEnd = "touchend"; -CPDOMEventTouchCancel = "touchcancel"; +CPUpArrowFunctionKey = "\uF700"; +CPDownArrowFunctionKey = "\uF701"; +CPLeftArrowFunctionKey = "\uF702"; +CPRightArrowFunctionKey = "\uF703"; +CPF1FunctionKey = "\uF704"; +CPF2FunctionKey = "\uF705"; +CPF3FunctionKey = "\uF706"; +CPF4FunctionKey = "\uF707"; +CPF5FunctionKey = "\uF708"; +CPF6FunctionKey = "\uF709"; +CPF7FunctionKey = "\uF70A"; +CPF8FunctionKey = "\uF70B"; +CPF9FunctionKey = "\uF70C"; +CPF10FunctionKey = "\uF70D"; +CPF11FunctionKey = "\uF70E"; +CPF12FunctionKey = "\uF70F"; +CPF13FunctionKey = "\uF710"; +CPF14FunctionKey = "\uF711"; +CPF15FunctionKey = "\uF712"; +CPF16FunctionKey = "\uF713"; +CPF17FunctionKey = "\uF714"; +CPF18FunctionKey = "\uF715"; +CPF19FunctionKey = "\uF716"; +CPF20FunctionKey = "\uF717"; +CPF21FunctionKey = "\uF718"; +CPF22FunctionKey = "\uF719"; +CPF23FunctionKey = "\uF71A"; +CPF24FunctionKey = "\uF71B"; +CPF25FunctionKey = "\uF71C"; +CPF26FunctionKey = "\uF71D"; +CPF27FunctionKey = "\uF71E"; +CPF28FunctionKey = "\uF71F"; +CPF29FunctionKey = "\uF720"; +CPF30FunctionKey = "\uF721"; +CPF31FunctionKey = "\uF722"; +CPF32FunctionKey = "\uF723"; +CPF33FunctionKey = "\uF724"; +CPF34FunctionKey = "\uF725"; +CPF35FunctionKey = "\uF726"; +CPInsertFunctionKey = "\uF727"; +CPDeleteFunctionKey = "\uF728"; +CPHomeFunctionKey = "\uF729"; +CPBeginFunctionKey = "\uF72A"; +CPEndFunctionKey = "\uF72B"; +CPPageUpFunctionKey = "\uF72C"; +CPPageDownFunctionKey = "\uF72D"; +CPPrintScreenFunctionKey = "\uF72E"; +CPScrollLockFunctionKey = "\uF72F"; +CPPauseFunctionKey = "\uF730"; +CPSysReqFunctionKey = "\uF731"; +CPBreakFunctionKey = "\uF732"; +CPResetFunctionKey = "\uF733"; +CPStopFunctionKey = "\uF734"; +CPMenuFunctionKey = "\uF735"; +CPUserFunctionKey = "\uF736"; +CPSystemFunctionKey = "\uF737"; +CPPrintFunctionKey = "\uF738"; +CPClearLineFunctionKey = "\uF739"; +CPClearDisplayFunctionKey = "\uF73A"; +CPInsertLineFunctionKey = "\uF73B"; +CPDeleteLineFunctionKey = "\uF73C"; +CPInsertCharFunctionKey = "\uF73D"; +CPDeleteCharFunctionKey = "\uF73E"; +CPPrevFunctionKey = "\uF73F"; +CPNextFunctionKey = "\uF740"; +CPSelectFunctionKey = "\uF741"; +CPExecuteFunctionKey = "\uF742"; +CPUndoFunctionKey = "\uF743"; +CPRedoFunctionKey = "\uF744"; +CPFindFunctionKey = "\uF745"; +CPHelpFunctionKey = "\uF746"; +CPModeSwitchFunctionKey = "\uF747"; +CPEscapeFunctionKey = "\u001B"; + + +CPDOMEventDoubleClick = "dblclick", +CPDOMEventMouseDown = "mousedown", +CPDOMEventMouseUp = "mouseup", +CPDOMEventMouseMoved = "mousemove", +CPDOMEventMouseDragged = "mousedrag", +CPDOMEventKeyUp = "keyup", +CPDOMEventKeyDown = "keydown", +CPDOMEventKeyPress = "keypress"; +CPDOMEventCopy = "copy"; +CPDOMEventPaste = "paste"; +CPDOMEventScrollWheel = "mousewheel"; +CPDOMEventTouchStart = "touchstart"; +CPDOMEventTouchMove = "touchmove"; +CPDOMEventTouchEnd = "touchend"; +CPDOMEventTouchCancel = "touchcancel"; var _CPEventPeriodicEventPeriod = 0, - _CPEventPeriodicEventTimer = nil; + _CPEventPeriodicEventTimer = nil, + _CPEventUpperCaseRegex = new RegExp("[A-Z]"); -/*! +/*! @ingroup appkit @class CPEvent CPEvent encapsulates the details of a Cappuccino keyboard or mouse event. @@ -155,7 +231,7 @@ var _CPEventPeriodicEventPeriod = 0, characters:(CPString)characters charactersIgnoringModifiers:(CPString)unmodCharacters isARepeat:(BOOL)repeatKey keyCode:(unsigned short)code { return [[self alloc] _initKeyEventWithType:anEventType location:aPoint modifierFlags:modifierFlags - timestamp:aTimestamp windowNumber:aWindowNumber context:aGraphicsContext + timestamp:aTimestamp windowNumber:aWindowNumber context:aGraphicsContext characters:characters charactersIgnoringModifiers:unmodCharacters isARepeat:repeatKey keyCode:code]; } @@ -173,7 +249,7 @@ var _CPEventPeriodicEventPeriod = 0, @throws CPInternalInconsistencyException if an invalid event type is provided @return the new mouse event */ -+ (id)mouseEventWithType:(CPEventType)anEventType location:(CGPoint)aPoint modifierFlags:(unsigned)modifierFlags ++ (id)mouseEventWithType:(CPEventType)anEventType location:(CGPoint)aPoint modifierFlags:(unsigned)modifierFlags timestamp:(CPTimeInterval)aTimestamp windowNumber:(int)aWindowNumber context:(CPGraphicsContext)aGraphicsContext eventNumber:(int)anEventNumber clickCount:(int)aClickCount pressure:(float)aPressure { @@ -204,12 +280,12 @@ var _CPEventPeriodicEventPeriod = 0, } /* @ignore */ -- (id)_initMouseEventWithType:(CPEventType)anEventType location:(CPPoint)aPoint modifierFlags:(unsigned)modifierFlags +- (id)_initMouseEventWithType:(CPEventType)anEventType location:(CPPoint)aPoint modifierFlags:(unsigned)modifierFlags timestamp:(CPTimeInterval)aTimestamp windowNumber:(int)aWindowNumber context:(CPGraphicsContext)aGraphicsContext eventNumber:(int)anEventNumber clickCount:(int)aClickCount pressure:(float)aPressure { self = [super init]; - + if (self) { _type = anEventType; @@ -222,7 +298,7 @@ var _CPEventPeriodicEventPeriod = 0, _pressure = aPressure; _window = [CPApp windowWithWindowNumber:aWindowNumber]; } - + return self; } @@ -232,7 +308,7 @@ var _CPEventPeriodicEventPeriod = 0, characters:(CPString)characters charactersIgnoringModifiers:(CPString)unmodCharacters isARepeat:(BOOL)isARepeat keyCode:(unsigned short)code { self = [super init]; - + if (self) { _type = anEventType; @@ -246,7 +322,7 @@ var _CPEventPeriodicEventPeriod = 0, _keyCode = code; _windowNumber = aWindowNumber; } - + return self; } @@ -256,7 +332,7 @@ var _CPEventPeriodicEventPeriod = 0, subtype:(short)aSubtype data1:(int)aData1 data2:(int)aData2 { self = [super init]; - + if (self) { _type = anEventType; @@ -267,7 +343,7 @@ var _CPEventPeriodicEventPeriod = 0, _subtype = aSubtype; _data1 = aData1; _data2 = aData2; - } + } return self; } @@ -445,23 +521,76 @@ var _CPEventPeriodicEventPeriod = 0, return _deltaZ; } +- (BOOL)_triggersKeyEquivalent:(CPString)aKeyEquivalent withModifierMask:aKeyEquivalentModifierMask +{ + if (!aKeyEquivalent) + return NO; + + if (_CPEventUpperCaseRegex.test(aKeyEquivalent)) + aKeyEquivalentModifierMask |= CPShiftKeyMask; + + if (CPBrowserIsOperatingSystem(CPWindowsOperatingSystem) && (aKeyEquivalentModifierMask & CPCommandKeyMask)) + { + aKeyEquivalentModifierMask |= CPControlKeyMask; + aKeyEquivalentModifierMask &= ~CPCommandKeyMask; + } + + if ((_modifierFlags & (CPShiftKeyMask | CPAlternateKeyMask | CPCommandKeyMask | CPControlKeyMask)) !== aKeyEquivalentModifierMask) + return NO; + + // Treat \r and \n as the same key equivalent. See issue #710. + if (_characters === CPNewlineCharacter || _characters === CPCarriageReturnCharacter) + return CPNewlineCharacter === aKeyEquivalent || CPCarriageReturnCharacter === aKeyEquivalent; + + return [_characters caseInsensitiveCompare:aKeyEquivalent] === CPOrderedSame; +} + - (BOOL)_couldBeKeyEquivalent { + if (_type !== CPKeyDown) + return NO; + + var characterCount = _characters.length; + + if (!characterCount) + return NO; + + if (_modifierFlags & (CPCommandKeyMask | CPControlKeyMask)) + return YES; + + for(var i=0; i 0; + return NO; } /*! - Generates periodic events every \c aPeriod seconds. + Gene rates periodic events every \c aPeriod seconds. @param aDelay the number of seconds before the first event @param aPeriod the length of time in seconds between successive events */ + (void)startPeriodicEventsAfterDelay:(CPTimeInterval)aDelay withPeriod:(CPTimeInterval)aPeriod { _CPEventPeriodicEventPeriod = aPeriod; - + // FIXME: OH TIMERS!!! _CPEventPeriodicEventTimer = window.setTimeout(function() { _CPEventPeriodicEventTimer = window.setInterval(_CPEventFirePeriodEvent, aPeriod * 1000.0); }, aDelay * 1000.0); } @@ -473,9 +602,9 @@ var _CPEventPeriodicEventPeriod = 0, { if (_CPEventPeriodicEventTimer === nil) return; - + window.clearTimeout(_CPEventPeriodicEventTimer); - + _CPEventPeriodicEventTimer = nil; } diff --git a/AppKit/CPImage.j b/AppKit/CPImage.j index 0bc69ba60..53a3f1937 100644 --- a/AppKit/CPImage.j +++ b/AppKit/CPImage.j @@ -193,6 +193,9 @@ function CPAppKitImage(aFilename, aSize) var imageOrSize = AppKitImageForNames[aName]; + if (!imageOrSize) + return nil; + if (!imageOrSize.isa) { imageOrSize = CPAppKitImage("CPImage/" + aName + ".png", imageOrSize); @@ -205,17 +208,19 @@ function CPAppKitImage(aFilename, aSize) return imageOrSize; } -- (void)setName:(CPString)aName +- (BOOL)setName:(CPString)aName { if (_name === aName) - return; + return YES; - if (imagesForNames[aName] === self) - imagesForNames[aName] = nil; + if (imagesForNames[aName]) + return NO; _name = aName; imagesForNames[aName] = self; + + return YES; } - (CPString)name diff --git a/AppKit/CPImageView.j b/AppKit/CPImageView.j index 7d365f71f..017b59ad3 100644 --- a/AppKit/CPImageView.j +++ b/AppKit/CPImageView.j @@ -35,6 +35,16 @@ CPScaleProportionally = 0; CPScaleToFit = 1; CPScaleNone = 2; +CPImageAlignCenter = 0; +CPImageAlignTop = 1; +CPImageAlignTopLeft = 2; +CPImageAlignTopRight = 3; +CPImageAlignLeft = 4; +CPImageAlignBottom = 5; +CPImageAlignBottomLeft = 6; +CPImageAlignBottomRight = 7; +CPImageAlignRight = 8; + var CPImageViewShadowBackgroundColor = nil; var LEFT_SHADOW_INSET = 3.0, @@ -52,14 +62,15 @@ var LEFT_SHADOW_INSET = 3.0, */ @implementation CPImageView : CPControl { - DOMElement _DOMImageElement; + DOMElement _DOMImageElement; - BOOL _hasShadow; - CPView _shadowView; + BOOL _hasShadow; + CPView _shadowView; - BOOL _isEditable; + BOOL _isEditable; - CGRect _imageRect; + CGRect _imageRect; + CPImageAlignment _imageAlignment; } - (id)initWithFrame:(CGRect)aFrame @@ -191,6 +202,30 @@ var LEFT_SHADOW_INSET = 3.0, [self hideOrDisplayContents]; } +/*! + Sets the type of image alignment that should be used to + render the image. + @param anImageAlignment the type of scaling to use +*/ +- (void)setImageAlignment:(CPImageAlignment)anImageAlignment +{ + if (_imageAlignment == anImageAlignment) + return; + + _imageAlignment = anImageAlignment; + + if (![self image]) + return; + + [self setNeedsLayout]; + [self setNeedsDisplay:YES]; +} + +- (unsigned)imageAlignment +{ + return _imageAlignment; +} + /*! Sets the type of image scaling that should be used to render the image. @@ -318,8 +353,45 @@ var LEFT_SHADOW_INSET = 3.0, #endif } - var x = (boundsWidth - width) / 2.0, - y = (boundsHeight - height) / 2.0; + var x, y; + + switch (_imageAlignment) + { + case CPImageAlignLeft: + case CPImageAlignTopLeft: + case CPImageAlignBottomLeft: + x = 0.0; + break; + + case CPImageAlignRight: + case CPImageAlignTopRight: + case CPImageAlignBottomRight: + x = boundsWidth - width; + break; + + default: + x = (boundsWidth - width) / 2.0; + break; + } + + switch (_imageAlignment) + { + case CPImageAlignTop: + case CPImageAlignTopLeft: + case CPImageAlignTopRight: + y = 0.0; + break; + + case CPImageAlignBottom: + case CPImageAlignBottomLeft: + case CPImageAlignBottomRight: + y = boundsHeight - height; + break; + + default: + y = (boundsHeight - height) / 2.0; + break; + } #if PLATFORM(DOM) CPDOMDisplayServerSetStyleLeftTop(_DOMImageElement, NULL, x, y); @@ -380,10 +452,11 @@ var LEFT_SHADOW_INSET = 3.0, @end -var CPImageViewImageKey = @"CPImageViewImageKey", - CPImageViewImageScalingKey = @"CPImageViewImageScalingKey", - CPImageViewHasShadowKey = @"CPImageViewHasShadowKey", - CPImageViewIsEditableKey = @"CPImageViewIsEditableKey"; +var CPImageViewImageKey = @"CPImageViewImageKey", + CPImageViewImageScalingKey = @"CPImageViewImageScalingKey", + CPImageViewImageAlignmentKey = @"CPImageViewImageAlignmentKey", + CPImageViewHasShadowKey = @"CPImageViewHasShadowKey", + CPImageViewIsEditableKey = @"CPImageViewIsEditableKey"; @implementation CPImageView (CPCoding) @@ -416,6 +489,7 @@ var CPImageViewImageKey = @"CPImageViewImageKey", #endif [self setHasShadow:[aCoder decodeBoolForKey:CPImageViewHasShadowKey]]; + [self setImageAlignment:[aCoder decodeIntForKey:CPImageViewImageAlignmentKey]]; if ([aCoder decodeBoolForKey:CPImageViewIsEditableKey] || NO) [self setEditable:YES]; @@ -450,6 +524,7 @@ var CPImageViewImageKey = @"CPImageViewImageKey", _subviews = actualSubviews; [aCoder encodeBool:_hasShadow forKey:CPImageViewHasShadowKey]; + [aCoder encodeInt:_imageAlignment forKey:CPImageViewImageAlignmentKey]; if (_isEditable) [aCoder encodeBool:_isEditable forKey:CPImageViewIsEditableKey]; diff --git a/AppKit/CPKeyBinding.j b/AppKit/CPKeyBinding.j new file mode 100644 index 000000000..5cc46b422 --- /dev/null +++ b/AppKit/CPKeyBinding.j @@ -0,0 +1,255 @@ +/* + * CPKeyBinding.j + * AppKit + * + * Created by Nicholas Small. + * Copyright 2010, 280 North, Inc. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +@import + + +CPStandardKeyBindings = { + @"@.": @"cancelOperation:", + + @"^a": @"moveToBeginningOfParagraph:", + @"^$a": @"moveToBeginningOfParagraphAndModifySelection:", + @"^b": @"moveBackward:", + @"^$b": @"moveBackwardAndModifySelection:", + @"^~b": @"moveWordBackward:", + @"^~$b": @"moveWordBackwardAndModifySelection:", + @"^d": @"deleteForward:", + @"^e": @"moveToEndOfParagraph:", + @"^$e": @"moveToEndOfParagraphAndModifySelection:", + @"^f": @"moveForward:", + @"^$f": @"moveForwardAndModifySelection:", + @"^~f": @"moveWordForward:", + @"^~$f": @"moveWordForwardAndModifySelection:", + @"^h": @"deleteBackward:", + @"^k": @"deleteToEndOfParagraph:", + @"^l": @"centerSelectionInVisibleArea:", + @"^n": @"moveDown:", + @"^$n": @"moveDownAndModifySelection:", + @"^o": [@"insertNewlineIgnoringFieldEditor:", @"moveBackward:"], + @"^p": @"moveUp:", + @"^$p": @"moveUpAndModifySelection:", + @"^t": @"transpose:", + @"^v": @"pageDown:", + @"^$v": @"pageDownAndModifySelection:", + @"^y": @"yank:" +}; + +CPStandardKeyBindings[CPNewlineCharacter] = @"insertNewline:"; +CPStandardKeyBindings[CPCarriageReturnCharacter] = @"insertNewline:"; +CPStandardKeyBindings[CPEnterCharacter] = @"insertNewline:"; +CPStandardKeyBindings[@"~" + CPNewlineCharacter] = @"insertNewlineIgnoringFieldEditor:"; +CPStandardKeyBindings[@"~" + CPCarriageReturnCharacter] = @"insertNewlineIgnoringFieldEditor:"; +CPStandardKeyBindings[@"~" + CPEnterCharacter] = @"insertNewlineIgnoringFieldEditor:"; +CPStandardKeyBindings[@"^" + CPNewlineCharacter] = @"insertLineBreak:"; +CPStandardKeyBindings[@"^" + CPCarriageReturnCharacter] = @"insertLineBreak:"; +CPStandardKeyBindings[@"^" + CPEnterCharacter] = @"insertLineBreak:"; + +CPStandardKeyBindings[CPBackspaceCharacter] = @"deleteBackward:"; +CPStandardKeyBindings[@"~" + CPBackspaceCharacter] = @"deleteWordBackward:"; +CPStandardKeyBindings[CPDeleteCharacter] = @"deleteBackward:"; +CPStandardKeyBindings[@"@" + CPDeleteCharacter] = @"deleteToBeginningOfLine:"; +CPStandardKeyBindings[@"~" + CPDeleteCharacter] = @"deleteWordBackward:"; +CPStandardKeyBindings[@"^" + CPDeleteCharacter] = @"deleteBackwardByDecomposingPreviousCharacter:"; +CPStandardKeyBindings[@"^~" + CPDeleteCharacter] = @"deleteWordBackward:"; + +CPStandardKeyBindings[CPDeleteFunctionKey] = @"deleteForward:"; +CPStandardKeyBindings[@"~" + CPDeleteFunctionKey] = @"deleteWordForward:"; + +CPStandardKeyBindings[CPTabCharacter] = @"insertTab:"; +CPStandardKeyBindings[@"~" + CPTabCharacter] = @"insertTabIgnoringFieldEditor:"; +CPStandardKeyBindings[@"^" + CPTabCharacter] = @"selectNextKeyView:"; +CPStandardKeyBindings[CPBackTabCharacter] = @"insertBacktab:"; +CPStandardKeyBindings[@"^" + CPBackTabCharacter] = @"selectPreviousKeyView:"; + +CPStandardKeyBindings[CPEscapeFunctionKey] = @"cancelOperation:"; +CPStandardKeyBindings[@"~" + CPEscapeFunctionKey] = @"complete:"; +CPStandardKeyBindings[CPF5FunctionKey] = @"complete:"; + +CPStandardKeyBindings[CPLeftArrowFunctionKey] = @"moveLeft:"; +CPStandardKeyBindings[@"~" + CPLeftArrowFunctionKey] = @"moveWordLeft:"; +CPStandardKeyBindings[@"^" + CPLeftArrowFunctionKey] = @"moveToLeftEndOfLine:"; +CPStandardKeyBindings[@"@" + CPLeftArrowFunctionKey] = @"moveToLeftEndOfLine:"; +CPStandardKeyBindings[@"$" + CPLeftArrowFunctionKey] = @"moveLeftAndModifySelection:"; +CPStandardKeyBindings[@"$~" + CPLeftArrowFunctionKey] = @"moveWordLeftAndModifySelection:"; +CPStandardKeyBindings[@"$^" + CPLeftArrowFunctionKey] = @"moveToLeftEndOfLineAndModifySelection:"; +CPStandardKeyBindings[@"$@" + CPLeftArrowFunctionKey] = @"moveToLeftEndOfLineAndModifySelection:"; +CPStandardKeyBindings[@"@^" + CPLeftArrowFunctionKey] = @"makeBaseWritingDirectionRightToLeft:"; +CPStandardKeyBindings[@"@^~" + CPLeftArrowFunctionKey] = @"makeTextWritingDirectionRightToLeft:"; + +CPStandardKeyBindings[CPRightArrowFunctionKey] = @"moveRight:"; +CPStandardKeyBindings[@"~" + CPRightArrowFunctionKey] = @"moveWordRight:"; +CPStandardKeyBindings[@"^" + CPRightArrowFunctionKey] = @"moveToRightEndOfLine:"; +CPStandardKeyBindings[@"@" + CPRightArrowFunctionKey] = @"moveToRightEndOfLine:"; +CPStandardKeyBindings[@"$" + CPRightArrowFunctionKey] = @"moveRightAndModifySelection:"; +CPStandardKeyBindings[@"$~" + CPRightArrowFunctionKey] = @"moveWordRightAndModifySelection:"; +CPStandardKeyBindings[@"$^" + CPRightArrowFunctionKey] = @"moveToRightEndOfLineAndModifySelection:"; +CPStandardKeyBindings[@"$@" + CPRightArrowFunctionKey] = @"moveToRightEndOfLineAndModifySelection:"; +CPStandardKeyBindings[@"@^" + CPRightArrowFunctionKey] = @"makeBaseWritingDirectionLeftToRight:"; +CPStandardKeyBindings[@"@^~" + CPRightArrowFunctionKey] = @"makeTextWritingDirectionLeftToRight:"; + +CPStandardKeyBindings[CPUpArrowFunctionKey] = @"moveUp:"; +CPStandardKeyBindings[@"~" + CPUpArrowFunctionKey] = [@"moveBackward:", @"moveToBeginningOfParagraph:"]; +CPStandardKeyBindings[@"^" + CPUpArrowFunctionKey] = @"scrollPageUp:"; +CPStandardKeyBindings[@"@" + CPUpArrowFunctionKey] = @"moveToBeginningOfDocument:"; +CPStandardKeyBindings[@"$" + CPUpArrowFunctionKey] = @"moveUpAndModifySelection:"; +CPStandardKeyBindings[@"$~" + CPUpArrowFunctionKey] = @"moveParagraphBackwardAndModifySelection:"; +CPStandardKeyBindings[@"$@" + CPUpArrowFunctionKey] = @"moveToBeginningOfDocumentAndModifySelection:"; + +CPStandardKeyBindings[CPDownArrowFunctionKey] = @"moveDown:"; +CPStandardKeyBindings[@"~" + CPDownArrowFunctionKey] = [@"moveForward:", @"moveToEndOfParagraph:"]; +CPStandardKeyBindings[@"^" + CPDownArrowFunctionKey] = @"scrollPageDown:"; +CPStandardKeyBindings[@"@" + CPDownArrowFunctionKey] = @"moveToEndOfDocument:"; +CPStandardKeyBindings[@"$" + CPDownArrowFunctionKey] = @"moveDownAndModifySelection:"; +CPStandardKeyBindings[@"$~" + CPDownArrowFunctionKey] = @"moveParagraphForwardAndModifySelection:"; +CPStandardKeyBindings[@"$@" + CPDownArrowFunctionKey] = @"moveToEndOfDocumentAndModifySelection:"; +CPStandardKeyBindings[@"@^" + CPDownArrowFunctionKey] = @"makeBaseWritingDirectionNatural:"; +CPStandardKeyBindings[@"@^~" + CPDownArrowFunctionKey] = @"makeTextWritingDirectionNatural:"; + +CPStandardKeyBindings[CPHomeFunctionKey] = @"scrollToBeginningOfDocument:"; +CPStandardKeyBindings[@"$" + CPHomeFunctionKey] = @"moveToBeginningOfDocumentAndModifySelection:"; +CPStandardKeyBindings[CPEndFunctionKey] = @"scrollToEndOfDocument:"; +CPStandardKeyBindings[@"$" + CPEndFunctionKey] = @"moveToEndOfDocumentAndModifySelection:"; + +CPStandardKeyBindings[CPPageUpFunctionKey] = @"scrollPageUp:"; +CPStandardKeyBindings[@"~" + CPPageUpFunctionKey] = @"pageUp:"; +CPStandardKeyBindings[@"$" + CPPageUpFunctionKey] = @"pageUpAndModifySelection:"; +CPStandardKeyBindings[CPPageDownFunctionKey] = @"scrollPageDown:"; +CPStandardKeyBindings[@"~" + CPPageDownFunctionKey] = @"pageDown:"; +CPStandardKeyBindings[@"$" + CPPageDownFunctionKey] = @"pageDownAndModifySelection:"; + +var CPKeyBindingCache = {}; + +@implementation CPKeyBinding : CPObject +{ + CPString _key; + unsigned _modifierFlags; + + CPArray _selectors; + + CPString _cacheName; +} + ++ (void)initialize +{ + if ([self class] !== CPKeyBinding) + return; + + [self createKeyBindingsFromJSObject:CPStandardKeyBindings]; +} + ++ (void)createKeyBindingsFromJSObject:(JSObject)anObject +{ + var binding; + for (binding in anObject) + { + var components = binding.split(@""), + modifierFlags = ([components containsObject:@"$"] ? CPShiftKeyMask : 0) | + ([components containsObject:@"^"] ? CPControlKeyMask : 0) | + ([components containsObject:@"~"] ? CPAlternateKeyMask : 0) | + ([components containsObject:@"@"] ? CPCommandKeyMask : 0); + + var selectors = anObject[binding]; + if (![selectors isKindOfClass:CPArray]) + selectors = [selectors]; + + var keyBinding = [[self alloc] initWithKey:[components lastObject] modifierFlags:modifierFlags selectors:selectors]; + [self cacheKeyBinding:keyBinding]; + } +} + ++ (void)cacheKeyBinding:(CPKeyBinding)aBinding +{ + if (!aBinding) + return; + + CPKeyBindingCache[[aBinding _cacheName]] = aBinding; +} + ++ (CPKeyBinding)keyBindingForKey:(CPString)aKey modifierFlags:(unsigned)aFlag +{ + var tempBinding = [[self alloc] initWithKey:aKey modifierFlags:aFlag selectors:nil]; + return CPKeyBindingCache[[tempBinding _cacheName]]; +} + ++ (CPArray)selectorsForKey:(CPString)aKey modifierFlags:(unsigned)aFlag +{ + return [[self keyBindingForKey:aKey modifierFlags:aFlag] selectors]; +} + +- (id)initWithKey:(CPString)aKey modifierFlags:(unsigned)aFlag selectors:(CPArray)selectors +{ + self = [super init]; + + if (self) + { + _key = aKey; + _modifierFlags = aFlag; + + _selectors = selectors; + + // We normalize our key binding string in order to properly cache it. + // We want to ensure the modifiers are always in the same order. + var cacheName = []; + + if (_modifierFlags & CPCommandKeyMask) + cacheName.push(@"@"); + if (_modifierFlags & CPControlKeyMask) + cacheName.push(@"^"); + if (_modifierFlags & CPAlternateKeyMask) + cacheName.push(@"~"); + if (_modifierFlags & CPShiftKeyMask) + cacheName.push(@"$"); + + cacheName.push(_key); + + _cacheName = cacheName.join(@""); + } + + return self; +} + +- (CPString)key +{ + return _key; +} + +- (unsigned)modifierFlags +{ + return _modifierFlags; +} + +- (CPArray)selectors +{ + return _selectors; +} + +- (CPString)_cacheName +{ + return _cacheName; +} + +- (BOOL)isEqual:(CPKeyBinding)rhs +{ + return _key === [rhs key] && _modifierFlags === [rhs modifierFlags]; +} + +@end diff --git a/AppKit/CPKeyValueBinding.j b/AppKit/CPKeyValueBinding.j index 09ae8a59e..3aabc98a6 100644 --- a/AppKit/CPKeyValueBinding.j +++ b/AppKit/CPKeyValueBinding.j @@ -108,7 +108,7 @@ var CPBindingOperationAnd = 0, count = allKeys.length; while (count--) - [anObject unbind:[bindings objectForKey:allKeys[count]]] + [anObject unbind:[bindings objectForKey:allKeys[count]]]; [bindingsMap removeObjectForKey:[anObject hash]]; } diff --git a/AppKit/CPMenu/CPMenu.j b/AppKit/CPMenu/CPMenu.j index 687fb7045..39203dfa7 100644 --- a/AppKit/CPMenu/CPMenu.j +++ b/AppKit/CPMenu/CPMenu.j @@ -50,11 +50,11 @@ var _CPMenuBarVisible = NO, _CPMenuBarAttributes = nil, _CPMenuBarSharedWindow = nil; -/*! +/*! @ingroup appkit @class CPMenu - Menus provide the user with a list of actions and/or submenus. Submenus themselves are full fledged menus + Menus provide the user with a list of actions and/or submenus. Submenus themselves are full fledged menus and so a heirarchical structure appears. */ @implementation CPMenu : CPObject @@ -69,12 +69,12 @@ var _CPMenuBarVisible = NO, float _minimumWidth; CPMutableArray _items; - + BOOL _autoenablesItems; BOOL _showsStateColumn; id _delegate; - + CPMenuItem _highlightedIndex; _CPMenuWindow _menuWindow; } @@ -95,7 +95,7 @@ var _CPMenuBarVisible = NO, { if (_CPMenuBarVisible === menuBarShouldBeVisible) return; - + _CPMenuBarVisible = menuBarShouldBeVisible; if ([CPPlatform supportsNativeMainMenu]) @@ -105,13 +105,13 @@ var _CPMenuBarVisible = NO, { if (!_CPMenuBarSharedWindow) _CPMenuBarSharedWindow = [[_CPMenuBarWindow alloc] init]; - + [_CPMenuBarSharedWindow setMenu:[CPApp mainMenu]]; - + [_CPMenuBarSharedWindow setTitle:_CPMenuBarTitle]; [_CPMenuBarSharedWindow setIconImage:_CPMenuBarIconImage]; [_CPMenuBarSharedWindow setIconImageAlphaValue:_CPMenuBarIconImageAlphaValue]; - + [_CPMenuBarSharedWindow setColor:[_CPMenuBarAttributes objectForKey:@"CPMenuBarBackgroundColor"]]; [_CPMenuBarSharedWindow setTextColor:[_CPMenuBarAttributes objectForKey:@"CPMenuBarTextColor"]]; [_CPMenuBarSharedWindow setTitleColor:[_CPMenuBarAttributes objectForKey:@"CPMenuBarTitleColor"]]; @@ -120,12 +120,12 @@ var _CPMenuBarVisible = NO, [_CPMenuBarSharedWindow setHighlightColor:[_CPMenuBarAttributes objectForKey:@"CPMenuBarHighlightColor"]]; [_CPMenuBarSharedWindow setHighlightTextColor:[_CPMenuBarAttributes objectForKey:@"CPMenuBarHighlightTextColor"]]; [_CPMenuBarSharedWindow setHighlightTextShadowColor:[_CPMenuBarAttributes objectForKey:@"CPMenuBarHighlightTextShadowColor"]]; - + [_CPMenuBarSharedWindow orderFront:self]; } else [_CPMenuBarSharedWindow orderOut:self]; - + // FIXME: There must be a better way to do this. #if PLATFORM(DOM) [[CPPlatformWindow primaryPlatformWindow] resizeEvent:nil]; @@ -159,9 +159,9 @@ var _CPMenuBarVisible = NO, { if (_CPMenuBarAttributes == attributes) return; - + _CPMenuBarAttributes = [attributes copy]; - + var textColor = [attributes objectForKey:@"CPMenuBarTextColor"], titleColor = [attributes objectForKey:@"CPMenuBarTitleColor"], textShadowColor = [attributes objectForKey:@"CPMenuBarTextShadowColor"], @@ -169,40 +169,40 @@ var _CPMenuBarVisible = NO, highlightColor = [attributes objectForKey:@"CPMenuBarHighlightColor"], highlightTextColor = [attributes objectForKey:@"CPMenuBarHighlightTextColor"], highlightTextShadowColor = [attributes objectForKey:@"CPMenuBarHighlightTextShadowColor"]; - + if (!textColor && titleColor) [_CPMenuBarAttributes setObject:titleColor forKey:@"CPMenuBarTextColor"]; - + else if (textColor && !titleColor) [_CPMenuBarAttributes setObject:textColor forKey:@"CPMenuBarTitleColor"]; - + else if (!textColor && !titleColor) { [_CPMenuBarAttributes setObject:[CPColor colorWithRed:0.051 green:0.2 blue:0.275 alpha:1.0] forKey:@"CPMenuBarTextColor"]; [_CPMenuBarAttributes setObject:[CPColor colorWithRed:0.051 green:0.2 blue:0.275 alpha:1.0] forKey:@"CPMenuBarTitleColor"]; } - + if (!textShadowColor && titleShadowColor) [_CPMenuBarAttributes setObject:titleShadowColor forKey:@"CPMenuBarTextShadowColor"]; - + else if (textShadowColor && !titleShadowColor) [_CPMenuBarAttributes setObject:textShadowColor forKey:@"CPMenuBarTitleShadowColor"]; - + else if (!textShadowColor && !titleShadowColor) { [_CPMenuBarAttributes setObject:[CPColor whiteColor] forKey:@"CPMenuBarTextShadowColor"]; [_CPMenuBarAttributes setObject:[CPColor whiteColor] forKey:@"CPMenuBarTitleShadowColor"]; } - + if (!highlightColor) [_CPMenuBarAttributes setObject:[CPColor colorWithCalibratedRed:94.0/255.0 green:130.0/255.0 blue:186.0/255.0 alpha:1.0] forKey:@"CPMenuBarHighlightColor"]; - + if (!highlightTextColor) [_CPMenuBarAttributes setObject:[CPColor whiteColor] forKey:@"CPMenuBarHighlightTextColor"]; - + if (!highlightTextShadowColor) [_CPMenuBarAttributes setObject:[CPColor blackColor] forKey:@"CPMenuBarHighlightTextShadowColor"]; - + if (_CPMenuBarSharedWindow) { [_CPMenuBarSharedWindow setColor:[_CPMenuBarAttributes objectForKey:@"CPMenuBarBackgroundColor"]]; @@ -231,7 +231,7 @@ var _CPMenuBarVisible = NO, { if (self === [CPApp mainMenu]) return MENUBAR_HEIGHT; - + return 0.0; } @@ -249,18 +249,18 @@ var _CPMenuBarVisible = NO, - (id)initWithTitle:(CPString)aTitle { self = [super init]; - + if (self) { _title = aTitle; _items = []; - + _autoenablesItems = YES; _showsStateColumn = YES; [self setMinimumWidth:0]; } - + return self; } @@ -287,7 +287,7 @@ var _CPMenuBarVisible = NO, [aMenuItem setMenu:self]; [_items insertObject:aMenuItem atIndex:anIndex]; - + [[CPNotificationCenter defaultCenter] postNotificationName:CPMenuDidAddItemNotification object:self @@ -306,7 +306,7 @@ var _CPMenuBarVisible = NO, - (CPMenuItem)insertItemWithTitle:(CPString)aTitle action:(SEL)anAction keyEquivalent:(CPString)aKeyEquivalent atIndex:(unsigned)anIndex { var item = [[CPMenuItem alloc] initWithTitle:aTitle action:anAction keyEquivalent:aKeyEquivalent]; - + [self insertItem:item atIndex:anIndex]; return item; @@ -351,10 +351,10 @@ var _CPMenuBarVisible = NO, { if (anIndex < 0 || anIndex >= _items.length) return; - + [_items[anIndex] setMenu:nil]; [_items removeObjectAtIndex:anIndex]; - + [[CPNotificationCenter defaultCenter] postNotificationName:CPMenuDidRemoveItemNotification object:self @@ -369,7 +369,7 @@ var _CPMenuBarVisible = NO, { if ([aMenuItem menu] != self) return; - + [[CPNotificationCenter defaultCenter] postNotificationName:CPMenuDidChangeItemNotification object:self @@ -385,10 +385,10 @@ var _CPMenuBarVisible = NO, - (CPMenuItem)itemWithTag:(int)aTag { var index = [self indexOfItemWithTag:aTag]; - + if (index == CPNotFound) return nil; - + return _items[index]; } @@ -400,10 +400,10 @@ var _CPMenuBarVisible = NO, - (CPMenuItem)itemWithTitle:(CPString)aTitle { var index = [self indexOfItemWithTitle:aTitle]; - + if (index == CPNotFound) return nil; - + return _items[index]; } @@ -442,7 +442,7 @@ var _CPMenuBarVisible = NO, { if ([aMenuItem menu] !== self) return CPNotFound; - + return [_items indexOfObjectIdenticalTo:aMenuItem]; } @@ -455,7 +455,7 @@ var _CPMenuBarVisible = NO, { var index = 0, count = _items.length; - + for (; index < count; ++index) if ([_items[index] title] === aTitle) return index; @@ -472,7 +472,7 @@ var _CPMenuBarVisible = NO, { var index = 0, count = _items.length; - + for (; index < count; ++index) if ([_items[index] tag] == aTag) return index; @@ -490,11 +490,11 @@ var _CPMenuBarVisible = NO, { var index = 0, count = _items.length; - + for (; index < count; ++index) { var item = _items[index]; - + if ([item target] == aTarget && (!anAction || [item action] == anAction)) return index; } @@ -511,7 +511,7 @@ var _CPMenuBarVisible = NO, { var index = 0, count = _items.length; - + for (; index < count; ++index) if ([[_items[index] representedObject] isEqual:anObject]) return index; @@ -528,7 +528,7 @@ var _CPMenuBarVisible = NO, { var index = 0, count = _items.length; - + for (; index < count; ++index) if ([_items[index] submenu] == aMenu) return index; @@ -546,7 +546,7 @@ var _CPMenuBarVisible = NO, { [aMenuItem setTarget:aMenuItem]; [aMenuItem setAction:@selector(submenuAction:)]; - + [aMenuItem setSubmenu:aMenu]; } @@ -683,7 +683,7 @@ var _CPMenuBarVisible = NO, itemIndex = [self indexOfItem:anItem]; if (itemIndex === CPNotFound) - throw "In call to popUpMenuPositioningItem:atLocation:inView:callback:, menu item " + + throw "In call to popUpMenuPositioningItem:atLocation:inView:callback:, menu item " + anItem + " is not present in menu " + self; } @@ -784,10 +784,10 @@ var _CPMenuBarVisible = NO, + (void)popUpContextMenu:(CPMenu)aMenu withEvent:(CPEvent)anEvent forView:(CPView)aView withFont:(CPFont)aFont { var delegate = [aMenu delegate]; - + if ([delegate respondsToSelector:@selector(menuWillOpen:)]) [delegate menuWillOpen:aMenu]; - + if (!aFont) aFont = [CPFont systemFontOfSize:12.0]; @@ -938,17 +938,7 @@ var _CPMenuBarVisible = NO, var item = _items[index], modifierMask = [item keyEquivalentModifierMask]; - if ([item keyEquivalent] === [[item keyEquivalent] uppercaseString]) - modifierMask |= CPShiftKeyMask; - - if (CPBrowserIsOperatingSystem(CPWindowsOperatingSystem) && (modifierMask & CPCommandKeyMask)) - { - modifierMask |= CPControlKeyMask; - modifierMask &= ~CPCommandKeyMask; - } - - if ((modifierFlags & (CPShiftKeyMask | CPAlternateKeyMask | CPCommandKeyMask | CPControlKeyMask)) == modifierMask && - [characters caseInsensitiveCompare:[item keyEquivalent]] == CPOrderedSame) + if ([anEvent _triggersKeyEquivalent:[item keyEquivalent] withModifierMask:[item keyEquivalentModifierMask]]) { if ([item isEnabled]) [self performActionForItemAtIndex:index]; @@ -956,7 +946,7 @@ var _CPMenuBarVisible = NO, { //beep? } - + return YES; } @@ -975,7 +965,7 @@ var _CPMenuBarVisible = NO, - (void)performActionForItemAtIndex:(unsigned)anIndex { var item = _items[anIndex]; - + [CPApp sendAction:[item action] to:[item target] from:item]; } @@ -1064,7 +1054,7 @@ var CPMenuTitleKey = @"CPMenuTitleKey", - (id)initWithCoder:(CPCoder)aCoder { self = [super init]; - + if (self) { _title = [aCoder decodeObjectForKey:CPMenuTitleKey]; @@ -1076,7 +1066,7 @@ var CPMenuTitleKey = @"CPMenuTitleKey", [self setMinimumWidth:0]; } - + return self; } diff --git a/AppKit/CPMenu/_CPMenuManager.j b/AppKit/CPMenu/_CPMenuManager.j index e022f8ddc..18d1021e1 100644 --- a/AppKit/CPMenu/_CPMenuManager.j +++ b/AppKit/CPMenu/_CPMenuManager.j @@ -96,7 +96,7 @@ var SharedMenuManager = nil; // Close Menu Event. if (type === CPAppKitDefined) - return [self completeTracking] + return [self completeTracking]; [CPApp setTarget:self selector:@selector(trackEvent:) forNextEventMatchingMask:CPPeriodicMask | CPMouseMovedMask | CPLeftMouseDraggedMask | CPLeftMouseUpMask | CPAppKitDefinedMask untilDate:nil inMode:nil dequeue:YES]; diff --git a/AppKit/CPMenuItem/CPMenuItem.j b/AppKit/CPMenuItem/CPMenuItem.j index c8d84026e..fb352ed69 100644 --- a/AppKit/CPMenuItem/CPMenuItem.j +++ b/AppKit/CPMenuItem/CPMenuItem.j @@ -29,7 +29,7 @@ @import "CPView.j" @import "_CPMenuItemView.j" -/*! +/*! @ingroup appkit @class CPMenuItem @@ -43,39 +43,39 @@ CPString _title; //CPAttributedString _attributedTitle; - + CPFont _font; - + id _target; SEL _action; - + BOOL _isEnabled; BOOL _isHidden; - + int _tag; int _state; - + CPImage _image; CPImage _alternateImage; CPImage _onStateImage; CPImage _offStateImage; CPImage _mixedStateImage; - + CPMenu _submenu; CPMenu _menu; - + CPString _keyEquivalent; unsigned _keyEquivalentModifierMask; - + int _mnemonicLocation; - + BOOL _isAlternate; int _indentationLevel; - + CPString _toolTip; id _representedObject; CPView _view; - + _CPMenuItemView _menuItemView; } @@ -94,19 +94,19 @@ - (id)initWithTitle:(CPString)aTitle action:(SEL)anAction keyEquivalent:(CPString)aKeyEquivalent { self = [super init]; - + if (self) { _isSeparator = NO; _title = aTitle; _action = anAction; - + _isEnabled = YES; - + _tag = 0; _state = CPOffState; - + _keyEquivalent = aKeyEquivalent || @""; _keyEquivalentModifierMask = CPPlatformActionKeyMask; @@ -114,7 +114,7 @@ _mnemonicLocation = CPNotFound; } - + return self; } @@ -152,7 +152,7 @@ { if (_isHidden == isHidden) return; - + _isHidden = isHidden; [_menu itemChanged:self]; @@ -173,9 +173,9 @@ { if (_isHidden) return YES; - + var supermenu = [_menu supermenu]; - + if ([[supermenu itemAtIndex:[supermenu indexOfItemWithSubmenu:_menu]] isHiddenOrHasHiddenAncestor]) return YES; @@ -228,11 +228,11 @@ if (_title == aTitle) return; - + _title = aTitle; - + [_menuItemView setDirty]; - + [_menu itemChanged:self]; } @@ -260,11 +260,11 @@ { if (_font == aFont) return; - + _font = aFont; [_menu itemChanged:self]; - + [_menuItemView setDirty]; } @@ -316,9 +316,9 @@ CPOffState { if (_state == aState) return; - + _state = aState; - + [_menu itemChanged:self]; [_menuItemView setDirty]; @@ -346,11 +346,11 @@ CPOffState { if (_image == anImage) return; - + _image = anImage; [_menuItemView setDirty]; - + [_menu itemChanged:self]; } @@ -388,7 +388,7 @@ CPOffState { if (_onStateImage == anImage) return; - + _onStateImage = anImage; [_menu itemChanged:self]; } @@ -409,7 +409,7 @@ CPOffState { if (_offStateImage == anImage) return; - + _offStateImage = anImage; [_menu itemChanged:self]; } @@ -430,7 +430,7 @@ CPOffState { if (_mixedStateImage == anImage) return; - + _mixedStateImage = anImage; [_menu itemChanged:self]; } @@ -648,14 +648,14 @@ CPControlKeyMask - (void)setTitleWithMnemonicLocation:(CPString)aTitle { var location = [aTitle rangeOfString:@"&"].location; - + if (location == CPNotFound) [self setTitle:aTitle]; else { [self setTitle:[aTitle substringToIndex:location] + [aTitle substringFromIndex:location + 1]]; [self setMnemonicLocation:location]; - } + } } /*! @@ -696,7 +696,7 @@ CPControlKeyMask { if (aLevel < 0) [CPException raise:CPInvalidArgumentException reason:"setIndentationLevel: argument must be greater than or equal to 0."]; - + _indentationLevel = MIN(15, aLevel); } @@ -755,11 +755,11 @@ CPControlKeyMask { if (_view === aView) return; - + _view = aView; - + [_menuItemView setDirty]; - + [_menu itemChanged:self]; } @@ -790,7 +790,7 @@ CPControlKeyMask { if (!_menuItemView) _menuItemView = [[_CPMenuItemView alloc] initWithFrame:CGRectMakeZero() forMenuItem:self]; - + return _menuItemView; } @@ -844,15 +844,15 @@ var CPMenuItemIsSeparatorKey = @"CPMenuItemIsSeparatorKey", - (id)initWithCoder:(CPCoder)aCoder { self = [super init]; - + if (self) { _isSeparator = [aCoder containsValueForKey:CPMenuItemIsSeparatorKey] && [aCoder decodeBoolForKey:CPMenuItemIsSeparatorKey]; _title = [aCoder decodeObjectForKey:CPMenuItemTitleKey]; - + // _font; - + _target = [aCoder decodeObjectForKey:CPMenuItemTargetKey]; _action = [aCoder decodeObjectForKey:CPMenuItemActionKey]; @@ -887,7 +887,7 @@ var CPMenuItemIsSeparatorKey = @"CPMenuItemIsSeparatorKey", _representedObject = DEFAULT_VALUE(CPMenuItemRepresentedObjectKey, nil); _view = DEFAULT_VALUE(CPMenuItemViewKey, nil); } - + return self; } @@ -901,11 +901,11 @@ var CPMenuItemIsSeparatorKey = @"CPMenuItemIsSeparatorKey", [aCoder encodeBool:_isSeparator forKey:CPMenuItemIsSeparatorKey]; [aCoder encodeObject:_title forKey:CPMenuItemTitleKey]; - + [aCoder encodeObject:_target forKey:CPMenuItemTargetKey]; [aCoder encodeObject:_action forKey:CPMenuItemActionKey]; - ENCODE_IFNOT(CPMenuItemIsEnabledKey, _isEnabled, YES); + ENCODE_IFNOT(CPMenuItemIsEnabledKey, _isEnabled, YES); ENCODE_IFNOT(CPMenuItemIsHiddenKey, _isHidden, NO); ENCODE_IFNOT(CPMenuItemTagKey, _tag, 0); @@ -913,7 +913,7 @@ var CPMenuItemIsSeparatorKey = @"CPMenuItemIsSeparatorKey", ENCODE_IFNOT(CPMenuItemImageKey, _image, nil); ENCODE_IFNOT(CPMenuItemAlternateImageKey, _alternateImage, nil); - + ENCODE_IFNOT(CPMenuItemSubmenuKey, _submenu, nil); ENCODE_IFNOT(CPMenuItemMenuKey, _menu, nil); diff --git a/AppKit/CPMenuItem/_CPMenuItemStandardView.j b/AppKit/CPMenuItem/_CPMenuItemStandardView.j index db15e5417..63777e96f 100644 --- a/AppKit/CPMenuItem/_CPMenuItemStandardView.j +++ b/AppKit/CPMenuItem/_CPMenuItemStandardView.j @@ -39,7 +39,7 @@ var SUBMENU_INDICATOR_COLOR = nil, SUBMENU_INDICATOR_COLOR = [CPColor grayColor]; _CPMenuItemSelectionColor = [CPColor colorWithCalibratedRed:95.0 / 255.0 green:131.0 / 255.0 blue:185.0 / 255.0 alpha:1.0]; - _CPMenuItemTextShadowColor = [CPColor colorWithCalibratedRed:26.0 / 255.0 green: 73.0 / 255.0 blue:109.0 / 255.0 alpha:1.0] + _CPMenuItemTextShadowColor = [CPColor colorWithCalibratedRed:26.0 / 255.0 green: 73.0 / 255.0 blue:109.0 / 255.0 alpha:1.0]; var bundle = [CPBundle bundleForClass:self]; diff --git a/AppKit/CPMenuItem/_CPMenuItemView.j b/AppKit/CPMenuItem/_CPMenuItemView.j index d4ef798f0..7b5438a17 100644 --- a/AppKit/CPMenuItem/_CPMenuItemView.j +++ b/AppKit/CPMenuItem/_CPMenuItemView.j @@ -43,7 +43,7 @@ var _CPMenuItemSelectionColor = nil, return; _CPMenuItemSelectionColor = [CPColor colorWithCalibratedRed:95.0 / 255.0 green:131.0 / 255.0 blue:185.0 / 255.0 alpha:1.0]; - _CPMenuItemTextShadowColor = [CPColor colorWithCalibratedRed:26.0 / 255.0 green: 73.0 / 255.0 blue:109.0 / 255.0 alpha:1.0] + _CPMenuItemTextShadowColor = [CPColor colorWithCalibratedRed:26.0 / 255.0 green: 73.0 / 255.0 blue:109.0 / 255.0 alpha:1.0]; var bundle = [CPBundle bundleForClass:self]; diff --git a/AppKit/CPOutlineView.j b/AppKit/CPOutlineView.j index 06812521a..f3bb21bac 100644 --- a/AppKit/CPOutlineView.j +++ b/AppKit/CPOutlineView.j @@ -1245,13 +1245,34 @@ var _loadItemInfoForItem = function(/*CPOutlineView*/ anOutlineView, /*id*/ anIt CGContextAddLineToPoint(context, 9.0, 0.0); CGContextAddLineToPoint(context, 4.5, 8.0); CGContextAddLineToPoint(context, 0.0, 0.0); - + CGContextClosePath(context); - var isHighlighted = [self hasThemeState:CPThemeStateHighlighted]; - var color = [self hasThemeState:CPThemeStateSelected] ? (isHighlighted ? [CPColor lightGrayColor] : [CPColor whiteColor]) : (isHighlighted ? [CPColor blackColor] : [CPColor grayColor]); - - CGContextSetFillColor(context, color); + CGContextSetFillColor(context, + colorForDisclosureTriangle([self hasThemeState:CPThemeStateSelected], + [self hasThemeState:CPThemeStateHighlighted])); CGContextFillPath(context); + + + CGContextBeginPath(context); + CGContextMoveToPoint(context, 0.0, 0.0); + if(_angle === 0.0) { + CGContextAddLineToPoint(context, 4.5, 8.0); + CGContextAddLineToPoint(context, 9.0, 0.0); + } else { + CGContextAddLineToPoint(context, 4.5, 8.0); + } + CGContextSetStrokeColor(context, [CPColor colorWithCalibratedWhite:1.0 alpha: 0.8]); + CGContextStrokePath(context); } @end + +var colorForDisclosureTriangle = function(isSelected, isHighlighted) { + return isSelected + ? (isHighlighted + ? [CPColor colorWithCalibratedWhite:0.9 alpha: 1.0] + : [CPColor colorWithCalibratedWhite:1.0 alpha: 1.0]) + : (isHighlighted + ? [CPColor colorWithCalibratedWhite:0.4 alpha: 1.0] + : [CPColor colorWithCalibratedWhite:0.5 alpha: 1.0]); +} diff --git a/AppKit/CPResponder.j b/AppKit/CPResponder.j index 9e268e40f..08b27616e 100644 --- a/AppKit/CPResponder.j +++ b/AppKit/CPResponder.j @@ -22,23 +22,23 @@ @import - -CPDeleteKeyCode = 8; -CPTabKeyCode = 9; -CPReturnKeyCode = 13; -CPEscapeKeyCode = 27; -CPSpaceKeyCode = 32; -CPPageUpKeyCode = 33; -CPPageDownKeyCode = 34; -CPLeftArrowKeyCode = 37; -CPUpArrowKeyCode = 38; -CPRightArrowKeyCode = 39; -CPDownArrowKeyCode = 40; +CPDeleteKeyCode = 8; +CPTabKeyCode = 9; +CPReturnKeyCode = 13; +CPEscapeKeyCode = 27; +CPSpaceKeyCode = 32; +CPPageUpKeyCode = 33; +CPPageDownKeyCode = 34; +CPLeftArrowKeyCode = 37; +CPUpArrowKeyCode = 38; +CPRightArrowKeyCode = 39; +CPDownArrowKeyCode = 40; +CPDeleteForwardKeyCode = 46; /*! @ingroup appkit @class CPResponder - + Subclasses of CPResonder can be part of the responder chain. */ @implementation CPResponder : CPObject @@ -104,42 +104,24 @@ CPDownArrowKeyCode = 40; for (; index < count; ++index) { - var event = events[index]; + var event = events[index], + modifierFlags = [event modifierFlags], + character = [event charactersIgnoringModifiers], + selectorNames = [CPKeyBinding selectorsForKey:character modifierFlags:modifierFlags]; - switch([event keyCode]) + if (selectorNames) { - case CPPageUpKeyCode: [self doCommandBySelector:@selector(pageUp:)]; - break; - case CPPageDownKeyCode: [self doCommandBySelector:@selector(pageDown:)]; - break; - case CPLeftArrowKeyCode: [self doCommandBySelector:@selector(moveLeft:)]; - break; - case CPRightArrowKeyCode: [self doCommandBySelector:@selector(moveRight:)]; - break; - case CPUpArrowKeyCode: [self doCommandBySelector:@selector(moveUp:)]; - break; - case CPDownArrowKeyCode: [self doCommandBySelector:@selector(moveDown:)]; - break; - case CPDeleteKeyCode: [self doCommandBySelector:@selector(deleteBackward:)]; - break; - case CPReturnKeyCode: - case 3: [self doCommandBySelector:@selector(insertLineBreak:)]; - break; - - case CPEscapeKeyCode: [self doCommandBySelector:@selector(cancel:)]; - break; + for (var s = 0, scount = selectorNames.length; s < scount; s++) + { + var selector = selectorNames[s]; + if (!selector) + continue; - case CPTabKeyCode: var shift = [event modifierFlags] & CPShiftKeyMask; - - if (!shift) - [self doCommandBySelector:@selector(insertTab:)]; - else - [self doCommandBySelector:@selector(insertBackTab:)]; - - break; - - default: [self insertText:[event characters]]; + [self doCommandBySelector:CPSelectorFromString(selector)]; + } } + else if (!(modifierFlags & (CPCommandKeyMask | CPControlKeyMask)) && [self respondsToSelector:@selector(insertText:)]) + [self insertText:[event characters]]; } } @@ -239,6 +221,15 @@ CPDownArrowKeyCode = 40; [_nextResponder performSelector:_cmd withObject:anEvent]; } +/*! + Notifies the receiver that the user has pressed or released a modifier key (Shift, Control, and so on). + @param anEvent information about the key press +*/ +- (void)flagsChanged:(CPEvent)anEvent +{ + [_nextResponder performSelector:_cmd withObject:anEvent]; +} + /* FIXME This description is bad. Based on \c anEvent, the receiver should simulate the event. @@ -315,7 +306,7 @@ CPDownArrowKeyCode = 40; if([self respondsToSelector:aSelector]) { [self performSelector:aSelector withObject:anObject]; - + return YES; } @@ -366,10 +357,10 @@ var CPResponderNextResponderKey = @"CPResponderNextResponderKey"; - (id)initWithCoder:(CPCoder)aCoder { self = [super init]; - + if (self) _nextResponder = [aCoder decodeObjectForKey:CPResponderNextResponderKey]; - + return self; } diff --git a/AppKit/CPSplitView.j b/AppKit/CPSplitView.j index 70f1a9282..4a90dcf99 100644 --- a/AppKit/CPSplitView.j +++ b/AppKit/CPSplitView.j @@ -152,7 +152,7 @@ var CPSplitViewHorizontalImage = nil, _isPaneSplitter = shouldBePaneSplitter; if(_DOMDividerElements[_drawingDivider]) - [self _setupDOMDivider] + [self _setupDOMDivider]; // The divider changes size when pane splitter mode is toggled, so the // subviews need to change size too. diff --git a/AppKit/CPTableView.j b/AppKit/CPTableView.j index c8bac1aae..480c01373 100644 --- a/AppKit/CPTableView.j +++ b/AppKit/CPTableView.j @@ -654,9 +654,9 @@ CPTableViewFirstColumnOnlyAutoresizingStyle = 5; return; _sourceListActiveGradient = [aDictionary valueForKey:CPSourceListGradient]; - _sourceListActiveTopLineColor = [aDictionary valueForKey:CPSourceListTopLineColor] + _sourceListActiveTopLineColor = [aDictionary valueForKey:CPSourceListTopLineColor]; _sourceListActiveBottomLineColor = [aDictionary valueForKey:CPSourceListBottomLineColor]; - [self setNeedsDisplay:YES] + [self setNeedsDisplay:YES]; } /*! @@ -909,6 +909,9 @@ CPTableViewFirstColumnOnlyAutoresizingStyle = 5; else _selectedRowIndexes = [rows copy]; + // update last selected row + _lastSelectedRow = ([rows count] > 0) ? [rows lastIndex] : -1; + [self _updateHighlightWithOldRows:previousSelectedIndexes newRows:_selectedRowIndexes]; [_tableDrawView display]; // FIXME: should be setNeedsDisplayInRect:enclosing rect of new (de)selected rows // but currently -drawRect: is not implemented here @@ -1020,7 +1023,7 @@ CPTableViewFirstColumnOnlyAutoresizingStyle = 5; - (int)selectedRow { - return [_selectedRowIndexes lastIndex]; + return _lastSelectedRow; } - (CPIndexSet)selectedRowIndexes @@ -3067,15 +3070,16 @@ CPTableViewFirstColumnOnlyAutoresizingStyle = 5; { // We don't use rowAtPoint here because the drag indicator can appear below the last row // and rowAtPoint doesn't return rows that are larger than numberOfRows - var row = FLOOR(dragPoint.y / ( _rowHeight + _intercellSpacing.height )); - + // FIX ME: this is going to break when we implement variable row heights... + var row = FLOOR(dragPoint.y / ( _rowHeight + _intercellSpacing.height )), // Determine if the mouse is currently closer to this row or the row below it - var lowerRow = row + 1, + lowerRow = row + 1, rect = [self rectOfRow:row], - lowerRect = [self rectOfRow:lowerRow]; + bottomPoint = CGRectGetMaxY(rect), + bottomThirty = bottomPoint - ((bottomPoint - CGRectGetMinY(rect)) * 0.3); - if (ABS(CPRectGetMinY(lowerRect) - dragPoint.y) < ABS(dragPoint.y - CPRectGetMinY(rect))) - row = lowerRow; + if (dragPoint.y > MAX(bottomThirty, bottomPoint - 6)) + row = lowerRow; if (row >= [self numberOfRows]) row = [self numberOfRows]; @@ -3268,8 +3272,6 @@ CPTableViewFirstColumnOnlyAutoresizingStyle = 5; } } - _lastSelectedRow = ([newSelection count] > 0) ? aRow : -1; - // if empty selection is not allowed and the new selection has nothing selected, abort if (!_allowsEmptySelection && [newSelection count] === 0) return; @@ -3358,6 +3360,11 @@ CPTableViewFirstColumnOnlyAutoresizingStyle = 5; [self scrollRowToVisible:i]; } +- (void)moveDownAndModifySelection:(id)sender +{ + [self moveDown:sender]; +} + - (void)moveUp:(id)sender { if (_implementedDelegateMethods & CPTableViewDelegate_selectionShouldChangeInTableView_ && @@ -3405,6 +3412,11 @@ CPTableViewFirstColumnOnlyAutoresizingStyle = 5; [self scrollRowToVisible:i]; } +- (void)moveUpAndModifySelection:(id)sender +{ + [self moveUp:sender]; +} + - (void)deleteBackward:(id)sender { if([_delegate respondsToSelector: @selector(tableViewDeleteKeyPressed:)]) @@ -3481,7 +3493,7 @@ var CPTableViewDataSourceKey = @"CPTableViewDataSourceKey", _gridColor = [aCoder decodeObjectForKey:CPTableViewGridColorKey] || [CPColor grayColor]; _gridStyleMask = [aCoder decodeIntForKey:CPTableViewGridStyleMaskKey] || CPTableViewGridNone; - _usesAlternatingRowBackgroundColors = [aCoder decodeObjectForKey:CPTableViewUsesAlternatingBackgroundKey] + _usesAlternatingRowBackgroundColors = [aCoder decodeObjectForKey:CPTableViewUsesAlternatingBackgroundKey]; _alternatingRowBackgroundColors = [[CPColor whiteColor], [CPColor colorWithRed:245.0 / 255.0 green:249.0 / 255.0 blue:252.0 / 255.0 alpha:1.0]]; diff --git a/AppKit/CPText.j b/AppKit/CPText.j new file mode 100644 index 000000000..4ac6354d4 --- /dev/null +++ b/AppKit/CPText.j @@ -0,0 +1,32 @@ +/* + * CPText.j + * AppKit + * + * Created by Alexander Ljungberg. + * Copyright 2010, WireLoad, LLC. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +@import "CPView.j" + +CPEnterCharacter = "\u0003"; +CPBackspaceCharacter = "\u0008"; +CPTabCharacter = "\u0009"; +CPNewlineCharacter = "\u000a"; +CPFormFeedCharacter = "\u000c"; +CPCarriageReturnCharacter = "\u000d"; +CPBackTabCharacter = "\u0019"; +CPDeleteCharacter = "\u007f"; diff --git a/AppKit/CPTextField.j b/AppKit/CPTextField.j index d3873e524..6440017d5 100644 --- a/AppKit/CPTextField.j +++ b/AppKit/CPTextField.j @@ -807,11 +807,8 @@ CPTextFieldStatePlaceholder = CPThemeState("placeholder"); { if ([[self window] firstResponder] === self) window.setTimeout(function() { element.select(); }, 0); - else - { - [[self window] makeFirstResponder:self]; + else if ([self window] !== nil && [[self window] makeFirstResponder:self]) window.setTimeout(function() {[self selectText:sender];}, 0); - } } #endif } diff --git a/AppKit/CPView.j b/AppKit/CPView.j index 3085a06fe..56025cd47 100644 --- a/AppKit/CPView.j +++ b/AppKit/CPView.j @@ -1679,7 +1679,7 @@ setBoundsOrigin: var theWindow = [self window]; [theWindow _noteUnregisteredDraggedTypes:_registeredDraggedTypes]; - [_registeredDraggedTypes addObjectsFromArray:pasteboardTypes] + [_registeredDraggedTypes addObjectsFromArray:pasteboardTypes]; [theWindow _noteRegisteredDraggedTypes:_registeredDraggedTypes]; _registeredDraggedTypesArray = nil; diff --git a/AppKit/CPViewAnimation.j b/AppKit/CPViewAnimation.j index b1bc4d414..594b638a2 100644 --- a/AppKit/CPViewAnimation.j +++ b/AppKit/CPViewAnimation.j @@ -79,9 +79,9 @@ CPViewAnimationFadeOutEffect = @"CPViewAnimationFadeOut"; while (animationIndex--) { var dictionary = [_viewAnimations objectAtIndex:animationIndex], - view = [self _targetView:dictionary] - startFrame = [self _startFrame:dictionary] - endFrame = [self _endFrame:dictionary] + view = [self _targetView:dictionary], + startFrame = [self _startFrame:dictionary], + endFrame = [self _endFrame:dictionary], differenceFrame = _CGRectMakeZero(); differenceFrame.origin.x = endFrame.origin.x - startFrame.origin.x; diff --git a/AppKit/CPWindow/CPWindow.j b/AppKit/CPWindow/CPWindow.j index a28ecf2a6..75650792f 100644 --- a/AppKit/CPWindow/CPWindow.j +++ b/AppKit/CPWindow/CPWindow.j @@ -194,13 +194,13 @@ var SHADOW_MARGIN_LEFT = 20.0, SHADOW_MARGIN_TOP = 10.0, SHADOW_MARGIN_BOTTOM = 10.0, SHADOW_DISTANCE = 5.0, - + _CPWindowShadowColor = nil; - + var CPWindowSaveImage = nil, CPWindowSavingImage = nil; -/*! +/*! @ingroup appkit @class CPWindow @@ -213,33 +213,33 @@ var CPWindowSaveImage = nil,

A window always contains a content view which is the highest level view available for public (application) use. This view fills the area of the window inside any decoration/border. This is the only part of the window that application programmers are allowed to draw in directly.

You can convert between view coordinates and window base coordinates using the [CPView -convertPoint:fromView:], [CPView -convertPoint:toView:], [CPView -convertRect:fromView:], and [CPView -convertRect:toView:] methods with a nil view argument. - + @par Delegate Methods - + @delegate -(void)windowDidResize:(CPNotification)notification; Sent from the notification center when the window has been resized. @param notification contains information about the resize event - + @delegate -(CPUndoManager)windowWillReturnUndoManager:(CPWindow)window; Called to obtain the undo manager for a window @param window the window for which to return the undo manager @return the window's undo manager - + @delegate -(void)windowDidBecomeMain:(CPNotification)notification; Sent from the notification center when the delegate's window becomes the main window. @param notification contains information about the event - + @delegate -(void)windowDidResignMain:(CPNotification)notification; Sent from the notification center when the delegate's window has resigned main window status. @param notification contains information about the event - + @delegate -(void)windowDidResignKey:(CPNotification)notification; Sent from the notification center when the delegate's window has resigned key window status. @param notification contains information about the event - + @delegate -(BOOL)windowShouldClose:(id)window; Called when the user tries to close the window. @param window the window to close @@ -311,15 +311,17 @@ var CPWindowSaveImage = nil, #endif unsigned _autoresizingMask; - + BOOL _delegateRespondsToWindowWillReturnUndoManagerSelector; BOOL _isFullPlatformWindow; _CPWindowFullPlatformWindowSession _fullPlatformWindowSession; - + CPDictionary _sheetContext; CPWindow _parentView; BOOL _isSheet; + + _CPWindowFrameAnimation _frameAnimation; } /* @@ -330,9 +332,9 @@ var CPWindowSaveImage = nil, { if (self != [CPWindow class]) return; - + var bundle = [CPBundle bundleForClass:[CPWindow class]]; - + CPWindowSavingImage = [[CPImage alloc] initWithContentsOfFile:[bundle pathForResource:@"CPProgressIndicator/CPProgressIndicatorSpinningStyleRegular.gif"] size:CGSizeMake(16.0, 16.0)] } @@ -359,7 +361,7 @@ CPTexturedBackgroundWindowMask - (id)initWithContentRect:(CGRect)aContentRect styleMask:(unsigned int)aStyleMask { self = [super init]; - + if (self) { var windowViewClass = [[self class] _windowViewClassForStyleMask:aStyleMask]; @@ -393,7 +395,7 @@ CPTexturedBackgroundWindowMask // Set up our window number. _windowNumber = [CPApp._windows count]; CPApp._windows[_windowNumber] = self; - + _styleMask = aStyleMask; [self setLevel:CPNormalWindowLevel]; @@ -408,15 +410,15 @@ CPTexturedBackgroundWindowMask [_windowView setNextResponder:self]; [self setMovableByWindowBackground:aStyleMask & CPHUDBackgroundWindowMask]; - + // Create a generic content view. [self setContentView:[[CPView alloc] initWithFrame:CGRectMakeZero()]]; - + _firstResponder = self; #if PLATFORM(DOM) _DOMElement = document.createElement("div"); - + _DOMElement.style.position = "absolute"; _DOMElement.style.visibility = "visible"; _DOMElement.style.zIndex = 0; @@ -442,7 +444,7 @@ CPTexturedBackgroundWindowMask [self setShowsResizeIndicator:_styleMask & CPResizableWindowMask]; } - + return self; } @@ -659,9 +661,10 @@ CPTexturedBackgroundWindowMask if (shouldAnimate) { - var animation = [[_CPWindowFrameAnimation alloc] initWithWindow:self targetFrame:aFrame]; - - [animation startAnimation]; + [_frameAnimation stopAnimation]; + _frameAnimation = [[_CPWindowFrameAnimation alloc] initWithWindow:self targetFrame:aFrame]; + + [_frameAnimation startAnimation]; } else { @@ -882,12 +885,12 @@ CPTexturedBackgroundWindowMask { if (_contentView) [_contentView removeFromSuperview]; - + var bounds = CGRectMake(0.0, 0.0, CGRectGetWidth(_frame), CGRectGetHeight(_frame)); - + _contentView = aView; [_contentView setFrame:[self contentRectForFrameRect:bounds]]; - + [_contentView setAutoresizingMask:CPViewWidthSizable | CPViewHeightSizable]; [_windowView addSubview:_contentView]; } @@ -943,24 +946,24 @@ CPTexturedBackgroundWindowMask { if (CGSizeEqualToSize(_minSize, aSize)) return; - + _minSize = CGSizeCreateCopy(aSize); var size = CGSizeMakeCopy([self frame].size), needsFrameChange = NO; - + if (size.width < _minSize.width) { size.width = _minSize.width; needsFrameChange = YES; } - + if (size.height < _minSize.height) { size.height = _minSize.height; needsFrameChange = YES; } - + if (needsFrameChange) [self setFrameSize:size]; } @@ -983,24 +986,24 @@ CPTexturedBackgroundWindowMask { if (CGSizeEqualToSize(_maxSize, aSize)) return; - + _maxSize = CGSizeCreateCopy(aSize); var size = CGSizeMakeCopy([self frame].size), needsFrameChange = NO; - + if (size.width > _maxSize.width) { size.width = _maxSize.width; needsFrameChange = YES; } - + if (size.height > _maxSize.height) { size.height = _maxSize.height; needsFrameChange = YES; } - + if (needsFrameChange) [self setFrameSize:size]; } @@ -1041,10 +1044,10 @@ CPTexturedBackgroundWindowMask if (_hasShadow && !_shadowView) { var bounds = [_windowView bounds]; - - _shadowView = [[CPView alloc] initWithFrame:CGRectMake(-SHADOW_MARGIN_LEFT, -SHADOW_MARGIN_TOP + SHADOW_DISTANCE, + + _shadowView = [[CPView alloc] initWithFrame:CGRectMake(-SHADOW_MARGIN_LEFT, -SHADOW_MARGIN_TOP + SHADOW_DISTANCE, SHADOW_MARGIN_LEFT + CGRectGetWidth(bounds) + SHADOW_MARGIN_RIGHT, SHADOW_MARGIN_TOP + CGRectGetHeight(bounds) + SHADOW_MARGIN_BOTTOM)]; - + if (!_CPWindowShadowColor) { var bundle = [CPBundle bundleForClass:[CPWindow class]]; @@ -1067,7 +1070,7 @@ CPTexturedBackgroundWindowMask [_shadowView setBackgroundColor:_CPWindowShadowColor]; [_shadowView setAutoresizingMask:CPViewWidthSizable | CPViewHeightSizable]; - + #if PLATFORM(DOM) CPDOMDisplayServerInsertBefore(_DOMElement, _shadowView._DOMElement, _windowView._DOMElement); #endif @@ -1133,7 +1136,7 @@ CPTexturedBackgroundWindowMask selector:@selector(windowDidBecomeKey:) name:CPWindowDidBecomeKeyNotification object:self]; - + if ([_delegate respondsToSelector:@selector(windowDidBecomeMain:)]) [defaultCenter addObserver:_delegate @@ -1229,7 +1232,7 @@ CPTexturedBackgroundWindowMask if(!aResponder || ![aResponder acceptsFirstResponder] || ![aResponder becomeFirstResponder]) { _firstResponder = self; - + return NO; } @@ -1282,9 +1285,9 @@ CPTexturedBackgroundWindowMask - (void)setTitle:(CPString)aTitle { _title = aTitle; - + [_windowView setTitle:aTitle]; - + [self _synchronizeMenuBarTitleWithWindowTitle]; } @@ -1376,8 +1379,18 @@ CPTexturedBackgroundWindowMask switch (type) { + case CPFlagsChanged: return [[self firstResponder] flagsChanged:anEvent]; + case CPKeyUp: return [[self firstResponder] keyUp:anEvent]; - case CPKeyDown: return [[self firstResponder] keyDown:anEvent]; + + case CPKeyDown: [[self firstResponder] keyDown:anEvent]; + + // Trigger the default button if needed + if (![self disableKeyEquivalentForDefaultButton]) + if ([anEvent _triggersKeyEquivalent:[[self defaultButton] keyEquivalent] withModifierMask:[[self defaultButton] keyEquivalentModifierMask]]) + [[self defaultButton] performClick:self]; + + return; case CPScrollWheel: return [[_windowView hitTest:point] scrollWheel:anEvent]; @@ -1536,7 +1549,7 @@ CPTexturedBackgroundWindowMask - (void)makeKeyAndOrderFront:(id)aSender { [self orderFront:self]; - + [self makeKeyWindow]; [self makeMainWindow]; } @@ -1600,7 +1613,7 @@ CPTexturedBackgroundWindowMask if (!pasteboardTypes) return; - [_inclusiveRegisteredDraggedTypes minusSet:pasteboardTypes] + [_inclusiveRegisteredDraggedTypes minusSet:pasteboardTypes]; if ([_inclusiveRegisteredDraggedTypes count] === 0) _inclusiveRegisteredDraggedTypes = nil; @@ -1631,7 +1644,7 @@ CPTexturedBackgroundWindowMask return; [self _noteUnregisteredDraggedTypes:_registeredDraggedTypes]; - [_registeredDraggedTypes addObjectsFromArray:pasteboardTypes] + [_registeredDraggedTypes addObjectsFromArray:pasteboardTypes]; [self _noteRegisteredDraggedTypes:_registeredDraggedTypes]; _registeredDraggedTypesArray = nil; @@ -1644,7 +1657,7 @@ CPTexturedBackgroundWindowMask - (CPArray)registeredDraggedTypes { if (!_registeredDraggedTypesArray) - _registeredDraggedTypesArray = [_registeredDraggedTypes allObjects] + _registeredDraggedTypesArray = [_registeredDraggedTypes allObjects]; return _registeredDraggedTypesArray; } @@ -1670,9 +1683,9 @@ CPTexturedBackgroundWindowMask { if (_isDocumentEdited == isDocumentEdited) return; - + _isDocumentEdited = isDocumentEdited; - + [CPMenu _setMenuBarIconImageAlphaValue:_isDocumentEdited ? 0.5 : 1.0]; [_windowView setDocumentEdited:isDocumentEdited]; @@ -1690,11 +1703,11 @@ CPTexturedBackgroundWindowMask { if (_isDocumentSaving == isDocumentSaving) return; - + _isDocumentSaving = isDocumentSaving; - + [self _synchronizeSaveMenuWithDocumentSaving]; - + [_windowView windowDidChangeDocumentSaving]; } @@ -1711,16 +1724,16 @@ CPTexturedBackgroundWindowMask var mainMenu = [CPApp mainMenu], index = [mainMenu indexOfItemWithTitle:_isDocumentSaving ? @"Save" : @"Saving..."]; - + if (index == CPNotFound) return; - + var item = [mainMenu itemAtIndex:index]; - + if (_isDocumentSaving) { CPWindowSaveImage = [item image]; - + [item setTitle:@"Saving..."]; [item setImage:CPWindowSavingImage]; [item setEnabled:NO]; @@ -1810,7 +1823,7 @@ CPTexturedBackgroundWindowMask if (![_delegate windowShouldClose:self]) return; } - + // Only check self is delegate does NOT implement this. This also ensures this when delegate == self (returns true). else if ([self respondsToSelector:@selector(windowShouldClose:)] && ![self windowShouldClose:self]) return; @@ -1820,8 +1833,8 @@ CPTexturedBackgroundWindowMask { var index = [documents indexOfObject:[_windowController document]]; - [documents[index] shouldCloseWindowController:_windowController - delegate:self + [documents[index] shouldCloseWindowController:_windowController + delegate:self shouldCloseSelector:@selector(_windowControllerContainingDocument:shouldClose:contextInfo:) contextInfo:{documents:[documents copy], visited:0, index:index}]; } @@ -1845,8 +1858,8 @@ CPTexturedBackgroundWindowMask { [windowController setDocument:documents[index]]; - [documents[index] shouldCloseWindowController:_windowController - delegate:self + [documents[index] shouldCloseWindowController:_windowController + delegate:self shouldCloseSelector:@selector(_windowControllerContainingDocument:shouldClose:contextInfo:) contextInfo:context]; } @@ -1883,7 +1896,7 @@ CPTexturedBackgroundWindowMask // FIXME: Also check if we can resize and titlebar. if ([self isVisible]) return YES; - + return NO; } @@ -1942,10 +1955,16 @@ CPTexturedBackgroundWindowMask else { var mainMenu = [CPApp mainMenu], - menuWindow = mainMenu ? mainMenu._menuWindow : nil; + menuBarClass = objj_getClass("_CPMenuBarWindow"), + menuWindow; + for (var i = 0; i < windowCount; i++) { var currentWindow = allWindows[i]; + + if ([currentWindow isKindOfClass:menuBarClass]) + menuWindow = currentWindow; + if (currentWindow === self || currentWindow === menuWindow) continue; @@ -1971,10 +1990,16 @@ CPTexturedBackgroundWindowMask else { var mainMenu = [CPApp mainMenu], - menuWindow = mainMenu ? mainMenu._menuWindow : nil; + menuBarClass = objj_getClass("_CPMenuBarWindow"), + menuWindow; + for (var i = 0; i < windowCount; i++) { var currentWindow = allWindows[i]; + + if ([currentWindow isKindOfClass:menuBarClass]) + menuWindow = currentWindow; + if (currentWindow === self || currentWindow === menuWindow) continue; @@ -2005,25 +2030,25 @@ CPTexturedBackgroundWindowMask { if (_toolbar === aToolbar) return; - + // If this has an owner, dump it! [[aToolbar _window] setToolbar:nil]; - + // This is no longer out toolbar. [_toolbar _setWindow:nil]; - + _toolbar = aToolbar; - + // THIS is our toolbar. [_toolbar _setWindow:self]; - + [self _noteToolbarChanged]; } - (void)toggleToolbarShown:(id)aSender { var toolbar = [self toolbar]; - + [toolbar setVisible:![toolbar isVisible]]; } @@ -2039,10 +2064,10 @@ CPTexturedBackgroundWindowMask else { newFrame = CGRectMakeCopy([self frame]); - + newFrame.origin = frame.origin; } - + [self setFrame:newFrame]; /* [_windowView setAnimatingToolbar:YES]; @@ -2054,11 +2079,12 @@ CPTexturedBackgroundWindowMask - (void)_setFrame:(CGRect)aFrame delegate:(id)delegate duration:(int)duration curve:(CPAnimationCurve)curve { - var animation = [[_CPWindowFrameAnimation alloc] initWithWindow:self targetFrame:aFrame]; - [animation setDelegate:delegate]; - [animation setAnimationCurve:curve]; - [animation setDuration:duration]; - [animation startAnimation]; + [_frameAnimation stopAnimation]; + _frameAnimation = [[_CPWindowFrameAnimation alloc] initWithWindow:self targetFrame:aFrame]; + [_frameAnimation setDelegate:delegate]; + [_frameAnimation setAnimationCurve:curve]; + [_frameAnimation setDuration:duration]; + [_frameAnimation startAnimation]; } /* @ignore */ @@ -2068,7 +2094,7 @@ CPTexturedBackgroundWindowMask var attachedSheet = [self attachedSheet]; var contentRect = [[self contentView] frame], sheetFrame = CGRectMakeCopy([attachedSheet frame]); - + sheetFrame.origin.y = CGRectGetMinY(_frame) + CGRectGetMinY(contentRect); sheetFrame.origin.x = CGRectGetMinX(_frame) + FLOOR((CGRectGetWidth(_frame) - CGRectGetWidth(sheetFrame)) / 2.0); @@ -2080,8 +2106,8 @@ CPTexturedBackgroundWindowMask { var sheetFrame = [aSheet frame]; - _sheetContext = {"sheet":aSheet, "modalDelegate":aModalDelegate, "endSelector":aDidEndSelector, "contextInfo":aContextInfo, "frame":CGRectMakeCopy(sheetFrame), "returnCode":-1, "opened": NO}; - + _sheetContext = {"sheet":aSheet, "modalDelegate":aModalDelegate, "endSelector":aDidEndSelector, "contextInfo":aContextInfo, "frame":CGRectMakeCopy(sheetFrame), "returnCode":-1, "opened": NO}; + [self _attachSheetWindow:aSheet]; } @@ -2091,12 +2117,12 @@ CPTexturedBackgroundWindowMask var sheetFrame = [aSheet frame], frame = [self frame], sheetContent = [aSheet contentView]; - + [self _setUpMasksForView:sheetContent]; - + aSheet._isSheet = YES; aSheet._parentView = self; - + var originx = frame.origin.x + FLOOR((frame.size.width - sheetFrame.size.width)/2), originy = frame.origin.y + [[self contentView] frame].origin.y, startFrame = CGRectMake(originx, originy, sheetFrame.size.width, 0), @@ -2104,7 +2130,7 @@ CPTexturedBackgroundWindowMask [[CPNotificationCenter defaultCenter] postNotificationName:CPWindowWillBeginSheetNotification object:self]; [CPApp runModalForWindow:aSheet]; - + [aSheet orderFront:self]; [aSheet setFrame:startFrame display:YES animate:NO]; _sheetContext["opened"] = YES; @@ -2112,7 +2138,7 @@ CPTexturedBackgroundWindowMask [aSheet _setFrame:endFrame delegate:self duration:0.2 curve:CPAnimationEaseOut]; // Should run the main loop here until _isAnimating = FALSE - [aSheet becomeKeyWindow]; + [aSheet becomeKeyWindow]; } /* @ignore */ @@ -2123,9 +2149,9 @@ CPTexturedBackgroundWindowMask endFrame = CGRectMakeCopy(startFrame); endFrame.size.height = 0; - + _sheetContext["frame"] = startFrame; - + var sheetContent = [sheet contentView]; [self _setUpMasksForView:sheetContent]; @@ -2141,27 +2167,27 @@ CPTexturedBackgroundWindowMask return; var sheetContent = [sheet contentView]; - + if (_sheetContext["opened"] === YES) { [self _restoreMasksForView:sheetContent]; return; } - - [CPApp stopModal]; + + [CPApp stopModal]; [[CPNotificationCenter defaultCenter] postNotificationName:CPWindowDidEndSheetNotification object:self]; [sheet orderOut:self]; var lastFrame = _sheetContext["frame"]; [sheet setFrame:lastFrame]; - + [self _restoreMasksForView:sheetContent]; var delegate = _sheetContext["modalDelegate"], endSelector = _sheetContext["endSelector"]; - if (delegate != nil && endSelector != nil) + if (delegate != nil && endSelector != nil) objj_msgSend(delegate, endSelector, sheet, _sheetContext["returnCode"], _sheetContext["contextInfo"]); _sheetContext = nil; @@ -2173,7 +2199,7 @@ CPTexturedBackgroundWindowMask var views = [aView subviews]; [views addObject:aView]; - + for (var i = 0, count = [views count]; i < count; i++) { var view = [views objectAtIndex:i], @@ -2189,7 +2215,7 @@ CPTexturedBackgroundWindowMask var views = [aView subviews]; [views addObject:aView]; - + for (var i = 0, count = [views count]; i < count; i++) { var view = [views objectAtIndex:i], @@ -2207,7 +2233,7 @@ CPTexturedBackgroundWindowMask { if (_sheetContext === nil) return nil; - + return _sheetContext["sheet"]; } @@ -2238,7 +2264,7 @@ CPTexturedBackgroundWindowMask return NO; } -- (void)performKeyEquivalent:(CPEvent)anEvent +- (BOOL)performKeyEquivalent:(CPEvent)anEvent { // FIXME: should we be starting at the root, in other words _windowView? // The evidence seems to point to no... @@ -2249,14 +2275,11 @@ CPTexturedBackgroundWindowMask { // It's not clear why we do performKeyEquivalent again here... // Perhaps to allow something to happen between sendEvent: and keyDown:? - if (![anEvent _couldBeKeyEquivalent] || ![self performKeyEquivalent:anEvent]) - [self interpretKeyEvents:[anEvent]]; -} + if ([anEvent _couldBeKeyEquivalent] && [self performKeyEquivalent:anEvent]) + return; -- (void)insertNewline:(id)sender -{ - if (_defaultButton && _defaultButtonEnabled) - [_defaultButton performClick:nil]; + // Interpret the key events + [self interpretKeyEvents:[anEvent]]; } - (void)insertTab:(id)sender @@ -2288,7 +2311,7 @@ CPTexturedBackgroundWindowMask - (void)recalculateKeyViewLoop { var subviews = []; - + [self _appendSubviewsOf:_contentView toArray:subviews]; var keyViewOrder = [subviews sortedArrayUsingFunction:keyViewComparator context:_contentView], @@ -2296,7 +2319,7 @@ CPTexturedBackgroundWindowMask for (var i=0; i @import "CPEvent.j" +@import "CPText.j" @import "CPCompatibility.j" @import "CPDOMWindowLayer.j" @@ -138,10 +139,31 @@ var KeyCodesToPrevent = {}, MozKeyCodeToKeyCodeMap = { 61: 187, // =, equals 59: 186 // ;, semicolon - }; + }, + KeyCodesToFunctionUnicodeMap = {}; KeyCodesToPrevent[CPKeyCodes.A] = YES; +KeyCodesToFunctionUnicodeMap[CPKeyCodes.BACKSPACE] = CPDeleteCharacter; +KeyCodesToFunctionUnicodeMap[CPKeyCodes.DELETE] = CPDeleteFunctionKey; +KeyCodesToFunctionUnicodeMap[CPKeyCodes.TAB] = CPTabCharacter; +KeyCodesToFunctionUnicodeMap[CPKeyCodes.ENTER] = CPCarriageReturnCharacter; +KeyCodesToFunctionUnicodeMap[CPKeyCodes.ESC] = CPEscapeFunctionKey; +KeyCodesToFunctionUnicodeMap[CPKeyCodes.PAGE_UP] = CPPageUpFunctionKey; +KeyCodesToFunctionUnicodeMap[CPKeyCodes.PAGE_DOWN] = CPPageDownFunctionKey; +KeyCodesToFunctionUnicodeMap[CPKeyCodes.LEFT] = CPLeftArrowFunctionKey; +KeyCodesToFunctionUnicodeMap[CPKeyCodes.UP] = CPUpArrowFunctionKey; +KeyCodesToFunctionUnicodeMap[CPKeyCodes.RIGHT] = CPRightArrowFunctionKey; +KeyCodesToFunctionUnicodeMap[CPKeyCodes.DOWN] = CPDownArrowFunctionKey; + +var ModifierKeyCodes = [ + CPKeyCodes.META, + CPKeyCodes.MAC_FF_META, + CPKeyCodes.CTRL, + CPKeyCodes.ALT, + CPKeyCodes.SHIFT +]; + var supportsNativeDragAndDrop = [CPPlatform supportsDragAndDrop]; @implementation CPPlatformWindow (DOM) @@ -306,11 +328,11 @@ var supportsNativeDragAndDrop = [CPPlatform supportsDragAndDrop]; keyEventSelector = @selector(keyEvent:), keyEventImplementation = class_getMethodImplementation(theClass, keyEventSelector), keyEventCallback = function (anEvent) { keyEventImplementation(self, nil, anEvent); }, - + mouseEventSelector = @selector(mouseEvent:), mouseEventImplementation = class_getMethodImplementation(theClass, mouseEventSelector), mouseEventCallback = function (anEvent) { mouseEventImplementation(self, nil, anEvent); }, - + contextMenuEventSelector = @selector(contextMenuEvent:), contextMenuEventImplementation = class_getMethodImplementation(theClass, contextMenuEventSelector), contextMenuEventCallback = function (anEvent) { return contextMenuEventImplementation(self, nil, anEvent); }, @@ -318,7 +340,7 @@ var supportsNativeDragAndDrop = [CPPlatform supportsDragAndDrop]; scrollEventSelector = @selector(scrollEvent:), scrollEventImplementation = class_getMethodImplementation(theClass, scrollEventSelector), scrollEventCallback = function (anEvent) { scrollEventImplementation(self, nil, anEvent); }, - + touchEventSelector = @selector(touchEvent:), touchEventImplementation = class_getMethodImplementation(theClass, touchEventSelector), touchEventCallback = function (anEvent) { touchEventImplementation(self, nil, anEvent); }; @@ -356,7 +378,7 @@ var supportsNativeDragAndDrop = [CPPlatform supportsDragAndDrop]; _DOMWindow.addEventListener("DOMMouseScroll", scrollEventCallback, NO); _DOMWindow.addEventListener("mousewheel", scrollEventCallback, NO); - _DOMWindow.addEventListener("resize", resizeEventCallback, NO); + _DOMWindow.addEventListener("resize", resizeEventCallback, NO); _DOMWindow.addEventListener("unload", function() { @@ -400,16 +422,16 @@ var supportsNativeDragAndDrop = [CPPlatform supportsDragAndDrop]; theDocument.attachEvent("onmousemove", mouseEventCallback); theDocument.attachEvent("ondblclick", mouseEventCallback); theDocument.attachEvent("oncontextmenu", contextMenuEventCallback); - + theDocument.attachEvent("onkeyup", keyEventCallback); theDocument.attachEvent("onkeydown", keyEventCallback); theDocument.attachEvent("onkeypress", keyEventCallback); - + _DOMWindow.attachEvent("onresize", resizeEventCallback); - + _DOMWindow.onmousewheel = scrollEventCallback; theDocument.onmousewheel = scrollEventCallback; - + _DOMBodyElement.ondrag = function () { return NO; }; _DOMBodyElement.onselectstart = function () { return _DOMWindow.event.srcElement === _DOMPasteboardElement; }; @@ -593,15 +615,15 @@ var supportsNativeDragAndDrop = [CPPlatform supportsDragAndDrop]; - (void)keyEvent:(DOMEvent)aDOMEvent { var event, - timestamp = aDOMEvent.timeStamp ? aDOMEvent.timeStamp : new Date(), - sourceElement = (aDOMEvent.target || aDOMEvent.srcElement), + timestamp = aDOMEvent.timeStamp || new Date(), + sourceElement = aDOMEvent.target || aDOMEvent.srcElement, windowNumber = [[CPApp keyWindow] windowNumber], - modifierFlags = (aDOMEvent.shiftKey ? CPShiftKeyMask : 0) | - (aDOMEvent.ctrlKey ? CPControlKeyMask : 0) | - (aDOMEvent.altKey ? CPAlternateKeyMask : 0) | + modifierFlags = (aDOMEvent.shiftKey ? CPShiftKeyMask : 0) | + (aDOMEvent.ctrlKey ? CPControlKeyMask : 0) | + (aDOMEvent.altKey ? CPAlternateKeyMask : 0) | (aDOMEvent.metaKey ? CPCommandKeyMask : 0); - //We want to stop propagation if this is a command key AND this character or keycode has been added to our blacklist + //We want to stop propagation if this is a command key AND this character or keycode has been added to our blacklist StopDOMEventPropagation = !!(!(modifierFlags & (CPControlKeyMask | CPCommandKeyMask)) || CharacterKeysToPrevent[String.fromCharCode(aDOMEvent.keyCode || aDOMEvent.charCode).toLowerCase()] || KeyCodesToPrevent[aDOMEvent.keyCode]); @@ -613,19 +635,36 @@ var supportsNativeDragAndDrop = [CPPlatform supportsDragAndDrop]; switch (aDOMEvent.type) { case "keydown": // Grab and store the keycode now since it is correct and consistent at this point. - if (aDOMEvent.keyCode.keyCode in MozKeyCodeToKeyCodeMap) + if (aDOMEvent.keyCode in MozKeyCodeToKeyCodeMap) _keyCode = MozKeyCodeToKeyCodeMap[aDOMEvent.keyCode]; else _keyCode = aDOMEvent.keyCode; - var characters = String.fromCharCode(_keyCode).toLowerCase(); + var characters; + + // Is this a special key? + if (aDOMEvent.which === 0 || aDOMEvent.charCode === 0) + characters = KeyCodesToFunctionUnicodeMap[_keyCode]; + + if (!characters) + characters = String.fromCharCode(_keyCode).toLowerCase(); + overrideCharacters = (modifierFlags & CPShiftKeyMask || _capsLockActive) ? characters.toUpperCase() : characters; // check for caps lock state if (_keyCode === CPKeyCodes.CAPS_LOCK) _capsLockActive = YES; - if (modifierFlags & (CPControlKeyMask | CPCommandKeyMask)) + if ([ModifierKeyCodes containsObject:_keyCode]) + { + // A modifier key will never fire keypress. We don't need to do any other processing so we just fire it here and break. + event = [CPEvent keyEventWithType:CPFlagsChanged location:location modifierFlags:modifierFlags + timestamp:timestamp windowNumber:windowNumber context:nil + characters:nil charactersIgnoringModifiers:nil isARepeat:NO keyCode:_keyCode]; + + break; + } + else if (modifierFlags & (CPControlKeyMask | CPCommandKeyMask)) { //we are simply going to skip all keypress events that use cmd/ctrl key //this lets us be consistent in all browsers and send on the keydown @@ -633,7 +672,7 @@ var supportsNativeDragAndDrop = [CPPlatform supportsDragAndDrop]; var eligibleForCopyPaste = [self _validateCopyCutOrPasteEvent:aDOMEvent flags:modifierFlags]; - // If this could be a native PASTE event, then we need to further examine it before + // If this could be a native PASTE event, then we need to further examine it before // sending a CPEvent. Select our element to see if anything gets pasted in it. if (characters === "v" && eligibleForCopyPaste) { @@ -646,7 +685,7 @@ var supportsNativeDragAndDrop = [CPPlatform supportsDragAndDrop]; isNativePasteEvent = YES; } - // However, of this could be a native COPY event, we need to let the normal event-process take place so it + // However, of this could be a native COPY event, we need to let the normal event-process take place so it // can capture our internal Cappuccino pasteboard. else if ((characters == "c" || characters == "x") && eligibleForCopyPaste) { @@ -668,10 +707,10 @@ var supportsNativeDragAndDrop = [CPPlatform supportsDragAndDrop]; //this branch is taken by "remedial" key events // In this state we continue to keypress and send the CPEvent } - + case "keypress": - // we unconditionally break on keypress events with modifiers, - // because we forced the event to be sent on the keydown + // we unconditionally break on keypress events with modifiers, + // because we forced the event to be sent on the keydown if (aDOMEvent.type === "keypress" && (modifierFlags & (CPControlKeyMask | CPCommandKeyMask))) break; @@ -682,8 +721,15 @@ var supportsNativeDragAndDrop = [CPPlatform supportsDragAndDrop]; _lastKey = keyCode; _charCodes[keyCode] = charCode; - var characters = overrideCharacters || String.fromCharCode(charCode), - charactersIgnoringModifiers = characters.toLowerCase(); + var characters = overrideCharacters; + // Is this a special key? + if (!characters && (aDOMEvent.which === 0 || aDOMEvent.charCode === 0)) + characters = KeyCodesToFunctionUnicodeMap[charCode]; + + if (!characters) + characters = String.fromCharCode(charCode); + + charactersIgnoringModifiers = characters.toLowerCase(); // FIXME: This isn't correct. It SHOULD include Shift. // Safari won't send proper capitalization during cmd-key events if (!overrideCharacters && (modifierFlags & CPCommandKeyMask) && ((modifierFlags & CPShiftKeyMask) || _capsLockActive)) @@ -691,7 +737,7 @@ var supportsNativeDragAndDrop = [CPPlatform supportsDragAndDrop]; event = [CPEvent keyEventWithType:CPKeyDown location:location modifierFlags:modifierFlags timestamp:timestamp windowNumber:windowNumber context:nil - characters:characters charactersIgnoringModifiers:charactersIgnoringModifiers isARepeat:isARepeat keyCode:keyCode]; + characters:characters charactersIgnoringModifiers:charactersIgnoringModifiers isARepeat:isARepeat keyCode:charCode]; if (isNativePasteEvent) { @@ -700,10 +746,10 @@ var supportsNativeDragAndDrop = [CPPlatform supportsDragAndDrop]; } break; - + case "keyup": var keyCode = aDOMEvent.keyCode, charCode = _charCodes[keyCode]; - + _keyCode = -1; _lastKey = -1; _charCodes[keyCode] = nil; @@ -714,12 +760,15 @@ var supportsNativeDragAndDrop = [CPPlatform supportsDragAndDrop]; if (keyCode === CPKeyCodes.CAPS_LOCK) _capsLockActive = NO; - var characters = String.fromCharCode(charCode), + if ([ModifierKeyCodes containsObject:keyCode]) + break; + + var characters = KeyCodesToFunctionUnicodeMap[charCode] || String.fromCharCode(charCode), charactersIgnoringModifiers = characters.toLowerCase(); - + if (!(modifierFlags & CPShiftKeyMask) && (modifierFlags & CPCommandKeyMask) && !_capsLockActive) characters = charactersIgnoringModifiers; - + event = [CPEvent keyEventWithType:CPKeyUp location:location modifierFlags:modifierFlags timestamp: timestamp windowNumber:windowNumber context:nil characters:characters charactersIgnoringModifiers:charactersIgnoringModifiers isARepeat:NO keyCode:keyCode]; @@ -867,10 +916,10 @@ var supportsNativeDragAndDrop = [CPPlatform supportsDragAndDrop]; { x += element.offsetLeft; y += element.offsetTop; - + } while (element = element.offsetParent); } - + location = _CGPointMake((x + ((aDOMEvent.clientX - 8) / 15)), (y + ((aDOMEvent.clientY - 8) / 15))); } else if (aDOMEvent._overrideLocation) @@ -882,9 +931,9 @@ var supportsNativeDragAndDrop = [CPPlatform supportsDragAndDrop]; deltaY = 0.0, windowNumber = 0, timestamp = aDOMEvent.timeStamp ? aDOMEvent.timeStamp : new Date(), - modifierFlags = (aDOMEvent.shiftKey ? CPShiftKeyMask : 0) | - (aDOMEvent.ctrlKey ? CPControlKeyMask : 0) | - (aDOMEvent.altKey ? CPAlternateKeyMask : 0) | + modifierFlags = (aDOMEvent.shiftKey ? CPShiftKeyMask : 0) | + (aDOMEvent.ctrlKey ? CPControlKeyMask : 0) | + (aDOMEvent.altKey ? CPAlternateKeyMask : 0) | (aDOMEvent.metaKey ? CPCommandKeyMask : 0); StopDOMEventPropagation = YES; @@ -903,31 +952,31 @@ var supportsNativeDragAndDrop = [CPPlatform supportsDragAndDrop]; deltaX = aDOMEvent.wheelDeltaX / 120.0; deltaY = aDOMEvent.wheelDeltaY / 120.0; } - + else if (aDOMEvent.wheelDelta) deltaY = aDOMEvent.wheelDelta / 120.0; - - else if (aDOMEvent.detail) + + else if (aDOMEvent.detail) deltaY = -aDOMEvent.detail / 3.0; - + else - return; + return; if(!CPFeatureIsCompatible(CPJavaScriptNegativeMouseWheelValues)) { deltaX = -deltaX; deltaY = -deltaY; } - + var event = [CPEvent mouseEventWithType:CPScrollWheel location:location modifierFlags:modifierFlags timestamp:timestamp windowNumber:windowNumber context:nil eventNumber:-1 clickCount:1 pressure:0 ]; - + event._DOMEvent = aDOMEvent; event._deltaX = deltaX; event._deltaY = deltaY; - + [CPApp sendEvent:event]; - + if (StopDOMEventPropagation) CPDOMEventStop(aDOMEvent, self); @@ -970,7 +1019,7 @@ var supportsNativeDragAndDrop = [CPPlatform supportsDragAndDrop]; if (aDOMEvent.touches && (aDOMEvent.touches.length == 1 || (aDOMEvent.touches.length == 0 && aDOMEvent.changedTouches.length == 1))) { var newEvent = {}; - + switch(aDOMEvent.type) { case CPDOMEventTouchStart: newEvent.type = CPDOMEventMouseDown; @@ -984,27 +1033,27 @@ var supportsNativeDragAndDrop = [CPPlatform supportsDragAndDrop]; } var touch = aDOMEvent.touches.length ? aDOMEvent.touches[0] : aDOMEvent.changedTouches[0]; - + newEvent.clientX = touch.clientX; newEvent.clientY = touch.clientY; - + newEvent.timestamp = aDOMEvent.timestamp; newEvent.target = aDOMEvent.target; - + newEvent.shiftKey = newEvent.ctrlKey = newEvent.altKey = newEvent.metaKey = false; - + newEvent.preventDefault = function(){if(aDOMEvent.preventDefault) aDOMEvent.preventDefault()}; newEvent.stopPropagation = function(){if(aDOMEvent.stopPropagation) aDOMEvent.stopPropagation()}; - + [self mouseEvent:newEvent]; - + return; } else { if (aDOMEvent.preventDefault) aDOMEvent.preventDefault(); - + if (aDOMEvent.stopPropagation) aDOMEvent.stopPropagation(); } @@ -1026,7 +1075,7 @@ var supportsNativeDragAndDrop = [CPPlatform supportsDragAndDrop]; _overriddenEventType = nil; - return; + return; } var event, @@ -1034,9 +1083,9 @@ var supportsNativeDragAndDrop = [CPPlatform supportsDragAndDrop]; timestamp = aDOMEvent.timeStamp ? aDOMEvent.timeStamp : new Date(), sourceElement = (aDOMEvent.target || aDOMEvent.srcElement), windowNumber = 0, - modifierFlags = (aDOMEvent.shiftKey ? CPShiftKeyMask : 0) | - (aDOMEvent.ctrlKey ? CPControlKeyMask : 0) | - (aDOMEvent.altKey ? CPAlternateKeyMask : 0) | + modifierFlags = (aDOMEvent.shiftKey ? CPShiftKeyMask : 0) | + (aDOMEvent.ctrlKey ? CPControlKeyMask : 0) | + (aDOMEvent.altKey ? CPAlternateKeyMask : 0) | (aDOMEvent.metaKey ? CPCommandKeyMask : 0); StopDOMEventPropagation = YES; @@ -1062,7 +1111,7 @@ var supportsNativeDragAndDrop = [CPPlatform supportsDragAndDrop]; if(_mouseIsDown) { event = _CPEventFromNativeMouseEvent(aDOMEvent, _mouseDownIsRightClick ? CPRightMouseUp : CPLeftMouseUp, location, modifierFlags, timestamp, windowNumber, nil, -1, CPDOMEventGetClickCount(_lastMouseUp, timestamp, location), 0); - + _mouseIsDown = NO; _lastMouseUp = event; _mouseDownWindow = nil; @@ -1075,7 +1124,7 @@ var supportsNativeDragAndDrop = [CPPlatform supportsDragAndDrop]; return; } } - + else if (type === "mousedown") { if (sourceElement.tagName === "INPUT" && sourceElement != _DOMFocusElement) @@ -1091,11 +1140,11 @@ var supportsNativeDragAndDrop = [CPPlatform supportsDragAndDrop]; //fake a down and up event so that event tracking mode will work correctly [CPApp sendEvent:[CPEvent mouseEventWithType:CPLeftMouseDown location:location modifierFlags:modifierFlags - timestamp:timestamp windowNumber:windowNumber context:nil eventNumber:-1 + timestamp:timestamp windowNumber:windowNumber context:nil eventNumber:-1 clickCount:CPDOMEventGetClickCount(_lastMouseDown, timestamp, location) pressure:0]]; [CPApp sendEvent:[CPEvent mouseEventWithType:CPLeftMouseUp location:location modifierFlags:modifierFlags - timestamp:timestamp windowNumber:windowNumber context:nil eventNumber:-1 + timestamp:timestamp windowNumber:windowNumber context:nil eventNumber:-1 clickCount:CPDOMEventGetClickCount(_lastMouseDown, timestamp, location) pressure:0]]; return; @@ -1116,7 +1165,7 @@ var supportsNativeDragAndDrop = [CPPlatform supportsDragAndDrop]; _mouseIsDown = YES; _lastMouseDown = event; } - + else // if (type === "mousemove" || type === "drag") { if (_DOMEventMode) @@ -1130,7 +1179,7 @@ var supportsNativeDragAndDrop = [CPPlatform supportsDragAndDrop]; if (event && (!isDragging || !supportsNativeDragAndDrop)) { event._DOMEvent = aDOMEvent; - + [CPApp sendEvent:event]; } @@ -1157,7 +1206,7 @@ var supportsNativeDragAndDrop = [CPPlatform supportsDragAndDrop]; if (!layer) return []; - + return [layer orderedWindows]; } @@ -1170,19 +1219,19 @@ var supportsNativeDragAndDrop = [CPPlatform supportsDragAndDrop]; if (!layer && aFlag) { layer = [[CPDOMWindowLayer alloc] initWithLevel:aLevel]; - + [_windowLayers setObject:layer forKey:aLevel]; - // Find the nearest layer. This is similar to a binary search, + // Find the nearest layer. This is similar to a binary search, // only we know we won't find the value. var low = 0, high = _windowLevels.length - 1, middle; - + while (low <= high) { middle = FLOOR((low + high) / 2); - + if (_windowLevels[middle] > aLevel) high = middle - 1; else @@ -1190,7 +1239,7 @@ var supportsNativeDragAndDrop = [CPPlatform supportsDragAndDrop]; } var insertionIndex = 0; - if (middle !== undefined) + if (middle !== undefined) insertionIndex = _windowLevels[middle] > aLevel ? middle : middle + 1 [_windowLevels insertObject:aLevel atIndex:insertionIndex]; @@ -1198,7 +1247,7 @@ var supportsNativeDragAndDrop = [CPPlatform supportsDragAndDrop]; _DOMBodyElement.appendChild(layer._DOMElement); } - + return layer; } @@ -1206,11 +1255,11 @@ var supportsNativeDragAndDrop = [CPPlatform supportsDragAndDrop]; { [CPPlatform initializeScreenIfNecessary]; - // Grab the appropriate level for the layer, and create it if + // Grab the appropriate level for the layer, and create it if // necessary (if we are not simply removing the window). var layer = [self layerAtLevel:[aWindow level] create:aPlace !== CPWindowOut]; - // Ignore otherWindow, simply remove this window from it's level. + // Ignore otherWindow, simply remove this window from it's level. // If layer is nil, this will be a no-op. if (aPlace === CPWindowOut) return [layer removeWindow:aWindow]; @@ -1263,10 +1312,10 @@ var supportsNativeDragAndDrop = [CPPlatform supportsDragAndDrop]; // Skip any windows above or at the dragging level. if (levels[levelCount] >= CPDraggingWindowLevel) continue; - + var windows = [layers objectForKey:levels[levelCount]]._windows, windowCount = windows.length; - + while (windowCount--) { var theWindow = windows[windowCount]; @@ -1278,7 +1327,7 @@ var supportsNativeDragAndDrop = [CPPlatform supportsDragAndDrop]; return [theWindow _dragHitTest:aPoint pasteboard:aPasteboard]; } } - + return nil; } @@ -1308,7 +1357,7 @@ var supportsNativeDragAndDrop = [CPPlatform supportsDragAndDrop]; - (CPWindow)hitTest:(CPPoint)location { - if (self._only) + if (self._only) return self._only; var levels = _windowLevels, @@ -1324,7 +1373,7 @@ var supportsNativeDragAndDrop = [CPPlatform supportsDragAndDrop]; while (windowCount-- && !theWindow) { var candidateWindow = windows[windowCount]; - + if (!candidateWindow._ignoresMouseEvents && [candidateWindow containsPoint:location]) theWindow = candidateWindow; } @@ -1334,10 +1383,10 @@ var supportsNativeDragAndDrop = [CPPlatform supportsDragAndDrop]; } /*! - When using command (mac) or control (windows), keys are propagated to the browser by default. + When using command (mac) or control (windows), keys are propagated to the browser by default. To prevent a character key from propagating (to prevent its default action, and instead use it in your own application), use these methods. These methods are additive -- the list builds until you clear it. - + @param characters a list of characters to stop propagating keypresses to the browser. */ + (void)preventCharacterKeysFromPropagating:(CPArray)characters @@ -1418,11 +1467,11 @@ var CPDOMEventGetClickCount = function(aComparisonEvent, aTimestamp, aLocation) { if (!aComparisonEvent) return 1; - + var comparisonLocation = [aComparisonEvent locationInWindow]; - - return (aTimestamp - [aComparisonEvent timestamp] < CLICK_TIME_DELTA && - ABS(comparisonLocation.x - aLocation.x) < CLICK_SPACE_DELTA && + + return (aTimestamp - [aComparisonEvent timestamp] < CLICK_TIME_DELTA && + ABS(comparisonLocation.x - aLocation.x) < CLICK_SPACE_DELTA && ABS(comparisonLocation.y - aLocation.y) < CLICK_SPACE_DELTA) ? [aComparisonEvent clickCount] + 1 : 1; } diff --git a/AppKit/Resources/CPWindow/HUD/CPWindowHUDBackground0.png b/AppKit/Resources/CPWindow/HUD/CPWindowHUDBackground0.png index f43a3aad2..49f0f1bbf 100644 Binary files a/AppKit/Resources/CPWindow/HUD/CPWindowHUDBackground0.png and b/AppKit/Resources/CPWindow/HUD/CPWindowHUDBackground0.png differ diff --git a/AppKit/Resources/CPWindow/HUD/CPWindowHUDBackground1.png b/AppKit/Resources/CPWindow/HUD/CPWindowHUDBackground1.png index ee4dba58a..03afa8531 100644 Binary files a/AppKit/Resources/CPWindow/HUD/CPWindowHUDBackground1.png and b/AppKit/Resources/CPWindow/HUD/CPWindowHUDBackground1.png differ diff --git a/AppKit/Resources/CPWindow/HUD/CPWindowHUDBackground2.png b/AppKit/Resources/CPWindow/HUD/CPWindowHUDBackground2.png index 76f41a64e..049910091 100644 Binary files a/AppKit/Resources/CPWindow/HUD/CPWindowHUDBackground2.png and b/AppKit/Resources/CPWindow/HUD/CPWindowHUDBackground2.png differ diff --git a/AppKit/Resources/CPWindow/HUD/CPWindowHUDBackground3.png b/AppKit/Resources/CPWindow/HUD/CPWindowHUDBackground3.png index a38e4dad1..3ba64c822 100644 Binary files a/AppKit/Resources/CPWindow/HUD/CPWindowHUDBackground3.png and b/AppKit/Resources/CPWindow/HUD/CPWindowHUDBackground3.png differ diff --git a/AppKit/Resources/CPWindow/HUD/CPWindowHUDBackground4.png b/AppKit/Resources/CPWindow/HUD/CPWindowHUDBackground4.png index 4d450f53a..eb0bc87f3 100644 Binary files a/AppKit/Resources/CPWindow/HUD/CPWindowHUDBackground4.png and b/AppKit/Resources/CPWindow/HUD/CPWindowHUDBackground4.png differ diff --git a/AppKit/Resources/CPWindow/HUD/CPWindowHUDBackground5.png b/AppKit/Resources/CPWindow/HUD/CPWindowHUDBackground5.png index b49ebef35..260cc579c 100644 Binary files a/AppKit/Resources/CPWindow/HUD/CPWindowHUDBackground5.png and b/AppKit/Resources/CPWindow/HUD/CPWindowHUDBackground5.png differ diff --git a/AppKit/Resources/CPWindow/HUD/CPWindowHUDBackground6.png b/AppKit/Resources/CPWindow/HUD/CPWindowHUDBackground6.png index a2cebe44f..01fe3ad8a 100644 Binary files a/AppKit/Resources/CPWindow/HUD/CPWindowHUDBackground6.png and b/AppKit/Resources/CPWindow/HUD/CPWindowHUDBackground6.png differ diff --git a/AppKit/Resources/CPWindow/HUD/CPWindowHUDBackground7.png b/AppKit/Resources/CPWindow/HUD/CPWindowHUDBackground7.png index d1b3faf37..6300ed5d6 100644 Binary files a/AppKit/Resources/CPWindow/HUD/CPWindowHUDBackground7.png and b/AppKit/Resources/CPWindow/HUD/CPWindowHUDBackground7.png differ diff --git a/AppKit/Resources/CPWindow/HUD/CPWindowHUDBackground8.png b/AppKit/Resources/CPWindow/HUD/CPWindowHUDBackground8.png index 60e25e8e0..14c5c58e1 100644 Binary files a/AppKit/Resources/CPWindow/HUD/CPWindowHUDBackground8.png and b/AppKit/Resources/CPWindow/HUD/CPWindowHUDBackground8.png differ diff --git a/AppKit/Resources/HUDTheme/WindowClose.png b/AppKit/Resources/HUDTheme/WindowClose.png index 4d3d405cf..17ba6f603 100644 Binary files a/AppKit/Resources/HUDTheme/WindowClose.png and b/AppKit/Resources/HUDTheme/WindowClose.png differ diff --git a/AppKit/Resources/HUDTheme/WindowCloseActive.png b/AppKit/Resources/HUDTheme/WindowCloseActive.png index f42282358..5fa29fed3 100644 Binary files a/AppKit/Resources/HUDTheme/WindowCloseActive.png and b/AppKit/Resources/HUDTheme/WindowCloseActive.png differ diff --git a/AppKit/Themes/Aristo/Resources/HUD/knob-disabled.png b/AppKit/Themes/Aristo/Resources/HUD/knob-disabled.png new file mode 100644 index 000000000..fc6e54041 Binary files /dev/null and b/AppKit/Themes/Aristo/Resources/HUD/knob-disabled.png differ diff --git a/CommonJS/lib/cappuccino/cib-analysis-tools.j b/CommonJS/lib/cappuccino/cib-analysis-tools.j index cab6a919a..2427987f8 100644 --- a/CommonJS/lib/cappuccino/cib-analysis-tools.j +++ b/CommonJS/lib/cappuccino/cib-analysis-tools.j @@ -17,7 +17,7 @@ function findCibClassDependencies(cibPath) { } // make sure CPApp is init'd - [CPApplication sharedApplication] + [CPApplication sharedApplication]; try { var x = [cib pressInstantiate]; @@ -61,7 +61,7 @@ function findCibClassDependencies(cibPath) { var topLevelObjects = nil;//[anExternalNameTable objectForKey:CPCibTopLevelObjects]; - [objectData instantiateWithOwner:owner topLevelObjects:topLevelObjects] + [objectData instantiateWithOwner:owner topLevelObjects:topLevelObjects]; // [objectData establishConnectionsWithOwner:owner topLevelObjects:topLevelObjects]; // [objectData awakeWithOwner:owner topLevelObjects:topLevelObjects]; diff --git a/Foundation/CPArray.j b/Foundation/CPArray.j index 70d33f9db..27acbd1f2 100755 --- a/Foundation/CPArray.j +++ b/Foundation/CPArray.j @@ -796,7 +796,7 @@ */ - (CPArray)sortedArrayUsingSelector:(SEL)aSelector { - var sorted = [self copy] + var sorted = [self copy]; [sorted sortUsingSelector:aSelector]; diff --git a/Foundation/CPBundle.j b/Foundation/CPBundle.j index 39aae0b29..89c082c4b 100644 --- a/Foundation/CPBundle.j +++ b/Foundation/CPBundle.j @@ -118,6 +118,16 @@ var CPBundlesForURLStrings = { }; return className ? CPClassFromString(className) : Nil; } +- (CPString)bundleIdentifier +{ + return [self objectForInfoDictionaryKey:@"CPBundleIdentifier"]; +} + +- (BOOL)isLoaded +{ + return _bundle.isLoaded(); +} + - (CPString)pathForResource:(CPString)aFilename { return _bundle.pathForResource(aFilename); @@ -133,8 +143,6 @@ var CPBundlesForURLStrings = { }; return _bundle.valueForInfoDictionaryKey(aKey); } -// - - (void)loadWithDelegate:(id)aDelegate { _delegate = aDelegate; diff --git a/Foundation/CPDate.j b/Foundation/CPDate.j index 6340ef48c..cba9ec382 100644 --- a/Foundation/CPDate.j +++ b/Foundation/CPDate.j @@ -177,10 +177,11 @@ var CPDateReferenceDate = new Date(Date.UTC(2001, 1, 1, 0, 0, 0, 0)); */ - (CPString)description { - var hours = Math.floor(self.getTimezoneOffset() / 60), + var positive = self.getTimezoneOffset() >= 0, + hours = FLOOR(self.getTimezoneOffset() / 60), minutes = self.getTimezoneOffset() - hours * 60; - return [CPString stringWithFormat:@"%04d-%02d-%02d %02d:%02d:%02d +%02d%02d", self.getFullYear(), self.getMonth() + 1, self.getDate(), self.getHours(), self.getMinutes(), self.getSeconds(), hours, minutes]; + return [CPString stringWithFormat:@"%04d-%02d-%02d %02d:%02d:%02d %s%02d%02d", self.getFullYear(), self.getMonth()+1, self.getDate(), self.getHours(), self.getMinutes(), self.getSeconds(), positive ? "+" : "-", ABS(hours), ABS(minutes)]; } - (id)copy diff --git a/Foundation/CPKeyValueObserving.j b/Foundation/CPKeyValueObserving.j index 66d08fbb0..0ff9fdd26 100644 --- a/Foundation/CPKeyValueObserving.j +++ b/Foundation/CPKeyValueObserving.j @@ -426,6 +426,11 @@ var kvoNewAndOld = CPKeyValueObservingOptionNew|CPKeyValueObservingOptionOld, } else { + // The isBefore path may not have been called as would happen if didChangeX + // was called alone. + if (!changes) + changes = [CPDictionary new]; + [changes removeObjectForKey:CPKeyValueChangeNotificationIsPriorKey]; var indexes = [changes objectForKey:CPKeyValueChangeIndexesKey]; @@ -738,7 +743,7 @@ var _kvoInsertMethodForMethod = function _kvoInsertMethodForMethod(theKey, theMe { [self willChange:CPKeyValueChangeInsertion valuesAtIndexes:[CPIndexSet indexSetWithIndex:index] forKey:theKey]; theMethod.method_imp(self, _cmd, object, index); - [self didChange:CPKeyValueChangeInsertion valuesAtIndexes:[CPIndexSet indexSetWithIndex:index] forKey:theKey] + [self didChange:CPKeyValueChangeInsertion valuesAtIndexes:[CPIndexSet indexSetWithIndex:index] forKey:theKey]; } } @@ -748,7 +753,7 @@ var _kvoReplaceMethodForMethod = function _kvoReplaceMethodForMethod(theKey, the { [self willChange:CPKeyValueChangeReplacement valuesAtIndexes:[CPIndexSet indexSetWithIndex:index] forKey:theKey]; theMethod.method_imp(self, _cmd, index, object); - [self didChange:CPKeyValueChangeReplacement valuesAtIndexes:[CPIndexSet indexSetWithIndex:index] forKey:theKey] + [self didChange:CPKeyValueChangeReplacement valuesAtIndexes:[CPIndexSet indexSetWithIndex:index] forKey:theKey]; } } @@ -758,7 +763,7 @@ var _kvoRemoveMethodForMethod = function _kvoRemoveMethodForMethod(theKey, theMe { [self willChange:CPKeyValueChangeRemoval valuesAtIndexes:[CPIndexSet indexSetWithIndex:index] forKey:theKey]; theMethod.method_imp(self, _cmd, index); - [self didChange:CPKeyValueChangeRemoval valuesAtIndexes:[CPIndexSet indexSetWithIndex:index] forKey:theKey] + [self didChange:CPKeyValueChangeRemoval valuesAtIndexes:[CPIndexSet indexSetWithIndex:index] forKey:theKey]; } } diff --git a/Foundation/CPObject.j b/Foundation/CPObject.j index c518f4f70..34d023421 100644 --- a/Foundation/CPObject.j +++ b/Foundation/CPObject.j @@ -548,7 +548,7 @@ CPLog(@"Got some class: %@", inst); objj_class.prototype.toString = objj_object.prototype.toString = function() { if (this.isa && class_getInstanceMethod(this.isa, "description") != NULL) - return [this description] + return [this description]; else return String(this) + " (-description not implemented)"; } diff --git a/Foundation/CPTimer.j b/Foundation/CPTimer.j index 6e14ba46a..5c25f1c59 100644 --- a/Foundation/CPTimer.j +++ b/Foundation/CPTimer.j @@ -62,7 +62,7 @@ */ + (CPTimer)scheduledTimerWithTimeInterval:(CPTimeInterval)seconds target:(id)aTarget selector:(SEL)aSelector userInfo:(id)userInfo repeats:(BOOL)shouldRepeat { - var timer = [[self alloc] initWithFireDate:[CPDate dateWithTimeIntervalSinceNow:seconds] interval:seconds target:aTarget selector:aSelector userInfo:userInfo repeats:shouldRepeat] + var timer = [[self alloc] initWithFireDate:[CPDate dateWithTimeIntervalSinceNow:seconds] interval:seconds target:aTarget selector:aSelector userInfo:userInfo repeats:shouldRepeat]; //add to the runloop [[CPRunLoop currentRunLoop] addTimer:timer forMode:CPDefaultRunLoopMode]; diff --git a/Foundation/CPUndoManager.j b/Foundation/CPUndoManager.j index f07247589..7f0b58a83 100644 --- a/Foundation/CPUndoManager.j +++ b/Foundation/CPUndoManager.j @@ -726,6 +726,12 @@ if (_currentGroup == nil) change:(CPDictionary)aChange context:(id)aContext { + // Don't add no-ops to the undo stack. + var before = [aChange valueForKey:CPKeyValueChangeOldKey], + after = [aChange valueForKey:CPKeyValueChangeNewKey]; + if (before === after || (before !== nil && before.isa && (after === nil || after.isa) && [before isEqual:after])) + return; + [[self prepareWithInvocationTarget:anObject] applyChange:[aChange inverseChangeDictionary] toKeyPath:aKeyPath]; diff --git a/Objective-J/CFBundle.js b/Objective-J/CFBundle.js index 8c7044e73..03639c8b0 100644 --- a/Objective-J/CFBundle.js +++ b/Objective-J/CFBundle.js @@ -235,6 +235,11 @@ CFBundle.prototype.isLoading = function() return this._loadStatus & CFBundleLoading; } +CFBundle.prototype.isLoaded = function() +{ + return this._loadStatus & CFBundleLoaded; +} + DISPLAY_NAME(CFBundle.prototype.isLoading); CFBundle.prototype.load = function(/*BOOL*/ shouldExecute) diff --git a/Objective-J/CFData.js b/Objective-J/CFData.js index 51743a5a9..7898756f7 100644 --- a/Objective-J/CFData.js +++ b/Objective-J/CFData.js @@ -227,6 +227,11 @@ CFData.decodeBase64ToString = function(input, strip) return CFData.bytesToString(CFData.decodeBase64ToArray(input, strip)); } +CFData.decodeBase64ToUtf16String = function(input, strip) +{ + return CFData.bytesToUtf16String(CFData.decodeBase64ToArray(input, strip)); +} + CFData.bytesToString = function(bytes) { // This is relatively efficient, I think: @@ -242,3 +247,28 @@ CFData.encodeBase64String = function(input) return CFData.encodeBase64Array(temp); } + +CFData.bytesToUtf16String = function(bytes) +{ + // Strings are encoded with 16 bits per character. + var temp = []; + for (var i = 0; i < bytes.length; i+=2) + temp.push(bytes[i+1] << 8 | bytes[i]); + // This is relatively efficient, I think: + return String.fromCharCode.apply(NULL, temp); +} + + +CFData.encodeBase64Utf16String = function(input) +{ + // charCodeAt returns UTF-16. + var temp = []; + for (var i = 0; i < input.length; i++) + { + var c = input.charCodeAt(i); + temp.push(input.charCodeAt(i) & 0xFF); + temp.push((input.charCodeAt(i) & 0xFF00) >> 8); + } + + return CFData.encodeBase64Array(temp); +} diff --git a/Objective-J/CFHTTPRequest.js b/Objective-J/CFHTTPRequest.js index d4007f585..2ef8c721f 100644 --- a/Objective-J/CFHTTPRequest.js +++ b/Objective-J/CFHTTPRequest.js @@ -219,12 +219,6 @@ CFHTTPRequest.prototype.open = function(/*String*/ aMethod, /*String*/ aURL, /*B CFHTTPRequest.prototype.send = function(/*Object*/ aBody) { - for (var i in this._requestHeaders) - { - if (this._requestHeaders.hasOwnProperty(i)) - this._nativeRequest.setRequestHeader(i, this._requestHeaders[i]); - } - if (!this._isOpen) { delete this._nativeRequest.onreadystatechange; @@ -232,6 +226,12 @@ CFHTTPRequest.prototype.send = function(/*Object*/ aBody) this._nativeRequest.onreadystatechange = this._stateChangeHandler; } + for (var i in this._requestHeaders) + { + if (this._requestHeaders.hasOwnProperty(i)) + this._nativeRequest.setRequestHeader(i, this._requestHeaders[i]); + } + if (this._mimeType && "overrideMimeType" in this._nativeRequest) this._nativeRequest.overrideMimeType(this._mimeType); diff --git a/Objective-J/CFPropertyList.js b/Objective-J/CFPropertyList.js index 39ba97e13..5faeb473b 100644 --- a/Objective-J/CFPropertyList.js +++ b/Objective-J/CFPropertyList.js @@ -306,6 +306,8 @@ var XML_XML = "xml", #define PARENT_NODE(anXMLNode) (anXMLNode.parentNode) #define DOCUMENT_ELEMENT(aDocument) (aDocument.documentElement) +#define HAS_ATTRIBUTE_VALUE(anXMLNode, anAttributeName, aValue) (anXMLNode.getAttribute(anAttributeName) === aValue) + #define IS_OF_TYPE(anXMLNode, aType) (NODE_NAME(anXMLNode) === aType) #define IS_PLIST(anXMLNode) IS_OF_TYPE(anXMLNode, PLIST_PLIST) @@ -559,13 +561,17 @@ CFPropertyList.propertyListFromXML = function(/*String | XMLNode*/ aStringOrXMLN case PLIST_DICTIONARY: object = new CFMutableDictionary(); containers.push(object); break; - + case PLIST_NUMBER_REAL: object = parseFloat(CHILD_VALUE(XMLNode)); break; case PLIST_NUMBER_INTEGER: object = parseInt(CHILD_VALUE(XMLNode), 10); break; - - case PLIST_STRING: object = decodeHTMLComponent(FIRST_CHILD(XMLNode) ? CHILD_VALUE(XMLNode) : ""); + + case PLIST_STRING: if (HAS_ATTRIBUTE_VALUE(XMLNode, "type", "base64")) + object = FIRST_CHILD(XMLNode) ? CFData.decodeBase64ToString(CHILD_VALUE(XMLNode)) : ""; + else + object = decodeHTMLComponent(FIRST_CHILD(XMLNode) ? CHILD_VALUE(XMLNode) : ""); + break; case PLIST_BOOLEAN_TRUE: object = YES; diff --git a/Objective-J/Runtime.js b/Objective-J/Runtime.js index 1df7ab26f..7a88ebb9a 100644 --- a/Objective-J/Runtime.js +++ b/Objective-J/Runtime.js @@ -50,7 +50,7 @@ GLOBAL(objj_method) = function(/*String*/ aName, /*IMP*/ anImplementation, /*Str DISPLAY_NAME(objj_method); -GLOBAL(objj_class) = function() +GLOBAL(objj_class) = function(displayName) { this.isa = NULL; @@ -67,7 +67,14 @@ GLOBAL(objj_class) = function() 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 + this.allocator = eval("(function " + (displayName || "OBJJ_OBJECT").replace(/\W/g, "_") + "() { })"); +#else this.allocator = function() { }; +#endif + this._UID = -1; } @@ -325,8 +332,8 @@ var REGISTERED_CLASSES = { }; GLOBAL(objj_allocateClassPair) = function(/*Class*/ superclass, /*String*/ aName) { - var classObject = new objj_class(), - metaClassObject = new objj_class(), + 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. diff --git a/Tests/AppKit/CPButtonTest.j b/Tests/AppKit/CPButtonTest.j index 998053007..e922c03b7 100644 --- a/Tests/AppKit/CPButtonTest.j +++ b/Tests/AppKit/CPButtonTest.j @@ -1,6 +1,7 @@ @import @import +@import [CPApplication sharedApplication] @@ -34,4 +35,71 @@ wasClicked = YES; } +- (void)testKeyEquivalent +{ + [button setTarget:self]; + [button setAction:@selector(clickMe:)]; + [button setKeyEquivalent:"a"]; + [button performKeyEquivalent:[CPEvent keyEventWithType:CPKeyUp location:CGPointMakeZero() modifierFlags:0 + timestamp:nil windowNumber:nil context:nil + characters:"b" charactersIgnoringModifiers:"b" isARepeat:NO keyCode:0]]; + [self assertFalse:wasClicked]; + [button performKeyEquivalent:[CPEvent keyEventWithType:CPKeyUp location:CGPointMakeZero() modifierFlags:0 + timestamp:nil windowNumber:nil context:nil + characters:"a" charactersIgnoringModifiers:"a" isARepeat:NO keyCode:0]]; + [self assertTrue:wasClicked]; +} + +- (void)testKeyEquivalentWithModifierMask +{ + [button setTarget:self]; + [button setAction:@selector(clickMe:)]; + [button setKeyEquivalent:"a"]; + [button setKeyEquivalentModifierMask:CPAlternateKeyMask]; + [button performKeyEquivalent:[CPEvent keyEventWithType:CPKeyUp location:CGPointMakeZero() modifierFlags:0 + timestamp:nil windowNumber:nil context:nil + characters:"a" charactersIgnoringModifiers:"a" isARepeat:NO keyCode:0]]; + [self assertFalse:wasClicked]; + [button performKeyEquivalent:[CPEvent keyEventWithType:CPKeyUp location:CGPointMakeZero() modifierFlags:CPAlternateKeyMask + timestamp:nil windowNumber:nil context:nil + characters:"a" charactersIgnoringModifiers:"a" isARepeat:NO keyCode:0]]; + [self assertTrue:wasClicked]; +} + +- (void)testKeyEquivalentWithShiftMask +{ + [button setTarget:self]; + [button setAction:@selector(clickMe:)]; + [button setKeyEquivalent:"A"]; + + [button performKeyEquivalent:[CPEvent keyEventWithType:CPKeyUp location:CGPointMakeZero() modifierFlags:0 + timestamp:nil windowNumber:nil context:nil + characters:"a" charactersIgnoringModifiers:"a" isARepeat:NO keyCode:0]]; + [self assertFalse:wasClicked]; + + [button performKeyEquivalent:[CPEvent keyEventWithType:CPKeyUp location:CGPointMakeZero() modifierFlags:CPShiftKeyMask + timestamp:nil windowNumber:nil context:nil + characters:"A" charactersIgnoringModifiers:"a" isARepeat:NO keyCode:0]]; + [self assertTrue:wasClicked]; +} + +- (void)testSpecialKeyEquivalent +{ + [button setTarget:self]; + [button setAction:@selector(clickMe:)]; + [button setKeyEquivalent:CPEscapeFunctionKey]; + [button performKeyEquivalent:[CPEvent keyEventWithType:CPKeyUp location:CGPointMakeZero() modifierFlags:0 + timestamp:nil windowNumber:nil context:nil + characters:CPDeleteCharacter charactersIgnoringModifiers:CPDeleteCharacter isARepeat:NO keyCode:0]]; + [self assertFalse:wasClicked]; + [button performKeyEquivalent:[CPEvent keyEventWithType:CPKeyUp location:CGPointMakeZero() modifierFlags:0 + timestamp:nil windowNumber:nil context:nil + characters:"a" charactersIgnoringModifiers:"a" isARepeat:NO keyCode:0]]; + [self assertFalse:wasClicked]; + [button performKeyEquivalent:[CPEvent keyEventWithType:CPKeyUp location:CGPointMakeZero() modifierFlags:0 + timestamp:nil windowNumber:nil context:nil + characters:CPEscapeFunctionKey charactersIgnoringModifiers:CPEscapeFunctionKey isARepeat:NO keyCode:0]]; + [self assertTrue:wasClicked]; +} + @end diff --git a/Tests/AppKit/CPKeyEquivalentPerformance.j b/Tests/AppKit/CPKeyEquivalentPerformance.j new file mode 100644 index 000000000..a6a509437 --- /dev/null +++ b/Tests/AppKit/CPKeyEquivalentPerformance.j @@ -0,0 +1,76 @@ +@import +@import +@import +@import + +[CPApplication sharedApplication]; + +@implementation CPKeyEquivalentPerformance : OJTestCase + +- (void)testKeyEquivalentSpeed +{ + var REPEATS = 1000, + theWindow = [[CPWindow alloc] initWithContentRect:CGRectMake(0,0,200,150) + styleMask:CPWindowNotSizable], + contentView = [theWindow contentView], + subView1 = [[CPView alloc] initWithFrame:CGRectMakeZero()], + subView2 = [[CPView alloc] initWithFrame:CGRectMakeZero()], + button1 = [CPButton buttonWithTitle:"when"], + button2 = [CPButton buttonWithTitle:"you have eliminated"], + button3 = [CPButton buttonWithTitle:"the impossible"]; + + [contentView addSubview:subView1]; + [contentView addSubview:subView2]; + [subView1 addSubview:button1]; + [subView2 addSubview:button2]; + [subView2 addSubview:button3]; + + [button1 setTarget:self]; + [button1 setAction:@selector(clicked:)]; + [button1 setKeyEquivalent:"a"]; + [button1 setKeyEquivalentModifierMask:CPControlKeyMask]; + button1.clicks = 0; + + [button2 setTarget:self]; + [button2 setAction:@selector(clicked:)]; + [button2 setKeyEquivalent:"a"]; + [button2 setKeyEquivalentModifierMask:CPAlternateKeyMask|CPCommandKeyMask]; + button2.clicks = 0; + + [button3 setTarget:self]; + [button3 setAction:@selector(clicked:)]; + [button3 setKeyEquivalent:"A"]; + [button3 setKeyEquivalentModifierMask:CPControlKeyMask]; + button3.clicks = 0; + + var start = (new Date).getTime(); + + for (var i=0; i +@import +@import +@import + +[CPApplication sharedApplication] + +@implementation CPMenuTest : OJTestCase +{ + CPMenu menu; + BOOL escapeWasCalled; + BOOL escapeNoModifierWasCalled; + BOOL openDocumentWasCalled; + BOOL saveDocumentWasCalled; + BOOL saveDocumentAsWasCalled; + BOOL undoWasCalled; +} + +- (void)setUp +{ + // Set up a fairly complete menu to have something to work with. + menu = [[CPMenu alloc] initWithTitle:@"MainMenu"]; + + var newMenuItem = [[CPMenuItem alloc] initWithTitle:@"New" action:@selector(newDocument:) keyEquivalent:@"n"]; + [menu addItem:newMenuItem]; + + var openMenuItem = [[CPMenuItem alloc] initWithTitle:@"Open" action:@selector(openDocument:) keyEquivalent:@"o"]; + [menu addItem:openMenuItem]; + + var saveMenu = [[CPMenu alloc] initWithTitle:@"Save"], + saveMenuItem = [[CPMenuItem alloc] initWithTitle:@"Save" action:@selector(saveDocument:) keyEquivalent:nil]; + // S + [saveMenu addItem:[[CPMenuItem alloc] initWithTitle:@"Save" action:@selector(saveDocument:) keyEquivalent:@"s"]]; + // ...vs Shift-S + [saveMenu addItem:[[CPMenuItem alloc] initWithTitle:@"Save As" action:@selector(saveDocumentAs:) keyEquivalent:@"S"]]; + + + // Cmd-Escape + [saveMenu addItem:[[CPMenuItem alloc] initWithTitle:@"Escape the monotonous" action:@selector(escape:) keyEquivalent:CPEscapeFunctionKey]]; + // Escape + var pureEscape = [[CPMenuItem alloc] initWithTitle:@"Escape the cruel" action:@selector(escapeNoModifier:) keyEquivalent:CPEscapeFunctionKey]; + [pureEscape setKeyEquivalentModifierMask:0]; + [saveMenu addItem:pureEscape]; + + [saveMenuItem setSubmenu:saveMenu]; + [menu addItem:saveMenuItem]; + + var editMenuItem = [[CPMenuItem alloc] initWithTitle:@"Edit" action:nil keyEquivalent:nil], + editMenu = [[CPMenu alloc] initWithTitle:@"Edit"], + + undoMenuItem = [[CPMenuItem alloc] initWithTitle:@"Undo" action:@selector(undo:) keyEquivalent:CPUndoKeyEquivalent], + redoMenuItem = [[CPMenuItem alloc] initWithTitle:@"Redo" action:@selector(redo:) keyEquivalent:CPRedoKeyEquivalent]; + + [undoMenuItem setKeyEquivalentModifierMask:CPUndoKeyEquivalentModifierMask]; + [redoMenuItem setKeyEquivalentModifierMask:CPRedoKeyEquivalentModifierMask]; + + [editMenu addItem:undoMenuItem]; + [editMenu addItem:redoMenuItem]; + + [editMenu addItem:[[CPMenuItem alloc] initWithTitle:@"Cut" action:@selector(cut:) keyEquivalent:@"x"]], + [editMenu addItem:[[CPMenuItem alloc] initWithTitle:@"Copy" action:@selector(copy:) keyEquivalent:@"c"]], + [editMenu addItem:[[CPMenuItem alloc] initWithTitle:@"Paste" action:@selector(paste:) keyEquivalent:@"v"]]; + + [editMenuItem setSubmenu:editMenu]; + [editMenuItem setHidden:YES]; + + [menu addItem:editMenuItem]; + [menu addItem:[CPMenuItem separatorItem]]; +} + +- (void)_retarget:(CPMenuItem)aMenu +{ + if (!aMenu) + return; + + for(var i=0; i<[aMenu numberOfItems]; i++) + { + var item = [aMenu itemAtIndex:i]; + [item setTarget:self]; + [self _retarget:[item submenu]]; + } +} + +- (void)testKeyEquivalent +{ + [self _retarget:menu]; + + // Don't match anything. + [menu performKeyEquivalent:[CPEvent keyEventWithType:CPKeyDown location:CGPointMakeZero() modifierFlags:CPPlatformActionKeyMask + timestamp:nil windowNumber:nil context:nil + characters:"b" charactersIgnoringModifiers:"b" isARepeat:NO keyCode:0]]; + [self assertFalse:escapeWasCalled || escapeNoModifierWasCalled || openDocumentWasCalled || undoWasCalled]; + + [menu performKeyEquivalent:[CPEvent keyEventWithType:CPKeyDown location:CGPointMakeZero() modifierFlags:0 + timestamp:nil windowNumber:nil context:nil + characters:"o" charactersIgnoringModifiers:"o" isARepeat:NO keyCode:0]]; + [self assertFalse:escapeWasCalled || escapeNoModifierWasCalled || openDocumentWasCalled || undoWasCalled]; + + [menu performKeyEquivalent:[CPEvent keyEventWithType:CPKeyDown location:CGPointMakeZero() modifierFlags:CPPlatformActionKeyMask + timestamp:nil windowNumber:nil context:nil + characters:"o" charactersIgnoringModifiers:"o" isARepeat:NO keyCode:0]]; + [self assertFalse:escapeWasCalled || escapeNoModifierWasCalled || undoWasCalled]; + [self assertTrue:openDocumentWasCalled message:"expect openDocumentWasCalled"]; + + openDocumentWasCalled = NO; + [menu performKeyEquivalent:[CPEvent keyEventWithType:CPKeyDown location:CGPointMakeZero() modifierFlags:CPPlatformActionKeyMask + timestamp:nil windowNumber:nil context:nil + characters:CPUndoKeyEquivalent charactersIgnoringModifiers:CPUndoKeyEquivalent isARepeat:NO keyCode:0]]; + [self assertFalse:escapeWasCalled || escapeNoModifierWasCalled || openDocumentWasCalled]; + [self assertTrue:undoWasCalled]; +} + +- (void)testKeyEquivalentModifierMask +{ + [self _retarget:menu]; + + [menu performKeyEquivalent:[CPEvent keyEventWithType:CPKeyDown location:CGPointMakeZero() modifierFlags:0 + timestamp:nil windowNumber:nil context:nil + characters:CPEscapeFunctionKey charactersIgnoringModifiers:CPEscapeFunctionKey isARepeat:NO keyCode:0]]; + [self assertFalse:escapeWasCalled || openDocumentWasCalled || undoWasCalled]; + [self assertTrue:escapeNoModifierWasCalled]; + + escapeNoModifierWasCalled = NO; + + [menu performKeyEquivalent:[CPEvent keyEventWithType:CPKeyDown location:CGPointMakeZero() modifierFlags:CPPlatformActionKeyMask + timestamp:nil windowNumber:nil context:nil + characters:CPEscapeFunctionKey charactersIgnoringModifiers:CPEscapeFunctionKey isARepeat:NO keyCode:0]]; + [self assertFalse:escapeNoModifierWasCalled || openDocumentWasCalled || undoWasCalled]; + [self assertTrue:escapeWasCalled]; +} + +- (void)testKeyEquivalentWithShiftMask +{ + [self _retarget:menu]; + + [menu performKeyEquivalent:[CPEvent keyEventWithType:CPKeyDown location:CGPointMakeZero() modifierFlags:CPPlatformActionKeyMask + timestamp:nil windowNumber:nil context:nil + characters:@"s" charactersIgnoringModifiers:@"s" isARepeat:NO keyCode:0]]; + [self assertFalse:escapeWasCalled || escapeNoModifierWasCalled || openDocumentWasCalled || saveDocumentAsWasCalled || undoWasCalled]; + [self assertTrue:saveDocumentWasCalled message:"saveDocumentWasCalled"]; + + saveDocumentWasCalled = NO; + + [menu performKeyEquivalent:[CPEvent keyEventWithType:CPKeyDown location:CGPointMakeZero() modifierFlags:CPPlatformActionKeyMask|CPShiftKeyMask + timestamp:nil windowNumber:nil context:nil + characters:@"s" charactersIgnoringModifiers:@"s" isARepeat:NO keyCode:0]]; + [self assertFalse:escapeWasCalled || escapeNoModifierWasCalled || openDocumentWasCalled || saveDocumentWasCalled || undoWasCalled]; + [self assertTrue:saveDocumentAsWasCalled message:"saveDocumentAsWasCalled"]; +} + +- (void)escape:(id)sender +{ + escapeWasCalled = YES; +} + +- (void)escapeNoModifier:(id)sender +{ + escapeNoModifierWasCalled = YES; +} + +- (void)openDocument:(id)sender +{ + openDocumentWasCalled = YES; +} + +- (void)saveDocument:(id)sender +{ + saveDocumentWasCalled = YES; +} + +- (void)saveDocumentAs:(id)sender +{ + saveDocumentAsWasCalled = YES; +} + +- (void)undo:(id)sender +{ + undoWasCalled = YES; +} + +@end diff --git a/Tests/AppKit/CPResponderTest.j b/Tests/AppKit/CPResponderTest.j new file mode 100644 index 000000000..2cfa482f3 --- /dev/null +++ b/Tests/AppKit/CPResponderTest.j @@ -0,0 +1,75 @@ +@import +@import +@import + +@import + +[CPApplication sharedApplication] + +@implementation CPResponderTest : OJTestCase +{ + CPWindow theWindow; + CPResponder responder; +} + +- (void)setUp +{ + responder = [TestResponder new]; + responder.doCommandCalls = []; +} + +- (void)testInterpretKeyEvents +{ + var tests = [ + CPKeyCodes.PAGE_UP, CPPageUpFunctionKey, @selector(scrollPageUp:), + CPKeyCodes.PAGE_DOWN, CPPageDownFunctionKey, @selector(scrollPageDown:), + CPKeyCodes.LEFT, CPLeftArrowFunctionKey, @selector(moveLeft:), + CPKeyCodes.RIGHT, CPRightArrowFunctionKey, @selector(moveRight:), + CPKeyCodes.UP, CPUpArrowFunctionKey, @selector(moveUp:), + CPKeyCodes.DOWN, CPDownArrowFunctionKey, @selector(moveDown:), + CPKeyCodes.BACKSPACE, CPDeleteCharacter, @selector(deleteBackward:), + CPKeyCodes.ENTER, CPCarriageReturnCharacter, @selector(insertNewline:), + 0, CPNewlineCharacter, @selector(insertNewline:), + CPKeyCodes.ESC, CPEscapeFunctionKey, @selector(cancelOperation:), + CPKeyCodes.TAB, CPTabCharacter, @selector(insertTab:) + ]; + + for (var i=0; i= 0, offsetHours = Math.floor(date.getTimezoneOffset() / 60), offsetMinutes = date.getTimezoneOffset() - offsetHours * 60, - expectedString = [CPString stringWithFormat:"2009-02-13 %02d:%02d:30 +%02d%02d", expectedHour-offsetHours, expectedMinute-offsetMinutes, offsetHours, offsetMinutes]; + expectedString; + expectedHour -= offsetHours; + expectedMinute -= offsetMinutes; + if (expectedMinute < 0) + { + expectedMinute += 60; + expectedHour--; + } + else if (expectedMinute > 59) + { + expectedMinute -= 60; + expectedHour++; + } + if (expectedHour < 0) + { + expectedHour += 24; + expectedDay--; + } + else if (expectedHour > 23) + { + expectedHour -= 24; + expectedDay++; + } - [self assert:expectedString equals:[date description]]; + if (offsetPositive) + expectedString = [CPString stringWithFormat:"2009-02-%02d %02d:%02d:30 +%02d%02d", expectedDay, expectedHour, expectedMinute, offsetHours, offsetMinutes]; + else + expectedString = [CPString stringWithFormat:"2009-02-%02d %02d:%02d:30 -%02d%02d", expectedDay, expectedHour, expectedMinute, ABS(offsetHours), ABS(offsetMinutes)]; + + [self assert:expectedString equals: [date description]]; } - (void)testCopy diff --git a/Tests/AppKit/SimpleBindings2/AppController.j b/Tests/Manual/SimpleBindings2/AppController.j similarity index 100% rename from Tests/AppKit/SimpleBindings2/AppController.j rename to Tests/Manual/SimpleBindings2/AppController.j diff --git a/Tests/AppKit/SimpleBindings2/Info.plist b/Tests/Manual/SimpleBindings2/Info.plist similarity index 100% rename from Tests/AppKit/SimpleBindings2/Info.plist rename to Tests/Manual/SimpleBindings2/Info.plist diff --git a/Tests/AppKit/SimpleBindings2/index-debug.html b/Tests/Manual/SimpleBindings2/index-debug.html similarity index 100% rename from Tests/AppKit/SimpleBindings2/index-debug.html rename to Tests/Manual/SimpleBindings2/index-debug.html diff --git a/Tests/AppKit/SimpleBindings2/index.html b/Tests/Manual/SimpleBindings2/index.html similarity index 100% rename from Tests/AppKit/SimpleBindings2/index.html rename to Tests/Manual/SimpleBindings2/index.html diff --git a/Tests/AppKit/SimpleBindings2/main.j b/Tests/Manual/SimpleBindings2/main.j similarity index 100% rename from Tests/AppKit/SimpleBindings2/main.j rename to Tests/Manual/SimpleBindings2/main.j diff --git a/Tests/Objective-J/base64Test.j b/Tests/Objective-J/base64Test.j index 43186ff9b..bce099d2c 100644 --- a/Tests/Objective-J/base64Test.j +++ b/Tests/Objective-J/base64Test.j @@ -42,10 +42,16 @@ var base64TestStrings = [ { var result = CFData.decodeBase64ToArray(base64TestStrings[i][1]), expected = base64TestStrings[i][0]; - + for (var j = 0; j < expected.length || j < result.length; j++) [self assert:result[j] equals:expected.charCodeAt(j)]; } } +- (void)test_CFData_encodeUtfString +{ + var utfTest = "\uF728"; // A common key equivalent. + [self assert:CFData.decodeBase64ToUtf16String(CFData.encodeBase64Utf16String(utfTest)) equals:utfTest]; +} + @end diff --git a/Tools/NativeHost/Application.m b/Tools/NativeHost/Application.m index 4dbb8a397..2234f307d 100644 --- a/Tools/NativeHost/Application.m +++ b/Tools/NativeHost/Application.m @@ -15,6 +15,8 @@ - (void)sendEvent:(NSEvent *)anEvent { + [WebWindow enableAllWindows]; + NSWindow * window = [anEvent window]; if (!window || [window isKindOfClass:[WebWindow class]]) diff --git a/Tools/NativeHost/WebWindow.m b/Tools/NativeHost/WebWindow.m index 631e73a72..122aa453b 100644 --- a/Tools/NativeHost/WebWindow.m +++ b/Tools/NativeHost/WebWindow.m @@ -18,9 +18,6 @@ CGEventRef headTapCallback(CGEventTapProxy proxy, CGEventType type, CGEventRef e // It's dangerous to fail in this code: could disable mousedown system-wide. So just try catch it all. @try { - [DisabledWindows makeObjectsPerformSelector:@selector(stopIgnoringMouseEvents)]; - [DisabledWindows removeAllObjects]; - if (type == kCGEventLeftMouseDown) { CGPoint location = CGEventGetLocation(event); @@ -55,6 +52,12 @@ CGEventRef headTapCallback(CGEventTapProxy proxy, CGEventType type, CGEventRef e @implementation WebWindow ++ (void)enableAllWindows +{ + [DisabledWindows makeObjectsPerformSelector:@selector(stopIgnoringMouseEvents)]; + [DisabledWindows removeAllObjects]; +} + + (void)initialize { if (self != [WebWindow class]) @@ -117,8 +120,9 @@ CGEventRef headTapCallback(CGEventTapProxy proxy, CGEventType type, CGEventRef e [self setBackgroundColor:[NSColor clearColor]]; [self setOpaque:NO]; + [self setIgnoresMouseEvents:NO]; [self setReleasedWhenClosed:YES]; - [super setHasShadow:NO]; + [super setHasShadow:NO]; } return self; diff --git a/Tools/nib2cib/Converter.j b/Tools/nib2cib/Converter.j index 46eb0036a..88175bb48 100644 --- a/Tools/nib2cib/Converter.j +++ b/Tools/nib2cib/Converter.j @@ -121,9 +121,9 @@ ConverterConversionException = @"ConverterConversionException"; else plistContents = plistContents.replace(/\\s*CF\$UID\s*\<\/key\>/g, "CP$UID"); - plistContents = plistContents.replace(/\u001b/g, function(c) { - CPLog.warn("Warning: Stripping character 0x"+c.charCodeAt(0).toString(16)); - return ""; + plistContents = plistContents.replace(/[\u0000-\u0008\u000B\u000C\u000E-\u001F]<\/string>/g, function(c) { + CPLog.warn("Warning: Converting character 0x"+c.charCodeAt(8).toString(16)+" to base64 representation"); + return ""+CFData.encodeBase64String(c.charAt(8))+""; }); return [CPData dataWithRawString:plistContents]; diff --git a/Tools/nib2cib/NSButton.j b/Tools/nib2cib/NSButton.j index 9c13724e4..a9adfb9c1 100644 --- a/Tools/nib2cib/NSButton.j +++ b/Tools/nib2cib/NSButton.j @@ -97,6 +97,7 @@ _CPButtonBezelStyleHeights[CPHUDBezelStyle] = 20; { CPLog.info("Adjusting CPButton height from " +_frame.size.height+ " / " + _bounds.size.height+" to " + 24); _frame.size.height = 24.0; + _frame.origin.y += 4.0; _bounds.size.height = 24.0; } } @@ -180,6 +181,9 @@ _CPButtonBezelStyleHeights[CPHUDBezelStyle] = 20; } } + [self setKeyEquivalent:[cell keyEquivalent]]; + [self setKeyEquivalentModifierMask:[cell keyEquivalentModifierMask]]; + return [self NS_initWithCoder:aCoder]; } @@ -203,6 +207,9 @@ _CPButtonBezelStyleHeights[CPHUDBezelStyle] = 20; CPString _title @accessors(readonly, getter=title); CPImage _alternateImage @accessors(readonly, getter=alternateImage); + + CPString _keyEquivalent @accessors(readonly, getter=keyEquivalent); + unsigned _keyEquivalentModifierMask @accessors(readonly, getter=keyEquivalentModifierMask); } - (id)initWithCoder:(CPCoder)aCoder @@ -223,6 +230,9 @@ _CPButtonBezelStyleHeights[CPHUDBezelStyle] = 20; _objectValue = [self state]; _alternateImage = [aCoder decodeObjectForKey:@"NSAlternateImage"]; + + _keyEquivalent = [aCoder decodeObjectForKey:@"NSKeyEquivalent"]; + _keyEquivalentModifierMask = buttonFlags2 >> 8; } return self; diff --git a/Tools/nib2cib/NSImageView.j b/Tools/nib2cib/NSImageView.j index 2624c2582..9c83e4a7b 100644 --- a/Tools/nib2cib/NSImageView.j +++ b/Tools/nib2cib/NSImageView.j @@ -34,6 +34,7 @@ var cell = [aCoder decodeObjectForKey:@"NSCell"]; [self setImageScaling:[cell imageScaling]]; + [self setImageAlignment:[cell imageAlignment]]; _isEditable = [cell isEditable]; } @@ -92,7 +93,7 @@ NSImageScalingToCPImageScaling[NSImageScaleProportionallyUpOrDown] = CPScalePro @implementation NSImageCell : NSCell { BOOL _animates @accessors; - NSImageAlignment _imageAlignment @accessors; + NSImageAlignment _imageAlignment @accessors(readonly, getter=imageAlignment); NSImageScaling _imageScaling @accessors(readonly, getter=imageScaling); NSImageFrameStyle _frameStyle @accessors; } diff --git a/Tools/nib2cib/NSSegmentedControl.j b/Tools/nib2cib/NSSegmentedControl.j index 737132f80..df3145d20 100644 --- a/Tools/nib2cib/NSSegmentedControl.j +++ b/Tools/nib2cib/NSSegmentedControl.j @@ -98,6 +98,9 @@ _selectedSegment = [aCoder decodeIntForKey:"NSSelectedSegment"] || -1; _segmentStyle = [aCoder decodeIntForKey:"NSSegmentStyle"]; _trackingMode = [aCoder decodeIntForKey:"NSTrackingMode"] || CPSegmentSwitchTrackingSelectOne; + + if (_trackingMode == CPSegmentSwitchTrackingSelectOne && _selectedSegment == -1) + _selectedSegment = 0; } return self;