diff --git a/AppKit/CPAlert.j b/AppKit/CPAlert.j index 75b63ce9d..6f5b3cefd 100644 --- a/AppKit/CPAlert.j +++ b/AppKit/CPAlert.j @@ -116,21 +116,12 @@ var CPAlertWarningImage, */ - (id)init { - self = [super init]; - - if (self) + if (self = [super init]) { _buttonCount = 0; _buttons = [CPArray array]; _alertStyle = CPWarningAlertStyle; - - _messageLabel = [[CPTextField alloc] initWithFrame:CGRectMake(57.0, 10.0, 220.0, 80.0)]; - [_messageLabel setFont:[CPFont systemFontOfSize:12.0]]; - [_messageLabel setLineBreakMode:CPLineBreakByWordWrapping]; - [_messageLabel setAlignment:CPJustifiedTextAlignment]; - - _alertImageView = [[CPImageView alloc] initWithFrame:CGRectMake(15.0, 12.0, 32.0, 32.0)]; - + [self setWindowStyle:nil]; } @@ -145,12 +136,12 @@ var CPAlertWarningImage, { _windowStyle = styleMask; - _alertPanel = [[CPPanel alloc] initWithContentRect:CGRectMake(0.0, 0.0, 300.0, 130.0) styleMask:styleMask ? styleMask | CPTitledWindowMask : CPTitledWindowMask]; + _alertPanel = [[CPPanel alloc] initWithContentRect:CGRectMake(0.0, 0.0, 400.0, 110.0) styleMask:styleMask ? styleMask | CPTitledWindowMask : CPTitledWindowMask]; [_alertPanel setFloatingPanel:YES]; [_alertPanel center]; - + [_messageLabel setTextColor:(styleMask & CPHUDBackgroundWindowMask) ? [CPColor whiteColor] : [CPColor blackColor]]; - + var count = [_buttons count]; for(var i=0; i < count; i++) { @@ -162,6 +153,18 @@ var CPAlertWarningImage, [[_alertPanel contentView] addSubview:button]; } + if (!_messageLabel) + { + var bounds = [[_alertPanel contentView] bounds]; + + _messageLabel = [[CPTextField alloc] initWithFrame:CGRectMake(57.0, 10.0, CGRectGetWidth(bounds) - 73.0, 62.0)]; + [_messageLabel setFont:[CPFont boldSystemFontOfSize:13.0]]; + [_messageLabel setLineBreakMode:CPLineBreakByWordWrapping]; + [_messageLabel setAlignment:CPJustifiedTextAlignment]; + + _alertImageView = [[CPImageView alloc] initWithFrame:CGRectMake(15.0, 12.0, 32.0, 32.0)]; + } + [[_alertPanel contentView] addSubview:_messageLabel]; [[_alertPanel contentView] addSubview:_alertImageView]; } @@ -251,7 +254,8 @@ var CPAlertWarningImage, */ - (void)addButtonWithTitle:(CPString)title { - var button = [[CPButton alloc] initWithFrame:CGRectMake(200.0 - (_buttonCount * 90.0), 98.0, 80.0, (_windowStyle == CPHUDBackgroundWindowMask) ? 20.0 : 24.0)]; + 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 setTitle:title]; [button setTarget:self]; @@ -298,11 +302,11 @@ var CPAlertWarningImage, /* @ignore */ - (void)_notifyDelegate:(id)button { - if (_delegate && [_delegate respondsToSelector:@selector(alertDidEnd:returnCode:)]) - [_delegate alertDidEnd:self returnCode:[button tag]]; - [CPApp abortModal]; [_alertPanel close]; + + if (_delegate && [_delegate respondsToSelector:@selector(alertDidEnd:returnCode:)]) + [_delegate alertDidEnd:self returnCode:[button tag]]; } @end diff --git a/AppKit/CPApplication.j b/AppKit/CPApplication.j index 72f88a420..b71659232 100644 --- a/AppKit/CPApplication.j +++ b/AppKit/CPApplication.j @@ -39,6 +39,11 @@ CPApp = nil; CPApplicationWillFinishLaunchingNotification = @"CPApplicationWillFinishLaunchingNotification"; CPApplicationDidFinishLaunchingNotification = @"CPApplicationDidFinishLaunchingNotification"; +CPApplicationWillTerminateNotification = @"CPApplicationWillTerminateNotification"; + +CPTerminateNow = YES; +CPTerminateCancel = NO; +CPTerminateLater = -1; // not currently supported CPRunStoppedResponse = -1000; CPRunAbortedResponse = -1001; @@ -283,7 +288,9 @@ CPRunContinuesResponse = -1002; if (needsUntitled) [_documentController newDocument:self]; - + + [_documentController _updateRecentDocumentsMenu]; + [defaultCenter postNotificationName:CPApplicationDidFinishLaunchingNotification object:self]; @@ -295,7 +302,35 @@ CPRunContinuesResponse = -1002; - (void)terminate:(id)aSender { - [CPPlatform terminateApplication]; + [[CPDocumentController sharedDocumentController] closeAllDocumentsWithDelegate:self + didCloseAllSelector:@selector(_documentController:didCloseAll:context:) + contextInfo:nil]; +} + +- (void)_documentController:(NSDocumentController *)docController didCloseAll:(BOOL)didCloseAll context:(Object)info +{ + // callback method for terminate: + if (didCloseAll) + { + if ([_delegate respondsToSelector:@selector(applicationShouldTerminate:)]) + [self replyToApplicationShouldTerminate:[_delegate applicationShouldTerminate:self]]; + else + [self replyToApplicationShouldTerminate:YES]; + } +} + +- (void)replyToApplicationShouldTerminate:(BOOL)terminate +{ + if (terminate == CPTerminateNow) + { + [[CPNotificationCenter defaultCenter] postNotificationName:CPApplicationWillTerminateNotification object:self]; + [CPPlatform terminateApplication]; + } +} + +- (void)activateIgnoringOtherApps:(BOOL)shouldIgnoreOtherApps +{ + [CPPlatform activateIgnoringOtherApps:shouldIgnoreOtherApps]; } /*! @@ -410,10 +445,8 @@ CPRunContinuesResponse = -1002; /* @ignore */ - (BOOL)_handleKeyEquivalent:(CPEvent)anEvent { - if ([_mainMenu performKeyEquivalent:anEvent]) - return YES; - - return NO; + return [[self keyWindow] performKeyEquivalent:anEvent] || + [_mainMenu performKeyEquivalent:anEvent]; } /*! @@ -425,11 +458,11 @@ CPRunContinuesResponse = -1002; _currentEvent = anEvent; // Check if this is a candidate for key equivalent... - if ([anEvent type] == CPKeyDown && - [anEvent modifierFlags] & (CPCommandKeyMask | CPControlKeyMask) && - [[anEvent characters] length] > 0 && - [self _handleKeyEquivalent:anEvent]) + if ([anEvent _couldBeKeyEquivalent] && [self _handleKeyEquivalent:anEvent]) + { + [[[anEvent window] platformWindow] _propagateCurrentDOMEvent:NO]; return; + } if (_eventListeners.length) { @@ -809,6 +842,12 @@ CPRunContinuesResponse = -1002; return [_documentController openDocumentWithContentsOfURL:aFilename display:YES error:NULL]; } +- (void)_didResignActive +{ + if (self._activeMenu) + [self._activeMenu cancelTracking]; +} + @end var _CPModalSessionMake = function(aWindow, aStopCode) diff --git a/AppKit/CPCompatibility.j b/AppKit/CPCompatibility.j index 2539b2ab1..b2636811d 100644 --- a/AppKit/CPCompatibility.j +++ b/AppKit/CPCompatibility.j @@ -179,7 +179,7 @@ function CPBrowserIsEngine(anEngine) if (USER_AGENT.indexOf("Mac") != -1) { CPPlatformActionKeyMask = CPCommandKeyMask; - + CPUndoKeyEquivalent = @"Z"; CPRedoKeyEquivalent = @"Z"; @@ -189,10 +189,10 @@ if (USER_AGENT.indexOf("Mac") != -1) else { CPPlatformActionKeyMask = CPControlKeyMask; - + CPUndoKeyEquivalent = @"Z"; CPRedoKeyEquivalent = @"Y"; - + CPUndoKeyEquivalentModifierMask = CPControlKeyMask; CPRedoKeyEquivalentModifierMask = CPControlKeyMask; } diff --git a/AppKit/CPControl.j b/AppKit/CPControl.j index d00f3aa6f..f9ddc817e 100644 --- a/AppKit/CPControl.j +++ b/AppKit/CPControl.j @@ -94,7 +94,8 @@ var CPControlBlackColor = [CPColor blackColor]; id _target; SEL _action; int _sendActionOn; - + BOOL _sendsActionOnEndEditing @accessors(property=sendsActionOnEndEditing); + // Mouse Tracking Support BOOL _continuousTracking; BOOL _trackingWasWithinFrame; diff --git a/AppKit/CPDocument.j b/AppKit/CPDocument.j index 82159b55f..22f62b0a8 100644 --- a/AppKit/CPDocument.j +++ b/AppKit/CPDocument.j @@ -24,6 +24,7 @@ @import @import "CPResponder.j" +@import "CPSavePanel.j" @import "CPViewController.j" @import "CPWindowController.j" @@ -108,6 +109,8 @@ var CPDocumentUntitledCount = 0; CPURLConnection _readConnection; CPURLRequest _writeRequest; + + CPAlert _canCloseAlert; } /*! @@ -279,12 +282,13 @@ var CPDocumentUntitledCount = 0; var view = [viewController view], theWindow = [[CPWindow alloc] initWithContentRect:[view frame] styleMask:CPTitledWindowMask | CPClosableWindowMask | CPMiniaturizableWindowMask | CPResizableWindowMask]; - [theWindow setSupportsMultipleDocuments:YES]; - windowController = [[CPWindowController alloc] initWithWindow:theWindow]; } } + if (windowController && viewController) + [windowController setSupportsMultipleDocuments:YES]; + if (windowController) [self addWindowController:windowController]; @@ -310,10 +314,21 @@ var CPDocumentUntitledCount = 0; [_windowControllers addObject:aWindowController]; if ([aWindowController document] !== self) - { - [aWindowController setNextResponder:self]; [aWindowController setDocument:self]; - } +} + +/*! + Remove a controller to the document's list of controllers. This should + be called after closing the controller's window. + @param aWindowController the controller to remove +*/ +- (void)removeWindowController:(CPWindowController)aWindowController +{ + if (aWindowController) + [_windowControllers removeObject:aWindowController]; + + if ([aWindowController document] === self) + [aWindowController setDocument:nil]; } - (CPView)view @@ -416,7 +431,7 @@ var CPDocumentUntitledCount = 0; // FIXME: Oh man is this every looking for trouble, we need to handle login at the Cappuccino level, with HTTP Errors. _readConnection = [CPURLConnection connectionWithRequest:[CPURLRequest requestWithURL:anAbsoluteURL] delegate:self]; - + _readConnection.session = _CPReadSessionMake(aType, aDelegate, aDidReadSelector, aContextInfo); } @@ -459,17 +474,22 @@ var CPDocumentUntitledCount = 0; _writeRequest = [CPURLRequest requestWithURL:anAbsoluteURL]; - [_writeRequest setHTTPMethod:@"POST"]; + // FIXME: THIS IS WRONG! We need a way to decide + if ([CPPlatform isBrowser]) + [_writeRequest setHTTPMethod:@"POST"]; + else + [_writeRequest setHTTPMethod:@"PUT"]; + [_writeRequest setHTTPBody:[data string]]; - + [_writeRequest setValue:@"close" forHTTPHeaderField:@"Connection"]; if (aSaveOperation == CPSaveOperation) [_writeRequest setValue:@"true" forHTTPHeaderField:@"x-cappuccino-overwrite"]; - + if (aSaveOperation != CPSaveToOperation) [self updateChangeCount:CPChangeCleared]; - + // FIXME: Oh man is this every looking for trouble, we need to handle login at the Cappuccino level, with HTTP Errors. var connection = [CPURLConnection connectionWithRequest:_writeRequest delegate:self]; @@ -526,6 +546,7 @@ var CPDocumentUntitledCount = 0; _writeRequest = nil; objj_msgSend(session.delegate, session.didSaveSelector, self, NO, session.contextInfo); + [self _sendDocumentSavedNotification:NO]; } } } @@ -554,6 +575,7 @@ var CPDocumentUntitledCount = 0; _writeRequest = nil; objj_msgSend(session.delegate, session.didSaveSelector, self, YES, session.contextInfo); + [self _sendDocumentSavedNotification:YES]; } } @@ -581,6 +603,7 @@ var CPDocumentUntitledCount = 0; alert("There was an error saving the document."); objj_msgSend(session.delegate, session.didSaveSelector, self, NO, session.contextInfo); + [self _sendDocumentSavedNotification:NO]; } } @@ -765,6 +788,11 @@ var CPDocumentUntitledCount = 0; @param aSender the object requesting the save */ - (void)saveDocument:(id)aSender +{ + [self saveDocumentWithDelegate:nil didSaveSelector:nil contextInfo:nil]; +} + +- (void)saveDocumentWithDelegate:(id)delegate didSaveSelector:(SEL)didSaveSelector contextInfo:(Object)contextInfo { if (_fileURL) { @@ -772,10 +800,10 @@ var CPDocumentUntitledCount = 0; postNotificationName:CPDocumentWillSaveNotification object:self]; - [self saveToURL:_fileURL ofType:[self fileType] forSaveOperation:CPSaveOperation delegate:self didSaveSelector:@selector(document:didSave:contextInfo:) contextInfo:NULL]; + [self saveToURL:_fileURL ofType:[self fileType] forSaveOperation:CPSaveOperation delegate:delegate didSaveSelector:didSaveSelector contextInfo:contextInfo]; } else - [self saveDocumentAs:self]; + [self _saveDocumentAsWithDelegate:delegate didSaveSelector:didSaveSelector contextInfo:contextInfo]; } /*! @@ -784,22 +812,30 @@ var CPDocumentUntitledCount = 0; */ - (void)saveDocumentAs:(id)aSender { - _documentName = window.prompt("Document Name:"); + [self _saveDocumentAsWithDelegate:nil didSaveSelector:nil contextInfo:nil]; +} - if (!_documentName) +- (void)_saveDocumentAsWithDelegate:(id)delegate didSaveSelector:(SEL)didSaveSelector contextInfo:(Object)contextInfo +{ + var savePanel = [CPSavePanel savePanel], + response = [savePanel runModal]; + + if (!response) return; - + + var saveURL = [savePanel URL]; + [[CPNotificationCenter defaultCenter] postNotificationName:CPDocumentWillSaveNotification object:self]; - - [self saveToURL:[self proposedFileURL] ofType:[self fileType] forSaveOperation:CPSaveAsOperation delegate:self didSaveSelector:@selector(document:didSave:contextInfo:) contextInfo:NULL]; + + [self saveToURL:saveURL ofType:[self fileType] forSaveOperation:CPSaveAsOperation delegate:delegate didSaveSelector:didSaveSelector contextInfo:contextInfo]; } /* @ignore */ -- (void)document:(id)aDocument didSave:(BOOL)didSave contextInfo:(id)aContextInfo +- (void)_sendDocumentSavedNotification:(BOOL)didSave { if (didSave) [[CPNotificationCenter defaultCenter] @@ -813,6 +849,72 @@ var CPDocumentUntitledCount = 0; @end +@implementation CPDocument (ClosingDocuments) + +- (void)close +{ + [_windowControllers makeObjectsPerformSelector:@selector(removeDocumentAndCloseIfNecessary:) withObject:self]; + [[CPDocumentController sharedDocumentController] removeDocument:self]; +} + +- (void)shouldCloseWindowController:(CPWindowController)controller delegate:(id)delegate shouldCloseSelector:(SEL)selector contextInfo:(Object)info +{ + if ([controller shouldCloseDocument] || ([_windowControllers count] < 2 && [_windowControllers indexOfObject:controller] !== CPNotFound)) + [self canCloseDocumentWithDelegate:self shouldCloseSelector:@selector(_document:shouldClose:context:) contextInfo:{delegate:delegate, selector:selector, context:info}]; + + else if ([delegate respondsToSelector:selector]) + objj_msgSend(delegate, selector, self, YES, info); +} + +- (void)_document:(CPDocument)aDocument shouldClose:(BOOL)shouldClose context:(Object)context +{ + if (aDocument === self && shouldClose) + [self close]; + + objj_msgSend(context.delegate, context.selector, aDocument, shouldClose, context.context); +} + +- (void)canCloseDocumentWithDelegate:(id)aDelegate shouldCloseSelector:(SEL)aSelector contextInfo:(Object)context +{ + if (![self isDocumentEdited]) + return [aDelegate respondsToSelector:aSelector] && objj_msgSend(aDelegate, aSelector, self, YES, context); + + _canCloseAlert = [[CPAlert alloc] init]; + + [_canCloseAlert setDelegate:self]; + [_canCloseAlert setAlertStyle:CPWarningAlertStyle]; + [_canCloseAlert setTitle:@"Unsaved Document"]; + [_canCloseAlert setMessageText:sprintf(@"Do you want to save the changes you've made to the document \"%@\"?", + [self displayName] || [self fileName])]; + + [_canCloseAlert addButtonWithTitle:@"Save"]; + [_canCloseAlert addButtonWithTitle:@"Cancel"]; + [_canCloseAlert addButtonWithTitle:@"Don't Save"]; + + _canCloseAlert._context = {delegate:aDelegate, selector:aSelector, context:context}; + + [_canCloseAlert runModal]; +} + +- (void)alertDidEnd:(CPAlert)alert returnCode:(int)returnCode +{ + if (alert !== _canCloseAlert) + return; + + var delegate = alert._context.delegate, + selector = alert._context.selector, + context = alert._context.context; + + if (returnCode === 0) + [self saveDocumentWithDelegate:delegate didSaveSelector:selector contextInfo:context]; + else + objj_msgSend(delegate, selector, self, returnCode === 2, context); + + _canCloseAlert = nil; +} + +@end + var _CPReadSessionMake = function(aType, aDelegate, aDidReadSelector, aContextInfo) { return { fileType:aType, delegate:aDelegate, didReadSelector:aDidReadSelector, contextInfo:aContextInfo }; diff --git a/AppKit/CPDocumentController.j b/AppKit/CPDocumentController.j index 4a8f83724..1e6917315 100644 --- a/AppKit/CPDocumentController.j +++ b/AppKit/CPDocumentController.j @@ -24,6 +24,7 @@ @import @import "CPDocument.j" +@import "CPOpenPanel.j"; var CPSharedDocumentController = nil; @@ -113,7 +114,7 @@ var CPSharedDocumentController = nil; [theDocument makeWindowControllers]; [theDocument showWindows]; } - + return theDocument; } @@ -138,16 +139,19 @@ var CPSharedDocumentController = nil; - (CPDocument)openDocumentWithContentsOfURL:(CPURL)anAbsoluteURL display:(BOOL)shouldDisplay error:(CPError)anError { var result = [self documentForURL:anAbsoluteURL]; - + if (!result) { var type = [self typeForContentsOfURL:anAbsoluteURL error:anError]; - + result = [self makeDocumentWithContentsOfURL:anAbsoluteURL ofType:type delegate:self didReadSelector:@selector(document:didRead:contextInfo:) contextInfo:[CPDictionary dictionaryWithObject:shouldDisplay forKey:@"shouldDisplay"]]; + + if (result) + [self noteNewRecentDocument:result]; } else if (shouldDisplay) [result showWindows]; - + return result; } @@ -219,6 +223,20 @@ var CPSharedDocumentController = nil; [self openUntitledDocumentOfType:[[_documentTypes objectAtIndex:0] objectForKey:@"CPBundleTypeName"] display:YES]; } +-(void)openDocument:(id)aSender +{ + var openPanel = [CPOpenPanel openPanel]; + + [openPanel runModal]; + + var URLs = [openPanel URLs], + index = 0, + count = [URLs count]; + + for (; index < count; ++index) + [self openDocumentWithContentsOfURL:[CPURL URLWithString:URLs[index]] display:YES error:nil]; +} + // Managing Documents /*! @@ -308,3 +326,125 @@ var CPSharedDocumentController = nil; } @end + +@implementation CPDocumentController (Closing) + +- (void)closeAllDocumentsWithDelegate:(id)aDelegate didCloseAllSelector:(SEL)didCloseSelector contextInfo:(Object)info +{ + var context = { + delegate: aDelegate, + selector: didCloseSelector, + context: info + }; + + [self _closeDocumentsStartingWith:nil shouldClose:YES context:context]; +} + +// Recursive callback method. Start it by passing in a document of nil. +- (void)_closeDocumentsStartingWith:(CPDocument)aDocument shouldClose:(BOOL)shouldClose context:(Object)context +{ + if (shouldClose) + { + [aDocument close]; + + if ([[self documents] count] > 0) + { + [[[self documents] lastObject] canCloseDocumentWithDelegate:self + shouldCloseSelector:@selector(_closeDocumentsStartingWith:shouldClose:context:) + contextInfo:context]; + return; + } + } + + if ([context.delegate respondsToSelector:context.selector]) + objj_msgSend(context.delegate, context.selector, self, [[self documents] count] === 0, context.context); +} + +@end + +@implementation CPDocumentController (Recents) + +- (CPArray)recentDocumentURLs +{ + // FIXME move this to CP land + if (typeof window["cpRecentDocumentURLs"] === 'function') + return window.cpRecentDocumentURLs(); + + return []; +} + +- (void)clearRecentDocuments:(id)sender +{ + if (typeof window["cpClearRecentDocuments"] === 'function') + window.cpClearRecentDocuments(); + + [self _updateRecentDocumentsMenu]; +} + +- (void)noteNewRecentDocument:(CPDocument)aDocument +{ + [self noteNewRecentDocumentURL:[[aDocument fileURL] absoluteString]]; +} + +- (void)noteNewRecentDocumentURL:(CPString)aURL +{ + if (typeof window["cpNoteNewRecentDocumentPath"] === 'function') + window.cpNoteNewRecentDocumentPath(aURL); + + [self _updateRecentDocumentsMenu]; +} + +- (void)_removeAllRecentDocumentsFromMenu:(CPMenu)aMenu +{ + var items = [aMenu itemArray], + count = [items count]; + + while (count--) + { + var item = items[count]; + + if ([item action] === @selector(_openRecentDocument:)) + [aMenu removeItemAtIndex:count]; + } +} + +- (void)_updateRecentDocumentsMenu +{ + var menu = [[CPApp mainMenu] _menuWithName:@"_CPRecentDocumentsMenu"], + recentDocuments = [self recentDocumentURLs], + menuItems = [menu itemArray], + documentCount = [recentDocuments count], + menuItemCount = [menuItems count]; + + [self _removeAllRecentDocumentsFromMenu:menu]; + + if (menuItemCount) + { + if (!documentCount) + { + if ([menuItems[0] isSeparatorItem]) + [menu removeItemAtIndex:0]; + } + else + { + if (![menuItems[0] isSeparatorItem]) + [menu insertItem:[CPMenuItem separatorItem] atIndex:0]; + } + } + + while (documentCount--) + { + var path = recentDocuments[documentCount], + item = [[CPMenuItem alloc] initWithTitle:[path lastPathComponent] action:@selector(_openRecentDocument:) keyEquivalent:nil]; + + [item setTag:path]; + [menu insertItem:item atIndex:0]; + } +} + +- (void)_openRecentDocument:(id)sender +{ + [self openDocumentWithContentsOfURL:[sender tag] display:YES error:nil]; +} + +@end diff --git a/AppKit/CPEvent.j b/AppKit/CPEvent.j index eb942a79f..95993fb8a 100644 --- a/AppKit/CPEvent.j +++ b/AppKit/CPEvent.j @@ -515,6 +515,14 @@ var _CPEventPeriodicEventPeriod = 0, return _deltaZ; } +- (BOOL)_couldBeKeyEquivalent +{ + // FIXME: More cases? Space? + return _type === CPKeyDown && + _modifierFlags & (CPCommandKeyMask | CPControlKeyMask) && + [_characters length] > 0; +} + /*! Generates periodic events every \c aPeriod seconds. @param aDelay the number of seconds before the first event diff --git a/AppKit/CPMenu.j b/AppKit/CPMenu.j index 6c0ce76ba..ef7579484 100644 --- a/AppKit/CPMenu.j +++ b/AppKit/CPMenu.j @@ -30,6 +30,7 @@ @import "CPMenuItem.j" @import "CPPanel.j" +#include "CoreGraphics/CGGeometry.h" #include "Platform/Platform.h" @@ -738,7 +739,21 @@ var _CPMenuBarVisible = NO, */ - (void)cancelTracking { - [_menuWindow cancelTracking]; + [[CPRunLoop currentRunLoop] performSelector:@selector(_fireCancelTrackingEvent) target:self argument:nil order:0 modes:[CPDefaultRunLoopMode]]; +} + +- (void)_fireCancelTrackingEvent +{ + [CPApp sendEvent:[CPEvent + otherEventWithType:CPAppKitDefined + location:_CGPointMakeZero() + modifierFlags:0 + timestamp:0 + windowNumber:0 + context:0 + subtype:0 + data1:0 + data2:0]]; } /* @ignore */ @@ -767,7 +782,7 @@ var _CPMenuBarVisible = NO, { var item = _items[index], modifierMask = [item keyEquivalentModifierMask]; - + if ((modifierFlags & (CPShiftKeyMask | CPAlternateKeyMask | CPCommandKeyMask | CPControlKeyMask)) == modifierMask && [characters caseInsensitiveCompare:[item keyEquivalent]] == CPOrderedSame) { @@ -850,6 +865,22 @@ var _CPMenuBarVisible = NO, } } +- (void)_menuWithName:(CPString)aName +{ + if (aName === _name) + return self; + + for (var i = 0, count = [_items count]; i < count; i++) + { + var menu = [[_items[i] submenu] _menuWithName:aName]; + + if (menu) + return menu; + } + + return nil; +} + @end @@ -928,7 +959,7 @@ var STICKY_TIME_INTERVAL = 500, */ @implementation _CPMenuWindow : CPWindow { - _CPMenuView _menuView; + _CPMenuView _menuView; CPClipView _menuClipView; CPView _lastMouseOverMenuView; @@ -991,6 +1022,7 @@ var STICKY_TIME_INTERVAL = 500, { [self setLevel:CPPopUpMenuWindowLevel]; [self setHasShadow:YES]; + [self setShadowStyle:CPMenuWindowShadowStyle]; [self setAcceptsMouseMovedEvents:YES]; _unconstrainedFrame = CGRectMakeZero(); @@ -1034,7 +1066,7 @@ var STICKY_TIME_INTERVAL = 500, [_menuView setFont:aFont]; } -- (void)setBackgroundStyle:(_CPMenuWindowBackgroundStyle)aBackgroundStyle ++ (CPColor)backgroundColorForBackgroundStyle:(_CPMenuWindowBackgroundStyle)aBackgroundStyle { var color = _CPMenuWindowBackgroundColors[aBackgroundStyle]; @@ -1076,8 +1108,13 @@ var STICKY_TIME_INTERVAL = 500, _CPMenuWindowBackgroundColors[aBackgroundStyle] = color; } - - [self setBackgroundColor:color]; + + return color; +} + +- (void)setBackgroundStyle:(_CPMenuWindowBackgroundStyle)aBackgroundStyle +{ + [self setBackgroundColor:[[self class] backgroundColorForBackgroundStyle:aBackgroundStyle]]; } - (void)setMenu:(CPMenu)aMenu @@ -1178,13 +1215,10 @@ var STICKY_TIME_INTERVAL = 500, [_menuView scrollPoint:CGPointMake(0.0, [self convertBaseToGlobal:clipFrame.origin].y - menuViewOrigin.y)]; } -- (void)cancelTracking -{ - _trackingCanceled = YES; -} - - (void)beginTrackingWithEvent:(CPEvent)anEvent sessionDelegate:(id)aSessionDelegate didEndSelector:(SEL)aDidEndSelector { + CPApp._activeMenu = [_menuView menu]; + _startTime = [anEvent timestamp];//new Date(); _scrollingState = _CPMenuWindowScrollingStateNone; _trackingCanceled = NO; @@ -1198,7 +1232,41 @@ var STICKY_TIME_INTERVAL = 500, - (void)trackEvent:(CPEvent)anEvent { var type = [anEvent type], - theWindow = [anEvent window], + menu = [_menuView menu]; + + // Close Menu Event. + if (type === CPAppKitDefined) + { + // Stop all periodic events at this point. + [CPEvent stopPeriodicEvents]; + + var highlightedItem = [[_menuView menu] highlightedItem]; + + [menu _highlightItemAtIndex:CPNotFound]; + + [self orderOut:self]; + + var delegate = [menu delegate]; + + if ([delegate respondsToSelector:@selector(menuDidClose:)]) + [delegate menuDidClose:menu]; + + if (_sessionDelegate && _didEndSelector) + objj_msgSend(_sessionDelegate, _didEndSelector, self, highlightedItem); + + [[CPNotificationCenter defaultCenter] + postNotificationName:CPMenuDidEndTrackingNotification + object:menu]; + + // Clear these now so its faster next time around. + [_menuView setMenu:nil]; + + CPApp._activeMenu = nil; + + return; + } + + var theWindow = [anEvent window], globalLocation = theWindow ? [theWindow convertBaseToGlobal:[anEvent locationInWindow]] : [anEvent locationInWindow]; if (type === CPPeriodic) @@ -1222,8 +1290,7 @@ var STICKY_TIME_INTERVAL = 500, _lastGlobalLocation = globalLocation; - var menu = [_menuView menu], - menuLocation = [self convertGlobalToBase:globalLocation], + var menuLocation = [self convertGlobalToBase:globalLocation], activeItemIndex = [_menuView itemIndexAtPoint:[_menuView convertPoint:menuLocation fromView:nil]], mouseOverMenuView = [[menu itemAtIndex:activeItemIndex] view]; @@ -1279,47 +1346,11 @@ var STICKY_TIME_INTERVAL = 500, else if (oldScrollingState == _CPMenuWindowScrollingStateNone) [CPEvent startPeriodicEventsAfterDelay:0.0 withPeriod:0.04]; } - else if (type == CPLeftMouseUp && ([anEvent timestamp] - _startTime > STICKY_TIME_INTERVAL)) - { - // Stop these if they're still goin'. - if (_scrollingState != _CPMenuWindowScrollingStateNone) - [CPEvent stopPeriodicEvents]; - - [self cancelTracking]; - } + [menu cancelTracking]; } - - if (_trackingCanceled) - { - // Stop all periodic events at this point. - [CPEvent stopPeriodicEvents]; - - var highlightedItem = [[_menuView menu] highlightedItem]; - - [menu _highlightItemAtIndex:CPNotFound]; - - [self orderOut:self]; - var delegate = [menu delegate]; - - if ([delegate respondsToSelector:@selector(menuDidClose:)]) - [delegate menuDidClose:menu]; - - if (_sessionDelegate && _didEndSelector) - objj_msgSend(_sessionDelegate, _didEndSelector, self, highlightedItem); - - [[CPNotificationCenter defaultCenter] - postNotificationName:CPMenuDidEndTrackingNotification - object:menu]; - - // Clear these now so its faster next time around. - [_menuView setMenu:nil]; - - return; - } - - [CPApp setTarget:self selector:@selector(trackEvent:) forNextEventMatchingMask:CPPeriodicMask | CPMouseMovedMask | CPLeftMouseDraggedMask | CPLeftMouseUpMask untilDate:nil inMode:nil dequeue:YES]; + [CPApp setTarget:self selector:@selector(trackEvent:) forNextEventMatchingMask:CPPeriodicMask | CPMouseMovedMask | CPLeftMouseDraggedMask | CPLeftMouseUpMask | CPAppKitDefinedMask untilDate:nil inMode:nil dequeue:YES]; } @end @@ -1492,7 +1523,8 @@ var _CPMenuBarWindowBackgroundColor = nil, - (id)init { - var contentRect = [CPPlatform isBrowser] ? [[CPPlatformWindow primaryPlatformWindow] contentBounds] : [[self screen] visibleFrame]; + // This only shows up in browser land, so don't bother calculating metrics in desktop. + var contentRect = [[CPPlatformWindow primaryPlatformWindow] contentBounds]; contentRect.size.height = MENUBAR_HEIGHT; @@ -1969,11 +2001,14 @@ var _CPMenuBarWindowBackgroundColor = nil, } } -- (void)setFrameSize:(CGSize)aSize +- (void)setFrame:(CGRect)aRect display:(BOOL)shouldDisplay animate:(BOOL)shouldAnimate { - [super setFrameSize:aSize]; - - [self tile]; + var size = [self frame].size; + + [super setFrame:aRect display:shouldDisplay animate:shouldAnimate]; + + if (!_CGSizeEqualToSize(size, aRect.size)) + [self tile]; } @end diff --git a/AppKit/CPMenuItem.j b/AppKit/CPMenuItem.j index 2a35aceb2..7306b8f49 100644 --- a/AppKit/CPMenuItem.j +++ b/AppKit/CPMenuItem.j @@ -744,26 +744,29 @@ CPControlKeyMask @end -var CPMenuItemIsSeparatorKey = @"CPMenuItemIsSeparatorKey", +var CPMenuItemIsSeparatorKey = @"CPMenuItemIsSeparatorKey", - CPMenuItemTitleKey = @"CPMenuItemTitleKey", - CPMenuItemTargetKey = @"CPMenuItemTargetKey", - CPMenuItemActionKey = @"CPMenuItemActionKey", + CPMenuItemTitleKey = @"CPMenuItemTitleKey", + CPMenuItemTargetKey = @"CPMenuItemTargetKey", + CPMenuItemActionKey = @"CPMenuItemActionKey", - CPMenuItemIsEnabledKey = @"CPMenuItemIsEnabledKey", - CPMenuItemIsHiddenKey = @"CPMenuItemIsHiddenKey", + CPMenuItemIsEnabledKey = @"CPMenuItemIsEnabledKey", + CPMenuItemIsHiddenKey = @"CPMenuItemIsHiddenKey", - CPMenuItemTagKey = @"CPMenuItemTagKey", - CPMenuItemStateKey = @"CPMenuItemStateKey", + CPMenuItemTagKey = @"CPMenuItemTagKey", + CPMenuItemStateKey = @"CPMenuItemStateKey", - CPMenuItemImageKey = @"CPMenuItemImageKey", - CPMenuItemAlternateImageKey = @"CPMenuItemAlternateImageKey", + CPMenuItemImageKey = @"CPMenuItemImageKey", + CPMenuItemAlternateImageKey = @"CPMenuItemAlternateImageKey", - CPMenuItemSubmenuKey = @"CPMenuItemSubmenuKey", - CPMenuItemMenuKey = @"CPMenuItemMenuKey", + CPMenuItemSubmenuKey = @"CPMenuItemSubmenuKey", + CPMenuItemMenuKey = @"CPMenuItemMenuKey", - CPMenuItemRepresentedObjectKey = @"CPMenuItemRepresentedObjectKey", - CPMenuItemViewKey = @"CPMenuItemViewKey"; + CPMenuItemKeyEquivalentKey = @"CPMenuItemKeyEquivalentKey", + CPMenuItemKeyEquivalentModifierMaskKey = @"CPMenuItemKeyEquivalentModifierMaskKey", + + CPMenuItemRepresentedObjectKey = @"CPMenuItemRepresentedObjectKey", + CPMenuItemViewKey = @"CPMenuItemViewKey"; #define DEFAULT_VALUE(aKey, aDefaultValue) [aCoder containsValueForKey:(aKey)] ? [aCoder decodeObjectForKey:(aKey)] : (aDefaultValue) #define ENCODE_IFNOT(aKey, aValue, aDefaultValue) if ((aValue) !== (aDefaultValue)) [aCoder encodeObject:(aValue) forKey:(aKey)]; @@ -804,8 +807,8 @@ var CPMenuItemIsSeparatorKey = @"CPMenuItemIsSeparatorKey", _submenu = DEFAULT_VALUE(CPMenuItemSubmenuKey, nil); _menu = DEFAULT_VALUE(CPMenuItemMenuKey, nil); -// CPString _keyEquivalent; -// unsigned _keyEquivalentModifierMask; + _keyEquivalent = [aCoder decodeObjectForKey:CPMenuItemKeyEquivalentKey] || @""; + _keyEquivalentModifierMask = [aCoder decodeObjectForKey:CPMenuItemKeyEquivalentModifierMaskKey] || 0; // int _mnemonicLocation; @@ -847,6 +850,12 @@ var CPMenuItemIsSeparatorKey = @"CPMenuItemIsSeparatorKey", ENCODE_IFNOT(CPMenuItemSubmenuKey, _submenu, nil); ENCODE_IFNOT(CPMenuItemMenuKey, _menu, nil); + if (_keyEquivalent && _keyEquivalent.length) + [aCoder encodeObject:_keyEquivalent forKey:CPMenuItemKeyEquivalentKey]; + + if (_keyEquivalentModifierMask) + [aCoder encodeObject:_keyEquivalentModifierMask forKey:CPMenuItemKeyEquivalentModifierMaskKey]; + ENCODE_IFNOT(CPMenuItemRepresentedObjectKey, _representedObject, nil); ENCODE_IFNOT(CPMenuItemViewKey, _view, nil); } diff --git a/AppKit/CPOpenPanel.j b/AppKit/CPOpenPanel.j index 058853416..e8c2fe676 100644 --- a/AppKit/CPOpenPanel.j +++ b/AppKit/CPOpenPanel.j @@ -1,61 +1,46 @@ @import -#include "Platform/Platform.h" -var SharedOpenPanel = nil; @implementation CPOpenPanel : CPPanel { - CPArray _files; - BOOL _canChooseFiles @accessors(property=canChooseFiles); - BOOL _canChooseDirectories @accessors(property=canChooseDirectories); - BOOL _allowsMultipleSelection @accessors(property=allowsMultipleSelection); + BOOL _canChooseFiles @accessors(property=canChooseFiles); + BOOL _canChooseDirectories @accessors(property=canChooseDirectories); + BOOL _allowsMultipleSelection @accessors(property=allowsMultipleSelection); + CPURL _directoryURL @accessors(property=directoryURL); + CPArray _URLs; } + (id)openPanel { - if (!SharedOpenPanel) - SharedOpenPanel = [[CPOpenPanel alloc] init]; - - return SharedOpenPanel; + return [[CPOpenPanel alloc] init]; } -- (id)init +- (CPInteger)runModal { - if (self = [super init]) + if (typeof window["cpOpenPanel"] === "function") { - _files = []; - _canChooseFiles = YES; + // FIXME: Is this correct??? + [[CPRunLoop currentRunLoop] limitDateForMode:CPDefaultRunLoopMode]; + + var options = { directoryURL: [self directoryURL], + canChooseFiles: [self canChooseFiles], + canChooseDirectories: [self canChooseDirectories], + allowsMultipleSelection: [self allowsMultipleSelection] }; + + var result = window.cpOpenPanel(options); + + _URLs = result.URLs; + + return result.button; } - return self; + throw "-runModal is unimplemented."; } -- (void)filenames +- (CPArray)URLs { - return _files; -} - -- (unsigned)runModalForDirectory:(CPString)absoluteDirectoryPath file:(CPString)filename types:(CPArray)fileTypes -{ -#if PLATFORM(DOM) - if (window.Titanium) - { - _files = Titanium.Desktop.openFiles({ - path:absoluteDirectoryPath, - types:fileTypes, - multiple:_allowsMultipleSelection, - filename:filename, - directories:_canChooseDirectories, - files:_canChooseFiles - }); - } -#endif -} - -- (unsigned)runModalForTypes:(CPArray)fileTypes -{alert("HERE"); - [self runModalForDirectory:"/" file:nil types:fileTypes]; + return _URLs; } @end diff --git a/AppKit/CPSavePanel.j b/AppKit/CPSavePanel.j new file mode 100644 index 000000000..f8e888d8d --- /dev/null +++ b/AppKit/CPSavePanel.j @@ -0,0 +1,44 @@ + +@import + + +@implementation CPSavePanel : CPPanel +{ + CPURL _URL; +} + ++ (id)savePanel +{ + return [[CPSavePanel alloc] init]; +} + +- (CPInteger)runModal +{ + // FIXME: Is this correct??? + [[CPRunLoop currentRunLoop] limitDateForMode:CPDefaultRunLoopMode]; + + if (typeof window["cpSavePanel"] === "function") + { + var resultObject = window.cpSavePanel(), + result = resultObject.button; + + _URL = result ? [CPURL URLWithString:resultObject.URL] : nil; + } + else + { + // FIXME: This is not the best way to do this. + var documentName = window.prompt("Document Name:"), + result = documentName !== null; + + _URL = result ? [[self class] proposedFileURLWithDocumentName:documentName] : nil; + } + + return result; +} + +- (CPURL)URL +{ + return _URL; +} + +@end diff --git a/AppKit/CPSearchField.j b/AppKit/CPSearchField.j index 699de2a3a..85ef90282 100644 --- a/AppKit/CPSearchField.j +++ b/AppKit/CPSearchField.j @@ -106,10 +106,12 @@ var CPSearchFieldSearchImage = nil, [_cancelButton setHidden:YES]; + [_cancelButton setAutoresizingMask:CPViewMinXMargin]; [self addSubview:_cancelButton]; _searchButton = [[CPButton alloc] initWithFrame:CPMakeRect(5,(frame.size.height-25)/2,25,25)]; [_searchButton setBezelStyle:CPRegularSquareBezelStyle]; + [_searchButton setAutoresizingMask:CPViewMaxXMargin] [_searchButton setBordered:NO]; [_searchButton setImageScaling:CPScaleToFit]; diff --git a/AppKit/CPTabView.j b/AppKit/CPTabView.j index 66ad3d3e6..60dd53a3d 100644 --- a/AppKit/CPTabView.j +++ b/AppKit/CPTabView.j @@ -260,9 +260,11 @@ var CPTabViewDidSelectTabViewItemSelector = 1, */ - (void)removeTabViewItem:(CPTabViewItem)aTabViewItem { + var index = [self indexOfTabViewItem:aTabViewItem]; + [_tabViewItems removeObjectIdenticalTo:aTabViewItem]; - [_labelsView tabView:self didRemoveTabViewItem:aTabViewItem]; + [_labelsView tabView:self didRemoveTabViewItemAtIndex:index]; if (_delegateSelectors & CPTabViewDidChangeNumberOfTabViewItemsSelector) [_delegate tabViewDidChangeNumberOfTabViewItems:self]; @@ -648,10 +650,9 @@ var _CPTabLabelsViewBackgroundColor = nil, [self layoutSubviews]; } -- (void)tabView:(CPTabView)aTabView didRemoveTabViewItem:(CPTabViewItem)aTabViewItem +- (void)tabView:(CPTabView)aTabView didRemoveTabViewItemAtIndex:(unsigned)index { - var index = [aTabView indexOfTabViewItem:aTabViewItem], - label = _tabLabels[index]; + var label = _tabLabels[index]; [_tabLabels removeObjectAtIndex:index]; diff --git a/AppKit/CPTableColumn.j b/AppKit/CPTableColumn.j index 9653af630..8a8f84d33 100644 --- a/AppKit/CPTableColumn.j +++ b/AppKit/CPTableColumn.j @@ -209,6 +209,11 @@ CPTableColumnUserResizingMask = 2; [CPException raise:CPInvalidArgumentException reason:@"Attempt to set nil header view on " + [self description]]; _headerView = aView; + + var tableHeaderView = [_tableView headerView]; + + [tableHeaderView setNeedsLayout]; + [tableHeaderView setNeedsDisplay:YES]; } - (CPView)headerView diff --git a/AppKit/CPTableView.j b/AppKit/CPTableView.j index e7113c653..e4588c429 100644 --- a/AppKit/CPTableView.j +++ b/AppKit/CPTableView.j @@ -538,7 +538,7 @@ CPTableViewSolidHorizontalGridLineMask = 1 << 1; count = NUMBER_OF_COLUMNS(); for (; index < count; ++index) - if ([_tableColumns identifier] === anIdentifier) + if ([_tableColumns[index] identifier] === anIdentifier) return index; return CPNotFound; diff --git a/AppKit/CPTextField.j b/AppKit/CPTextField.j index 0303ee1bb..926810c36 100644 --- a/AppKit/CPTextField.j +++ b/AppKit/CPTextField.j @@ -30,50 +30,18 @@ #include "Platform/DOM/CPDOMDisplayServer.h" -/* - @global - @group CPLineBreakMode -*/ CPLineBreakByWordWrapping = 0; -/* - @global - @group CPLineBreakMode -*/ CPLineBreakByCharWrapping = 1; -/* - @global - @group CPLineBreakMode -*/ CPLineBreakByClipping = 2; -/* - @global - @group CPLineBreakMode -*/ CPLineBreakByTruncatingHead = 3; -/* - @global - @group CPLineBreakMode -*/ CPLineBreakByTruncatingTail = 4; -/* - @global - @group CPLineBreakMode -*/ CPLineBreakByTruncatingMiddle = 5; -/* - A textfield bezel with a squared corners. - @global - @group CPTextFieldBezelStyle -*/ -CPTextFieldSquareBezel = 0; -/* - A textfield bezel with rounded corners. - @global - @group CPTextFieldBezelStyle -*/ -CPTextFieldRoundedBezel = 1; +CPTextFieldSquareBezel = 0; /*! A textfield bezel with a squared corners. */ +CPTextFieldRoundedBezel = 1; /*! A textfield bezel with rounded corners. */ +CPTextFieldDidFocusNotification = @"CPTextFieldDidFocusNotification"; +CPTextFieldDidBlurNotification = @"CPTextFieldDidBlurNotification"; #if PLATFORM(DOM) @@ -118,6 +86,8 @@ CPTextFieldStatePlaceholder = CPThemeState("placeholder"); */ @implementation CPTextField : CPControl { + BOOL _isEditing; + BOOL _isEditable; BOOL _isSelectable; BOOL _isSecure; @@ -269,8 +239,14 @@ CPTextFieldStatePlaceholder = CPThemeState("placeholder"); if (aDOMEvent && aDOMEvent.keyCode == CPReturnKeyCode) { - [owner sendAction:[owner action] to:[owner target]]; - [[owner window] makeFirstResponder:nil]; + if (owner._isEditing) + { + owner._isEditing = NO; + [owner textDidEndEditing:[CPNotification notificationWithName:CPControlTextDidEndEditingNotification object:owner userInfo:nil]]; + } + + [owner sendAction:[owner action] to:[owner target]]; + [owner selectText:nil]; } else if (aDOMEvent && aDOMEvent.keyCode == CPTabKeyCode) { @@ -290,6 +266,12 @@ CPTextFieldStatePlaceholder = CPThemeState("placeholder"); if ([CPTextFieldInputOwner stringValue] !== CPTextFieldTextDidChangeValue) { + if (!CPTextFieldInputOwner._isEditing) + { + CPTextFieldInputOwner._isEditing = YES; + [CPTextFieldInputOwner textDidBeginEditing:[CPNotification notificationWithName:CPControlTextDidBeginEditingNotification object:CPTextFieldInputOwner userInfo:nil]]; + } + [CPTextFieldInputOwner textDidChange:[CPNotification notificationWithName:CPControlTextDidChangeNotification object:CPTextFieldInputOwner userInfo:nil]]; CPTextFieldTextDidChangeValue = [CPTextFieldInputOwner stringValue]; } @@ -567,6 +549,8 @@ CPTextFieldStatePlaceholder = CPThemeState("placeholder"); [self setNeedsLayout]; + _isEditing = NO; + #if PLATFORM(DOM) var string = [self stringValue], @@ -597,14 +581,12 @@ CPTextFieldStatePlaceholder = CPThemeState("placeholder"); window.setTimeout(function() { - element.value = [self stringValue]; element.focus(); CPTextFieldInputOwner = self; }, 0.0); - //post CPControlTextDidBeginEditingNotification - [self textDidBeginEditing:[CPNotification notificationWithName:CPControlTextDidBeginEditingNotification object:self userInfo:nil]]; - + element.value = [self stringValue]; + [[[self window] platformWindow] _propagateCurrentDOMEvent:YES]; CPTextFieldInputIsActive = YES; @@ -618,6 +600,7 @@ CPTextFieldStatePlaceholder = CPThemeState("placeholder"); document.body.onselectstart = function () {}; } + [self textDidFocus:[CPNotification notificationWithName:CPTextFieldDidFocusNotification object:self userInfo:nil]]; #endif return YES; @@ -664,11 +647,38 @@ CPTextFieldStatePlaceholder = CPThemeState("placeholder"); #endif //post CPControlTextDidEndEditingNotification - [self textDidEndEditing:[CPNotification notificationWithName:CPControlTextDidEndEditingNotification object:self userInfo:nil]]; + if (_isEditing) + { + _isEditing = NO; + [self textDidEndEditing:[CPNotification notificationWithName:CPControlTextDidEndEditingNotification object:self userInfo:nil]]; + + if ([self sendsActionOnEndEditing]) + [self sendAction:[self action] to:[self target]]; + } + + [self textDidBlur:[CPNotification notificationWithName:CPTextFieldDidBlurNotification object:self userInfo:nil]]; return YES; } +- (void)textDidBlur:(CPNotification)note +{ + //this looks to prevent false propagation of notifications for other objects + if([note object] != self) + return; + + [[CPNotificationCenter defaultCenter] postNotification:note]; +} + +- (void)textDidFocus:(CPNotification)note +{ + //this looks to prevent false propagation of notifications for other objects + if([note object] != self) + return; + + [[CPNotificationCenter defaultCenter] postNotification:note]; +} + - (void)mouseDown:(CPEvent)anEvent { // Don't track! (ever?) @@ -691,8 +701,10 @@ CPTextFieldStatePlaceholder = CPThemeState("placeholder"); */ - (void)_setStringValue:(id)aValue { + [self willChangeValueForKey:@"objectValue"]; [super setObjectValue:String(aValue)]; [self _updatePlaceholderState]; + [self didChangeValueForKey:@"objectValue"]; } - (void)setObjectValue:(id)aValue @@ -808,6 +820,8 @@ CPTextFieldStatePlaceholder = CPThemeState("placeholder"); [defaultCenter removeObserver:_delegate name:CPControlTextDidBeginEditingNotification object:self]; [defaultCenter removeObserver:_delegate name:CPControlTextDidChangeNotification object:self]; [defaultCenter removeObserver:_delegate name:CPControlTextDidEndEditingNotification object:self]; + [defaultCenter removeObserver:_delegate name:CPTextFieldDidFocusNotification object:self]; + [defaultCenter removeObserver:_delegate name:CPTextFieldDidBlurNotification object:self]; } _delegate = aDelegate; @@ -834,6 +848,19 @@ CPTextFieldStatePlaceholder = CPThemeState("placeholder"); name:CPControlTextDidEndEditingNotification object:self]; + if ([_delegate respondsToSelector:@selector(controlTextDidFocus:)]) + [defaultCenter + addObserver:_delegate + selector:@selector(controlTextDidFocus:) + name:CPTextFieldDidFocusNotification + object:self]; + + if ([_delegate respondsToSelector:@selector(controlTextDidBlur:)]) + [defaultCenter + addObserver:_delegate + selector:@selector(controlTextDidBlur:) + name:CPTextFieldDidBlurNotification + object:self]; } - (id)delegate @@ -952,13 +979,7 @@ var secureStringForString = function(aString) if (!aString) return ""; - var secureString = "", - length = aString.length; - - while (length--) - secureString += CPSecureTextFieldCharacter; - - return secureString; + return Array(aString.length+1).join(CPSecureTextFieldCharacter); } diff --git a/AppKit/CPView.j b/AppKit/CPView.j index c0e7a5157..c53892f76 100644 --- a/AppKit/CPView.j +++ b/AppKit/CPView.j @@ -1861,6 +1861,19 @@ setBoundsOrigin: @implementation CPView (KeyView) +- (BOOL)performKeyEquivalent:(CPEvent)anEvent +{ + var subviews = [self subviews], + count = [subviews count]; + + // Is reverse iteration correct here? It matches the other (correct) code like hit testing. + while (count--) + if ([subviews[count] performKeyEquivalent:anEvent]) + return YES; + + return NO; +} + - (BOOL)canBecomeKeyView { return [self acceptsFirstResponder] && ![self isHiddenOrHasHiddenAncestor]; diff --git a/AppKit/CPViewController.j b/AppKit/CPViewController.j index a5493b960..7aa31ec85 100644 --- a/AppKit/CPViewController.j +++ b/AppKit/CPViewController.j @@ -166,14 +166,16 @@ var CPViewControllerViewKey = @"CPViewControllerViewKey", - CPViewControllerTitleKey = @"CPViewControllerTitleKey"; + CPViewControllerTitleKey = @"CPViewControllerTitleKey", + CPViewControllerCibNameKey = @"CPViewControllerCibNameKey", + CPViewControllerBundleKey = @"CPViewControllerBundleKey"; @implementation CPViewController (CPCoding) /*! - Initializes the view item by unarchiving data from a coder. + Initializes the view controller by unarchiving data from a coder. @param aCoder the coder from which the data will be unarchived - @return the initialized collection view item + @return the initialized view controller */ - (id)initWithCoder:(CPCoder)aCoder { @@ -183,14 +185,20 @@ var CPViewControllerViewKey = @"CPViewControllerViewKey", { _view = [aCoder decodeObjectForKey:CPViewControllerViewKey]; _title = [aCoder decodeObjectForKey:CPViewControllerTitleKey]; + _cibName = [aCoder decodeObjectForKey:CPViewControllerCibNameKey]; + + var bundlePath = [aCoder decodeObjectForKey:CPViewControllerBundleKey]; + _cibBundle = bundlePath ? [CPBundle bundleWithPath:bundlePath] : [CPBundle mainBundle]; + + _cibExternalNameTable = [CPDictionary dictionaryWithObject:self forKey:CPCibOwner]; } return self; } /*! - Archives the colletion view item to the provided coder. - @param aCoder the coder to which the view item should be archived + Archives the view controller to the provided coder. + @param aCoder the coder to which the view controller should be archived */ - (void)encodeWithCoder:(CPCoder)aCoder { @@ -198,6 +206,8 @@ var CPViewControllerViewKey = @"CPViewControllerViewKey", [aCoder encodeObject:_view forKey:CPViewControllerViewKey]; [aCoder encodeObject:_title forKey:CPViewControllerTitleKey]; + [aCoder encodeObject:_cibName forKey:CPViewControllerCibNameKey]; + [aCoder encodeObject:[_cibBundle bundlePath] forKey:CPViewControllerBundleKey]; } @end diff --git a/AppKit/CPWindow/CPWindow.j b/AppKit/CPWindow/CPWindow.j index 2c0c0693d..fc82ea3a8 100644 --- a/AppKit/CPWindow/CPWindow.j +++ b/AppKit/CPWindow/CPWindow.j @@ -258,7 +258,6 @@ var CPWindowSaveImage = nil, BOOL _isMovableByWindowBackground; unsigned _shadowStyle; - BOOL _supportsMultipleDocuments; BOOL _isDocumentEdited; BOOL _isDocumentSaving; @@ -460,19 +459,6 @@ CPTexturedBackgroundWindowMask return _CPBorderlessBridgeWindowView; } -- (void)setSupportsMultipleDocuments:(BOOL)shouldSupportMultipleDocuments -{ - shouldSupportMultipleDocuments = !!shouldSupportMultipleDocuments; - - // FIXME: throw exception if window controller already has multiple documents and shouldSupportMultipleDocuments === NO - _supportsMultipleDocuments = shouldSupportMultipleDocuments; -} - -- (BOOL)supportsMultipleDocuments -{ - return _supportsMultipleDocuments; -} - - (void)awakeFromCib { if (_initialFirstResponder) @@ -581,7 +567,7 @@ CPTexturedBackgroundWindowMask */ + (CGRect)frameRectForContentRect:(CGRect)aContentRect styleMask:(unsigned)aStyleMask { - return [[[self class] _windowViewClassForStyleMask:_styleMask] frameRectForContentRect:aContentRect]; + return [[[self class] _windowViewClassForStyleMask:aStyleMask] frameRectForContentRect:aContentRect]; } /*! @@ -1004,6 +990,7 @@ CPTexturedBackgroundWindowMask - (void)setShadowStyle:(unsigned)aStyle { _shadowStyle = aStyle; + [[self platformWindow] setShadowStyle:_shadowStyle]; } @@ -1264,6 +1251,8 @@ CPTexturedBackgroundWindowMask if (_leftMouseDownView != _firstResponder && [_leftMouseDownView acceptsFirstResponder]) [self makeFirstResponder:_leftMouseDownView]; + [CPApp activateIgnoringOtherApps:YES]; + var theWindow = [anEvent window]; if ([theWindow isKeyWindow] || [theWindow becomesKeyOnlyIfNeeded]) @@ -1521,6 +1510,8 @@ CPTexturedBackgroundWindowMask _isDocumentEdited = isDocumentEdited; [CPMenu _setMenuBarIconImageAlphaValue:_isDocumentEdited ? 0.5 : 1.0]; + + [_windowView setDocumentEdited:isDocumentEdited]; } /*! @@ -1633,6 +1624,9 @@ CPTexturedBackgroundWindowMask */ - (void)performClose:(id)aSender { + if (!(_styleMask & CPClosableWindowMask)) + return; + // Only send ONE windowShouldClose: message. if ([_delegate respondsToSelector:@selector(windowShouldClose:)]) { @@ -1643,8 +1637,45 @@ CPTexturedBackgroundWindowMask // 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; - - [self close]; + + var documents = [_windowController documents]; + if ([documents count]) + { + var index = [documents indexOfObject:[_windowController document]]; + + [documents[index] shouldCloseWindowController:_windowController + delegate:self + shouldCloseSelector:@selector(_windowControllerContainingDocument:shouldClose:contextInfo:) + contextInfo:{documents:[documents copy], visited:0, index:index}]; + } + else + [self close]; +} + +- (void)_windowControllerContainingDocument:(CPDocument)document shouldClose:(BOOL)shouldClose contextInfo:(Object)context +{ + if (shouldClose) + { + var windowController = [self windowController], + documents = context.documents, + count = [documents count], + visited = ++context.visited, + index = ++context.index % count; + + [document removeWindowController:windowController]; + + if (visited < count) + { + [windowController setDocument:documents[index]]; + + [documents[index] shouldCloseWindowController:_windowController + delegate:self + shouldCloseSelector:@selector(_windowControllerContainingDocument:shouldClose:contextInfo:) + contextInfo:context]; + } + else + [self close]; + } } /*! @@ -1653,9 +1684,9 @@ CPTexturedBackgroundWindowMask */ - (void)close { - [[CPNotificationCenter defaultCenter] postNotificationName:CPWindowWillCloseNotification object:self]; + [[CPNotificationCenter defaultCenter] postNotificationName:CPWindowWillCloseNotification object:self]; - [self orderOut:nil]; + [self orderOut:nil]; } // Managing Main Status @@ -1965,10 +1996,19 @@ CPTexturedBackgroundWindowMask return NO; } -- (void)keyDown:(CPEvent)event +- (void)performKeyEquivalent:(CPEvent)anEvent { - if (![self performKeyEquivalent:event]) - [self interpretKeyEvents:[event]]; + // FIXME: should we be starting at the root, in other words _windowView? + // The evidence seems to point to no... + return [[self contentView] performKeyEquivalent:anEvent]; +} + +- (void)keyDown:(CPEvent)anEvent +{ + // 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]]; } - (void)insertNewline:(id)sender @@ -2378,6 +2418,12 @@ function _CPWindowFullPlatformWindowSessionMake(aWindowView, aContentRect, hasSh return { windowView:aWindowView, contentRect:aContentRect, hasShadow:hasShadow, level:aLevel }; } +CPStandardWindowShadowStyle = 0; +CPMenuWindowShadowStyle = 1; +CPPanelWindowShadowStyle = 2; +CPCustomWindowShadowStyle = 3; + + @import "_CPWindowView.j" @import "_CPStandardWindowView.j" @import "_CPDocModalWindowView.j" diff --git a/AppKit/CPWindow/_CPDocModalWindowView.j b/AppKit/CPWindow/_CPDocModalWindowView.j index 9db0ee9c7..221fbe011 100644 --- a/AppKit/CPWindow/_CPDocModalWindowView.j +++ b/AppKit/CPWindow/_CPDocModalWindowView.j @@ -11,7 +11,7 @@ var _CPStandardWindowViewBodyBackgroundColor = nil; + (CPColor)bodyBackgroundColor { if (!_CPStandardWindowViewBodyBackgroundColor) - _CPStandardWindowViewBodyBackgroundColor = [CPColor colorWithWhite:0.96 alpha:1.0]; + _CPStandardWindowViewBodyBackgroundColor = [CPColor colorWithWhite:0.96 alpha:0.9]; return _CPStandardWindowViewBodyBackgroundColor; } diff --git a/AppKit/CPWindow/_CPStandardWindowView.j b/AppKit/CPWindow/_CPStandardWindowView.j index 5b100a2cc..8ea33a72e 100644 --- a/AppKit/CPWindow/_CPStandardWindowView.j +++ b/AppKit/CPWindow/_CPStandardWindowView.j @@ -94,13 +94,15 @@ var _CPTexturedWindowHeadGradientColor = nil, @end -var _CPStandardWindowViewBodyBackgroundColor = nil, - _CPStandardWindowViewDividerBackgroundColor = nil, - _CPStandardWindowViewTitleBackgroundColor = nil, - _CPStandardWindowViewCloseButtonImage = nil, - _CPStandardWindowViewCloseButtonHighlightedImage = nil, - _CPStandardWindowViewMinimizeButtonImage = nil, - _CPStandardWindowViewMinimizeButtonHighlightedImage = nil; +var _CPStandardWindowViewBodyBackgroundColor = nil, + _CPStandardWindowViewDividerBackgroundColor = nil, + _CPStandardWindowViewTitleBackgroundColor = nil, + _CPStandardWindowViewCloseButtonImage = nil, + _CPStandardWindowViewCloseButtonHighlightedImage = nil, + _CPStandardWindowViewCloseButtonUnsavedImage = nil, + _CPStandardWindowViewCloseButtonUnsavedHighlightedImage = nil, + _CPStandardWindowViewMinimizeButtonImage = nil, + _CPStandardWindowViewMinimizeButtonHighlightedImage = nil; var STANDARD_GRADIENT_HEIGHT = 41.0; STANDARD_TITLEBAR_HEIGHT = 25.0; @@ -115,6 +117,8 @@ var STANDARD_GRADIENT_HEIGHT = 41.0; CPTextField _titleField; CPButton _closeButton; CPButton _minimizeButton; + + BOOL _isDocumentEdited; } + (CPColor)bodyBackgroundColor @@ -262,14 +266,14 @@ var STANDARD_GRADIENT_HEIGHT = 41.0; _CPStandardWindowViewCloseButtonImage = [[CPImage alloc] initWithContentsOfFile:[bundle pathForResource:@"CPWindow/Standard/CPWindowStandardCloseButton.png"] size:CGSizeMake(16.0, 16.0)]; _CPStandardWindowViewCloseButtonHighlightedImage = [[CPImage alloc] initWithContentsOfFile:[bundle pathForResource:@"CPWindow/Standard/CPWindowStandardCloseButtonHighlighted.png"] size:CGSizeMake(16.0, 16.0)]; + _CPStandardWindowViewCloseButtonUnsavedImage = [[CPImage alloc] initWithContentsOfFile:[bundle pathForResource:@"CPWindow/Standard/CPWindowStandardCloseButtonUnsaved.png"] size:CGSizeMake(16.0, 16.0)]; + _CPStandardWindowViewCloseButtonUnsavedHighlightedImage = [[CPImage alloc] initWithContentsOfFile:[bundle pathForResource:@"CPWindow/Standard/CPWindowStandardCloseButtonUnsavedHighlighted.png"] size:CGSizeMake(16.0, 16.0)]; } _closeButton = [[CPButton alloc] initWithFrame:CGRectMake(8.0, 7.0, 16.0, 16.0)]; [_closeButton setBordered:NO]; - - [_closeButton setImage:_CPStandardWindowViewCloseButtonImage]; - [_closeButton setAlternateImage:_CPStandardWindowViewCloseButtonHighlightedImage]; + [self _updateCloseButton]; [self addSubview:_closeButton]; } @@ -367,6 +371,27 @@ var STANDARD_GRADIENT_HEIGHT = 41.0; } } */ + +- (void)_updateCloseButton +{ + if (_isDocumentEdited) + { + [_closeButton setImage:_CPStandardWindowViewCloseButtonUnsavedImage]; + [_closeButton setAlternateImage:_CPStandardWindowViewCloseButtonUnsavedHighlightedImage]; + } + else + { + [_closeButton setImage:_CPStandardWindowViewCloseButtonImage]; + [_closeButton setAlternateImage:_CPStandardWindowViewCloseButtonHighlightedImage]; + } +} + +- (void)setDocumentEdited:(BOOL)isEdited +{ + _isDocumentEdited = isEdited; + [self _updateCloseButton]; +} + - (void)setTitle:(CPString)aTitle { [_titleField setStringValue:aTitle]; diff --git a/AppKit/CPWindow/_CPWindowView.j b/AppKit/CPWindow/_CPWindowView.j index 5231016f6..0f878fd9a 100644 --- a/AppKit/CPWindow/_CPWindowView.j +++ b/AppKit/CPWindow/_CPWindowView.j @@ -88,6 +88,10 @@ var _CPWindowViewResizeIndicatorImage = nil; return self; } +- (void)setDocumentEdited:(BOOL)isEdited +{ +} + - (void)setTitle:(CPString)aTitle { } diff --git a/AppKit/CPWindowController.j b/AppKit/CPWindowController.j index e9e9abe4a..aaaf14291 100644 --- a/AppKit/CPWindowController.j +++ b/AppKit/CPWindowController.j @@ -43,8 +43,10 @@ { CPWindow _window; + CPArray _documents; CPDocument _document; BOOL _shouldCloseDocument; + BOOL _supportsMultipleDocuments; id _cibOwner; CPString _windowCibName; @@ -74,6 +76,8 @@ [self setShouldCloseDocument:NO]; [self setNextResponder:CPApp]; + + _documents = []; } return self; @@ -220,6 +224,9 @@ if (_document) { + if (![self supportsMultipleDocuments]) + [self removeDocument:_document]; + [defaultCenter removeObserver:self name:CPDocumentWillSaveNotification object:_document]; @@ -237,6 +244,8 @@ if (_document) { + [self addDocument:_document]; + [defaultCenter addObserver:self selector:@selector(_documentWillSave:) name:CPDocumentWillSaveNotification @@ -263,6 +272,48 @@ [self synchronizeWindowTitleWithDocumentName]; } +- (void)setSupportsMultipleDocuments:(BOOL)shouldSupportMultipleDocuments +{ + _supportsMultipleDocuments = shouldSupportMultipleDocuments; +} + +- (BOOL)supportsMultipleDocuments +{ + return _supportsMultipleDocuments; +} + +- (void)addDocument:(CPDocument)aDocument +{ + if (aDocument && ![_documents containsObject:aDocument]) + [_documents addObject:aDocument]; +} + +- (void)removeDocument:(CPDocument)aDocument +{ + var index = [_documents indexOfObjectIdenticalTo:aDocument]; + + if (index === CPNotFound) + return; + + [_documents removeObjectAtIndex:index]; + + if (_document === aDocument && [_documents count]) + [self setDocument:[_documents objectAtIndex:MIN(index, [_documents count] - 1)]]; +} + +- (void)removeDocumentAndCloseIfNecessary:(CPDocument)aDocument +{ + [self removeDocument:aDocument]; + + if (![_documents count]) + [self close]; +} + +- (CPArray)documents +{ + return _documents; +} + - (void)setViewController:(CPViewController)aViewController { var containerView = [self viewControllerContainerView] || [[self window] contentView], @@ -348,6 +399,27 @@ */ - (void)setDocumentEdited:(BOOL)isEdited { + // we ignore this. in a multi-doc world, we base this flag on the logical OR + // of all the open documents edited state. so this is just a hint to re-check that state. + //[[self window] setDocumentEdited:isEdited]; + [self _synchronizeDocumentEditedState]; +} + +- (void)_synchronizeDocumentEditedState +{ + var docs = [self documents], + count = [docs count], + isEdited = NO; + + while (count--) + { + if ([docs[count] isDocumentEdited]) + { + isEdited = YES; + break; + } + } + [[self window] setDocumentEdited:isEdited]; } diff --git a/AppKit/Cib/_CPCibWindowTemplate.j b/AppKit/Cib/_CPCibWindowTemplate.j index f7880e5d1..47763f774 100644 --- a/AppKit/Cib/_CPCibWindowTemplate.j +++ b/AppKit/Cib/_CPCibWindowTemplate.j @@ -2,18 +2,19 @@ @import -var _CPCibWindowTemplateMinSizeKey = @"_CPCibWindowTemplateMinSizeKey", - _CPCibWindowTemplateMaxSizeKey = @"_CPCibWindowTemplateMaxSizeKey", +var _CPCibWindowTemplateMinSizeKey = @"_CPCibWindowTemplateMinSizeKey", + _CPCibWindowTemplateMaxSizeKey = @"_CPCibWindowTemplateMaxSizeKey", - _CPCibWindowTemplateViewClassKey = @"_CPCibWindowTemplateViewClassKey", - _CPCibWindowTemplateWindowClassKey = @"_CPCibWindowTemplateWindowClassKey", + _CPCibWindowTemplateViewClassKey = @"_CPCibWindowTemplateViewClassKey", + _CPCibWindowTemplateWindowClassKey = @"_CPCibWindowTemplateWindowClassKey", - _CPCibWindowTemplateWindowRectKey = @"_CPCibWindowTemplateWindowRectKey", - _CPCibWindowTemplateWindowStyleMaskKey = @"_CPCibWindowTempatStyleMaskKey", - _CPCibWindowTemplateWindowTitleKey = @"_CPCibWindowTemplateWindowTitleKey", - _CPCibWindowTemplateWindowViewKey = @"_CPCibWindowTemplateWindowViewKey", + _CPCibWindowTemplateWindowRectKey = @"_CPCibWindowTemplateWindowRectKey", + _CPCibWindowTemplateWindowStyleMaskKey = @"_CPCibWindowTempatStyleMaskKey", + _CPCibWindowTemplateWindowTitleKey = @"_CPCibWindowTemplateWindowTitleKey", + _CPCibWindowTemplateWindowViewKey = @"_CPCibWindowTemplateWindowViewKey", - _CPCibWindowTemplateWindowIsFullBridgeKey = @"_CPCibWindowTemplateWindowIsFullBridgeKey"; + _CPCibWindowTemplateWindowAutorecalculatesKeyViewLoop = @"_CPCibWindowTemplateWindowAutorecalculatesKeyViewLoop"; + _CPCibWindowTemplateWindowIsFullPlatformWindowKey = @"_CPCibWindowTemplateWindowIsFullPlatformWindowKey"; @implementation _CPCibWindowTemplate : CPObject { @@ -30,7 +31,8 @@ var _CPCibWindowTemplateMinSizeKey = @"_CPCibWindowTemplateMinSizeK CPString _windowTitle; CPView _windowView; - BOOL _windowIsFullBridge; + BOOL _windowAutorecalculatesKeyViewLoop; + BOOL _windowIsFullPlatformWindow; } - (id)init @@ -46,7 +48,7 @@ var _CPCibWindowTemplateMinSizeKey = @"_CPCibWindowTemplateMinSizeK _windowTitle = @"Window"; _windowView = [[CPView alloc] initWithFrame:CGRectMake(0.0, 0.0, 400.0, 200.0)]; - _windowIsFullBridge = NO; + _windowIsFullPlatformWindow = NO; } return self; @@ -72,7 +74,8 @@ var _CPCibWindowTemplateMinSizeKey = @"_CPCibWindowTemplateMinSizeK _windowTitle = [aCoder decodeObjectForKey:_CPCibWindowTemplateWindowTitleKey]; _windowView = [aCoder decodeObjectForKey:_CPCibWindowTemplateWindowViewKey]; - _windowIsFullBridge = [aCoder decodeObjectForKey:_CPCibWindowTemplateWindowIsFullBridgeKey]; + _windowAutorecalculatesKeyViewLoop = !![aCoder decodeObjectForKey:_CPCibWindowTemplateWindowAutorecalculatesKeyViewLoop]; + _windowIsFullPlatformWindow = !![aCoder decodeObjectForKey:_CPCibWindowTemplateWindowIsFullPlatformWindowKey]; } return self; @@ -94,7 +97,11 @@ var _CPCibWindowTemplateMinSizeKey = @"_CPCibWindowTemplateMinSizeK [aCoder encodeObject:_windowTitle forKey:_CPCibWindowTemplateWindowTitleKey]; [aCoder encodeObject:_windowView forKey:_CPCibWindowTemplateWindowViewKey]; - [aCoder encodeObject:_windowIsFullBridge forKey:_CPCibWindowTemplateWindowIsFullBridgeKey]; + if (_windowAutorecalculatesKeyViewLoop) + [aCoder encodeObject:_windowAutorecalculatesKeyViewLoop forKey:_CPCibWindowTemplateWindowAutorecalculatesKeyViewLoop]; + + if (_windowIsFullPlatformWindow) + [aCoder encodeObject:_windowIsFullPlatformWindow forKey:_CPCibWindowTemplateWindowIsFullPlatformWindowKey]; } - (CPString)customClassName @@ -144,7 +151,8 @@ var _CPCibWindowTemplateMinSizeKey = @"_CPCibWindowTemplateMinSizeK [theWindow setToolbar:_viewClass]; } - [theWindow setFullBridge:_windowIsFullBridge]; + [theWindow setAutorecalculatesKeyViewLoop:_windowAutorecalculatesKeyViewLoop]; + [theWindow setFullBridge:_windowIsFullPlatformWindow]; return theWindow; } diff --git a/AppKit/CoreGraphics/CGGeometry.j b/AppKit/CoreGraphics/CGGeometry.j index a046638fe..3c2902ddb 100644 --- a/AppKit/CoreGraphics/CGGeometry.j +++ b/AppKit/CoreGraphics/CGGeometry.j @@ -70,6 +70,8 @@ _function(CGInsetMakeZero()) _function(CGInsetMakeCopy(anInset)) _function(CGInsetIsEmpty(anInset)) +CGRectNull = _CGRectMake(Infinity, Infinity, 0.0, 0.0); + /*! @addtogroup appkit @{ @@ -174,6 +176,15 @@ function CGRectStandardize(aRect) function CGRectUnion(lhsRect, rhsRect) { + var lhsRectIsNull = !lhsRect || lhsRect === CGRectNull, + rhsRectIsNull = !rhsRect || rhsRect === CGRectNull; + + if (lhsRectIsNull) + return rhsRectIsNull ? CGRectNull : rhsRect; + + if (rhsRectIsNull) + return lhsRectIsNull ? CGRectNull : lhsRect; + var minX = MIN(_CGRectGetMinX(lhsRect), _CGRectGetMinX(rhsRect)), minY = MIN(_CGRectGetMinY(lhsRect), _CGRectGetMinY(rhsRect)), maxX = MAX(_CGRectGetMaxX(lhsRect), _CGRectGetMaxX(rhsRect)), diff --git a/AppKit/Platform/CPPlatform.j b/AppKit/Platform/CPPlatform.j index 851e538bc..a5eac3712 100644 --- a/AppKit/Platform/CPPlatform.j +++ b/AppKit/Platform/CPPlatform.j @@ -45,4 +45,12 @@ window.cpTerminate(); } ++ (void)activateIgnoringOtherApps:(BOOL)shouldIgnoreOtherApps +{ +#if PLATFORM(DOM) + if (typeof window["cpActivateIgnoringOtherApps"] === "function") + window.cpActivateIgnoringOtherApps(!!shouldIgnoreOtherApps); +#endif +} + @end diff --git a/AppKit/Platform/DOM/CPDOMWindowLayer.j b/AppKit/Platform/DOM/CPDOMWindowLayer.j index 3d6f3dede..a41fa4cc9 100644 --- a/AppKit/Platform/DOM/CPDOMWindowLayer.j +++ b/AppKit/Platform/DOM/CPDOMWindowLayer.j @@ -68,7 +68,7 @@ count = _windows.length - 1; CPDOMDisplayServerRemoveChild(_DOMElement, aWindow._DOMElement); - + [_windows removeObjectAtIndex:aWindow._index]; for (; index < count; ++index) @@ -107,11 +107,12 @@ _windows[zIndex]._index = zIndex; _windows[zIndex]._DOMElement.style.zIndex = zIndex; } + // If the window is not already a resident of this layer, add it. - if (!isVisible) + if (aWindow._DOMElement.parentNode !== _DOMElement) { CPDOMDisplayServerAppendChild(_DOMElement, aWindow._DOMElement); - + aWindow._isVisible = YES; if ([aWindow isFullBridge]) diff --git a/AppKit/Platform/DOM/CPPlatformWindow+DOM.j b/AppKit/Platform/DOM/CPPlatformWindow+DOM.j index 49a4cab15..84daf8745 100644 --- a/AppKit/Platform/DOM/CPPlatformWindow+DOM.j +++ b/AppKit/Platform/DOM/CPPlatformWindow+DOM.j @@ -192,6 +192,8 @@ var supportsNativeDragAndDrop = [CPPlatform supportsDragAndDrop]; // Make sure the pastboard element is blurred. _DOMPasteboardElement.blur(); + [self _addLayers]; + var theClass = [self class], dragEventImplementation = class_getMethodImplementation(theClass, @selector(dragEvent:)), @@ -250,6 +252,7 @@ var supportsNativeDragAndDrop = [CPPlatform supportsDragAndDrop]; _DOMWindow.addEventListener("unload", function() { [self updateFromNativeContentRect]; + [self _removeLayers]; theDocument.removeEventListener("mouseup", mouseEventCallback, NO); theDocument.removeEventListener("mousedown", mouseEventCallback, NO); @@ -296,6 +299,7 @@ var supportsNativeDragAndDrop = [CPPlatform supportsDragAndDrop]; _DOMWindow.attachEvent("onbeforeunload", function() { [self updateFromNativeContentRect]; + [self _removeLayers]; theDocument.removeEvent("onmouseup", mouseEventCallback); theDocument.removeEvent("onmousedown", mouseEventCallback); @@ -334,6 +338,7 @@ var supportsNativeDragAndDrop = [CPPlatform supportsDragAndDrop]; if (![CPPlatform isBrowser]) { + _DOMWindow.cpWindowNumber = [self._only windowNumber]; _DOMWindow.cpSetFrame(_contentRect); _DOMWindow.cpSetLevel(_level); _DOMWindow.cpSetHasShadow(_hasShadow); @@ -443,14 +448,16 @@ var supportsNativeDragAndDrop = [CPPlatform supportsDragAndDrop]; KeyCodesToPrevent[aDOMEvent.keyCode]; var isNativePasteEvent = NO, - isNativeCopyOrCutEvent = NO; + isNativeCopyOrCutEvent = NO, + overrideCharacters = nil; switch (aDOMEvent.type) { case "keydown": // Grab and store the keycode now since it is correct and consistent at this point. _keyCode = aDOMEvent.keyCode; - + var characters = String.fromCharCode(_keyCode).toLowerCase(); + overrideCharacters = modifierFlags & CPShiftKeyMask ? characters.toUpperCase() : characters; // 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. @@ -469,7 +476,7 @@ var supportsNativeDragAndDrop = [CPPlatform supportsDragAndDrop]; // can capture our internal Cappuccino pasteboard. else if ((characters == "c" || characters == "x") && (modifierFlags & CPPlatformActionKeyMask)) isNativeCopyOrCutEvent = YES; - + // Also, certain browsers (IE and Safari), have broken keyboard supportwhere they don't send keypresses for certain events. // So, allow the keypress event to handle the event if we are not a browser with broken (remedial) key support... else if (!CPFeatureIsCompatible(CPJavascriptRemedialKeySupport)) @@ -482,6 +489,7 @@ var supportsNativeDragAndDrop = [CPPlatform supportsDragAndDrop]; // If this is in fact our broke state, continue to keypress and send the keydown. case "keypress": // If the source of this event is our pasteboard element, then simply let it continue // as normal, so that the paste event can successfully complete. + if ((aDOMEvent.target || aDOMEvent.srcElement) == _DOMPasteboardElement) return; @@ -490,20 +498,22 @@ var supportsNativeDragAndDrop = [CPPlatform supportsDragAndDrop]; isARepeat = (_charCodes[keyCode] != nil); _charCodes[keyCode] = charCode; - - var characters = String.fromCharCode(charCode), + + var characters = overrideCharacters || String.fromCharCode(charCode), charactersIgnoringModifiers = characters.toLowerCase(); - + + // Safari won't send proper capitalization during cmd-key events + if (!overrideCharacters && (modifierFlags & CPCommandKeyMask) && (modifierFlags & CPShiftKeyMask)) + characters = characters.toUpperCase(); + event = [CPEvent keyEventWithType:CPKeyDown location:location modifierFlags:modifierFlags timestamp:timestamp windowNumber:windowNumber context:nil characters:characters charactersIgnoringModifiers:charactersIgnoringModifiers isARepeat:isARepeat keyCode:keyCode]; - + if (isNativePasteEvent) { _pasteboardKeyDownEvent = event; - window.setNativeTimeout(function () { [self _checkPasteboardElement] }, 0); - return; } @@ -705,7 +715,7 @@ var supportsNativeDragAndDrop = [CPPlatform supportsDragAndDrop]; newEvent.preventDefault = function(){if(aDOMEvent.preventDefault) aDOMEvent.preventDefault()}; newEvent.stopPropagation = function(){if(aDOMEvent.stopPropagation) aDOMEvent.stopPropagation()}; - [self _bridgeMouseEvent:newEvent]; + [self mouseEvent:newEvent]; return; } @@ -728,10 +738,10 @@ var supportsNativeDragAndDrop = [CPPlatform supportsDragAndDrop]; if (type === @"dblclick") { _overriddenEventType = CPDOMEventMouseDown; - [self _bridgeMouseEvent:aDOMEvent]; + [self mouseEvent:aDOMEvent]; _overriddenEventType = CPDOMEventMouseUp; - [self _bridgeMouseEvent:aDOMEvent]; + [self mouseEvent:aDOMEvent]; _overriddenEventType = nil; @@ -904,6 +914,34 @@ var supportsNativeDragAndDrop = [CPPlatform supportsDragAndDrop]; [layer insertWindow:aWindow atIndex:(otherWindow ? (aPlace == CPWindowAbove ? otherWindow._index + 1 : otherWindow._index) : CPNotFound)]; } +- (void)_removeLayers +{ + var levels = _windowLevels, + layers = _windowLayers, + levelCount = levels.length; + + while (levelCount--) + { + var layer = [layers objectForKey:levels[levelCount]]; + + _DOMBodyElement.removeChild(layer._DOMElement); + } +} + +- (void)_addLayers +{ + var levels = _windowLevels, + layers = _windowLayers, + levelCount = levels.length; + + while (levelCount--) + { + var layer = [layers objectForKey:levels[levelCount]]; + + _DOMBodyElement.appendChild(layer._DOMElement); + } +} + /* @ignore */ - (id)_dragHitTest:(CPPoint)aPoint pasteboard:(CPPasteboard)aPasteboard { diff --git a/AppKit/Resources/CPWindow/Standard/CPWindowStandardCloseButtonUnsaved.png b/AppKit/Resources/CPWindow/Standard/CPWindowStandardCloseButtonUnsaved.png index 2cb0e4929..9bd3566b1 100644 Binary files a/AppKit/Resources/CPWindow/Standard/CPWindowStandardCloseButtonUnsaved.png and b/AppKit/Resources/CPWindow/Standard/CPWindowStandardCloseButtonUnsaved.png differ diff --git a/AppKit/Resources/CPWindow/Standard/CPWindowStandardCloseButtonUnsavedHighlighted.png b/AppKit/Resources/CPWindow/Standard/CPWindowStandardCloseButtonUnsavedHighlighted.png index 29e0bd19e..a84804f09 100644 Binary files a/AppKit/Resources/CPWindow/Standard/CPWindowStandardCloseButtonUnsavedHighlighted.png and b/AppKit/Resources/CPWindow/Standard/CPWindowStandardCloseButtonUnsavedHighlighted.png differ diff --git a/AppKit/Themes/Aristo/Resources/button-bezel-disabled-center.png b/AppKit/Themes/Aristo/Resources/button-bezel-disabled-center.png index 2f25f5252..310e7382b 100644 Binary files a/AppKit/Themes/Aristo/Resources/button-bezel-disabled-center.png and b/AppKit/Themes/Aristo/Resources/button-bezel-disabled-center.png differ diff --git a/AppKit/Themes/Aristo/Resources/button-bezel-disabled-left.png b/AppKit/Themes/Aristo/Resources/button-bezel-disabled-left.png index 8ae5e8eb3..2ed1dc26a 100644 Binary files a/AppKit/Themes/Aristo/Resources/button-bezel-disabled-left.png and b/AppKit/Themes/Aristo/Resources/button-bezel-disabled-left.png differ diff --git a/AppKit/Themes/Aristo/Resources/button-bezel-disabled-right.png b/AppKit/Themes/Aristo/Resources/button-bezel-disabled-right.png index 92127238a..1a986e2fb 100644 Binary files a/AppKit/Themes/Aristo/Resources/button-bezel-disabled-right.png and b/AppKit/Themes/Aristo/Resources/button-bezel-disabled-right.png differ diff --git a/AppKit/Themes/Aristo/Resources/check-box-bezel-disabled.png b/AppKit/Themes/Aristo/Resources/check-box-bezel-disabled.png index c1246094f..9d595806b 100644 Binary files a/AppKit/Themes/Aristo/Resources/check-box-bezel-disabled.png and b/AppKit/Themes/Aristo/Resources/check-box-bezel-disabled.png differ diff --git a/AppKit/Themes/Aristo/Resources/check-box-bezel-mixed-disabled.png b/AppKit/Themes/Aristo/Resources/check-box-bezel-mixed-disabled.png new file mode 100644 index 000000000..2caf2010e Binary files /dev/null and b/AppKit/Themes/Aristo/Resources/check-box-bezel-mixed-disabled.png differ diff --git a/AppKit/Themes/Aristo/Resources/check-box-bezel-mixed-highlighted.png b/AppKit/Themes/Aristo/Resources/check-box-bezel-mixed-highlighted.png new file mode 100644 index 000000000..661b06627 Binary files /dev/null and b/AppKit/Themes/Aristo/Resources/check-box-bezel-mixed-highlighted.png differ diff --git a/AppKit/Themes/Aristo/Resources/check-box-bezel-mixed.png b/AppKit/Themes/Aristo/Resources/check-box-bezel-mixed.png new file mode 100644 index 000000000..d1c739a8b Binary files /dev/null and b/AppKit/Themes/Aristo/Resources/check-box-bezel-mixed.png differ diff --git a/AppKit/Themes/Aristo/Resources/check-box-bezel-selected-disabled.png b/AppKit/Themes/Aristo/Resources/check-box-bezel-selected-disabled.png index fea5c786c..191c479a9 100644 Binary files a/AppKit/Themes/Aristo/Resources/check-box-bezel-selected-disabled.png and b/AppKit/Themes/Aristo/Resources/check-box-bezel-selected-disabled.png differ diff --git a/AppKit/Themes/Aristo/Resources/default-button-bezel-disabled-center.png b/AppKit/Themes/Aristo/Resources/default-button-bezel-disabled-center.png new file mode 100644 index 000000000..20cb138e3 Binary files /dev/null and b/AppKit/Themes/Aristo/Resources/default-button-bezel-disabled-center.png differ diff --git a/AppKit/Themes/Aristo/Resources/default-button-bezel-disabled-left.png b/AppKit/Themes/Aristo/Resources/default-button-bezel-disabled-left.png new file mode 100644 index 000000000..03e468f39 Binary files /dev/null and b/AppKit/Themes/Aristo/Resources/default-button-bezel-disabled-left.png differ diff --git a/AppKit/Themes/Aristo/Resources/default-button-bezel-disabled-right.png b/AppKit/Themes/Aristo/Resources/default-button-bezel-disabled-right.png new file mode 100644 index 000000000..d3366babc Binary files /dev/null and b/AppKit/Themes/Aristo/Resources/default-button-bezel-disabled-right.png differ diff --git a/AppKit/Themes/Aristo/Resources/horizontal-track-disabled-center.png b/AppKit/Themes/Aristo/Resources/horizontal-track-disabled-center.png new file mode 100644 index 000000000..fa421383b Binary files /dev/null and b/AppKit/Themes/Aristo/Resources/horizontal-track-disabled-center.png differ diff --git a/AppKit/Themes/Aristo/Resources/horizontal-track-disabled-left.png b/AppKit/Themes/Aristo/Resources/horizontal-track-disabled-left.png new file mode 100644 index 000000000..370897e28 Binary files /dev/null and b/AppKit/Themes/Aristo/Resources/horizontal-track-disabled-left.png differ diff --git a/AppKit/Themes/Aristo/Resources/horizontal-track-disabled-right.png b/AppKit/Themes/Aristo/Resources/horizontal-track-disabled-right.png new file mode 100644 index 000000000..71c465d55 Binary files /dev/null and b/AppKit/Themes/Aristo/Resources/horizontal-track-disabled-right.png differ diff --git a/AppKit/Themes/Aristo/Resources/knob-disabled.png b/AppKit/Themes/Aristo/Resources/knob-disabled.png new file mode 100644 index 000000000..61e860a66 Binary files /dev/null and b/AppKit/Themes/Aristo/Resources/knob-disabled.png differ diff --git a/AppKit/Themes/Aristo/Resources/popup-bezel-disabled-right-pullsdown.png b/AppKit/Themes/Aristo/Resources/popup-bezel-disabled-right-pullsdown.png new file mode 100644 index 000000000..78249fb48 Binary files /dev/null and b/AppKit/Themes/Aristo/Resources/popup-bezel-disabled-right-pullsdown.png differ diff --git a/AppKit/Themes/Aristo/Resources/popup-bezel-disabled-right.png b/AppKit/Themes/Aristo/Resources/popup-bezel-disabled-right.png new file mode 100644 index 000000000..49e583a19 Binary files /dev/null and b/AppKit/Themes/Aristo/Resources/popup-bezel-disabled-right.png differ diff --git a/AppKit/Themes/Aristo/Resources/radio-bezel-disabled.png b/AppKit/Themes/Aristo/Resources/radio-bezel-disabled.png index 76d3c79d7..6d9d9bafe 100644 Binary files a/AppKit/Themes/Aristo/Resources/radio-bezel-disabled.png and b/AppKit/Themes/Aristo/Resources/radio-bezel-disabled.png differ diff --git a/AppKit/Themes/Aristo/Resources/radio-bezel-selected-disabled.png b/AppKit/Themes/Aristo/Resources/radio-bezel-selected-disabled.png index 7d5d963df..20858b2bd 100644 Binary files a/AppKit/Themes/Aristo/Resources/radio-bezel-selected-disabled.png and b/AppKit/Themes/Aristo/Resources/radio-bezel-selected-disabled.png differ diff --git a/AppKit/Themes/Aristo/Resources/scroller-horizontal-knob-disabled-center.png b/AppKit/Themes/Aristo/Resources/scroller-horizontal-knob-disabled-center.png new file mode 100644 index 000000000..7ae79b060 Binary files /dev/null and b/AppKit/Themes/Aristo/Resources/scroller-horizontal-knob-disabled-center.png differ diff --git a/AppKit/Themes/Aristo/Resources/scroller-horizontal-knob-disabled-left.png b/AppKit/Themes/Aristo/Resources/scroller-horizontal-knob-disabled-left.png new file mode 100644 index 000000000..1417e8c34 Binary files /dev/null and b/AppKit/Themes/Aristo/Resources/scroller-horizontal-knob-disabled-left.png differ diff --git a/AppKit/Themes/Aristo/Resources/scroller-horizontal-knob-disabled-right.png b/AppKit/Themes/Aristo/Resources/scroller-horizontal-knob-disabled-right.png new file mode 100644 index 000000000..ed2cd290e Binary files /dev/null and b/AppKit/Themes/Aristo/Resources/scroller-horizontal-knob-disabled-right.png differ diff --git a/AppKit/Themes/Aristo/Resources/scroller-vertical-knob-disabled-bottom.png b/AppKit/Themes/Aristo/Resources/scroller-vertical-knob-disabled-bottom.png new file mode 100644 index 000000000..1cb147b84 Binary files /dev/null and b/AppKit/Themes/Aristo/Resources/scroller-vertical-knob-disabled-bottom.png differ diff --git a/AppKit/Themes/Aristo/Resources/scroller-vertical-knob-disabled-center.png b/AppKit/Themes/Aristo/Resources/scroller-vertical-knob-disabled-center.png new file mode 100644 index 000000000..2756a41ca Binary files /dev/null and b/AppKit/Themes/Aristo/Resources/scroller-vertical-knob-disabled-center.png differ diff --git a/AppKit/Themes/Aristo/Resources/scroller-vertical-knob-disabled-top.png b/AppKit/Themes/Aristo/Resources/scroller-vertical-knob-disabled-top.png new file mode 100644 index 000000000..6ca32cb88 Binary files /dev/null and b/AppKit/Themes/Aristo/Resources/scroller-vertical-knob-disabled-top.png differ diff --git a/AppKit/Themes/Aristo/Resources/segmented-control-bezel-disabled-center.png b/AppKit/Themes/Aristo/Resources/segmented-control-bezel-disabled-center.png new file mode 100644 index 000000000..489c02b39 Binary files /dev/null and b/AppKit/Themes/Aristo/Resources/segmented-control-bezel-disabled-center.png differ diff --git a/AppKit/Themes/Aristo/Resources/segmented-control-bezel-disabled-divider.png b/AppKit/Themes/Aristo/Resources/segmented-control-bezel-disabled-divider.png new file mode 100644 index 000000000..8db39c622 Binary files /dev/null and b/AppKit/Themes/Aristo/Resources/segmented-control-bezel-disabled-divider.png differ diff --git a/AppKit/Themes/Aristo/Resources/segmented-control-bezel-disabled-left.png b/AppKit/Themes/Aristo/Resources/segmented-control-bezel-disabled-left.png new file mode 100644 index 000000000..cb7c29f61 Binary files /dev/null and b/AppKit/Themes/Aristo/Resources/segmented-control-bezel-disabled-left.png differ diff --git a/AppKit/Themes/Aristo/Resources/segmented-control-bezel-disabled-right.png b/AppKit/Themes/Aristo/Resources/segmented-control-bezel-disabled-right.png new file mode 100644 index 000000000..81611090d Binary files /dev/null and b/AppKit/Themes/Aristo/Resources/segmented-control-bezel-disabled-right.png differ diff --git a/AppKit/Themes/Aristo/Resources/segmented-control-bezel-highlighted-disabled-center.png b/AppKit/Themes/Aristo/Resources/segmented-control-bezel-highlighted-disabled-center.png new file mode 100644 index 000000000..eb00afc1f Binary files /dev/null and b/AppKit/Themes/Aristo/Resources/segmented-control-bezel-highlighted-disabled-center.png differ diff --git a/AppKit/Themes/Aristo/Resources/segmented-control-bezel-highlighted-disabled-divider.png b/AppKit/Themes/Aristo/Resources/segmented-control-bezel-highlighted-disabled-divider.png new file mode 100644 index 000000000..84cde75b9 Binary files /dev/null and b/AppKit/Themes/Aristo/Resources/segmented-control-bezel-highlighted-disabled-divider.png differ diff --git a/AppKit/Themes/Aristo/Resources/segmented-control-bezel-highlighted-disabled-left.png b/AppKit/Themes/Aristo/Resources/segmented-control-bezel-highlighted-disabled-left.png new file mode 100644 index 000000000..899b09c4e Binary files /dev/null and b/AppKit/Themes/Aristo/Resources/segmented-control-bezel-highlighted-disabled-left.png differ diff --git a/AppKit/Themes/Aristo/Resources/segmented-control-bezel-highlighted-disabled-right.png b/AppKit/Themes/Aristo/Resources/segmented-control-bezel-highlighted-disabled-right.png new file mode 100644 index 000000000..a932e6ac6 Binary files /dev/null and b/AppKit/Themes/Aristo/Resources/segmented-control-bezel-highlighted-disabled-right.png differ diff --git a/AppKit/Themes/Aristo/Resources/segmented-control-bezel-inactive-center.png b/AppKit/Themes/Aristo/Resources/segmented-control-bezel-inactive-center.png deleted file mode 100644 index 6a7b478ee..000000000 Binary files a/AppKit/Themes/Aristo/Resources/segmented-control-bezel-inactive-center.png and /dev/null differ diff --git a/AppKit/Themes/Aristo/Resources/segmented-control-bezel-inactive-divider.png b/AppKit/Themes/Aristo/Resources/segmented-control-bezel-inactive-divider.png deleted file mode 100644 index 74f4ba883..000000000 Binary files a/AppKit/Themes/Aristo/Resources/segmented-control-bezel-inactive-divider.png and /dev/null differ diff --git a/AppKit/Themes/Aristo/Resources/segmented-control-bezel-inactive-left.png b/AppKit/Themes/Aristo/Resources/segmented-control-bezel-inactive-left.png deleted file mode 100644 index cac177496..000000000 Binary files a/AppKit/Themes/Aristo/Resources/segmented-control-bezel-inactive-left.png and /dev/null differ diff --git a/AppKit/Themes/Aristo/Resources/segmented-control-bezel-inactive-right.png b/AppKit/Themes/Aristo/Resources/segmented-control-bezel-inactive-right.png deleted file mode 100644 index d04835db2..000000000 Binary files a/AppKit/Themes/Aristo/Resources/segmented-control-bezel-inactive-right.png and /dev/null differ diff --git a/AppKit/Themes/Aristo/Resources/slider-circular-disabled-bezel.png b/AppKit/Themes/Aristo/Resources/slider-circular-disabled-bezel.png new file mode 100644 index 000000000..7053d3f3a Binary files /dev/null and b/AppKit/Themes/Aristo/Resources/slider-circular-disabled-bezel.png differ diff --git a/AppKit/Themes/Aristo/Resources/slider-circular-disabled-knob.png b/AppKit/Themes/Aristo/Resources/slider-circular-disabled-knob.png new file mode 100644 index 000000000..f657e437b Binary files /dev/null and b/AppKit/Themes/Aristo/Resources/slider-circular-disabled-knob.png differ diff --git a/AppKit/Themes/Aristo/Resources/vertical-track-disabled-bottom.png b/AppKit/Themes/Aristo/Resources/vertical-track-disabled-bottom.png new file mode 100644 index 000000000..5ff3530fb Binary files /dev/null and b/AppKit/Themes/Aristo/Resources/vertical-track-disabled-bottom.png differ diff --git a/AppKit/Themes/Aristo/Resources/vertical-track-disabled-center.png b/AppKit/Themes/Aristo/Resources/vertical-track-disabled-center.png new file mode 100644 index 000000000..ca474fce8 Binary files /dev/null and b/AppKit/Themes/Aristo/Resources/vertical-track-disabled-center.png differ diff --git a/AppKit/Themes/Aristo/Resources/vertical-track-disabled-top.png b/AppKit/Themes/Aristo/Resources/vertical-track-disabled-top.png new file mode 100644 index 000000000..9edaf4fc2 Binary files /dev/null and b/AppKit/Themes/Aristo/Resources/vertical-track-disabled-top.png differ diff --git a/AppKit/Themes/Aristo/ThemeDescriptors.j b/AppKit/Themes/Aristo/ThemeDescriptors.j index 4729ecb1e..dc75cf4a5 100644 --- a/AppKit/Themes/Aristo/ThemeDescriptors.j +++ b/AppKit/Themes/Aristo/ThemeDescriptors.j @@ -54,6 +54,14 @@ [_CPCibCustomResource imageResourceWithName:"default-button-bezel-highlighted-right.png" size:CGSizeMake(4.0, 24.0)] ] isVertical:NO]], + + defaultDisabledBezelColor = [CPColor colorWithPatternImage:[[CPThreePartImage alloc] initWithImageSlices: + [ + [_CPCibCustomResource imageResourceWithName:"default-button-bezel-disabled-left.png" size:CGSizeMake(4.0, 24.0)], + [_CPCibCustomResource imageResourceWithName:"default-button-bezel-disabled-center.png" size:CGSizeMake(1.0, 24.0)], + [_CPCibCustomResource imageResourceWithName:"default-button-bezel-disabled-right.png" size:CGSizeMake(4.0, 24.0)] + ] + isVertical:NO]], disabledBezelColor = [CPColor colorWithPatternImage:[[CPThreePartImage alloc] initWithImageSlices: [ @@ -66,6 +74,7 @@ [button setValue:[CPFont boldSystemFontOfSize:12.0] forThemeAttribute:@"font" inState:CPThemeStateBordered]; [button setValue:[CPColor colorWithCalibratedWhite:79.0 / 255.0 alpha:1.0] forThemeAttribute:@"text-color"]; [button setValue:[CPColor colorWithCalibratedWhite:240.0 / 255.0 alpha:1.0] forThemeAttribute:@"text-shadow-color" inState:CPThemeStateBordered]; + [button setValue:[CPColor colorWithCalibratedWhite:240.0 / 255.0 alpha:0.6] forThemeAttribute:@"text-shadow-color" inState:CPThemeStateBordered|CPThemeStateDisabled]; [button setValue:CGSizeMake(0.0, 1.0) forThemeAttribute:@"text-shadow-offset" inState:CPThemeStateBordered]; [button setValue:CPLineBreakByTruncatingTail forThemeAttribute:@"line-break-mode"]; @@ -74,12 +83,13 @@ [button setValue:CGInsetMake(0.0, 5.0, 0.0, 5.0) forThemeAttribute:@"content-inset" inState:CPThemeStateBordered]; [button setValue:[CPColor colorWithCalibratedWhite:0.6 alpha:1.0] forThemeAttribute:@"text-color" inState:CPThemeStateDisabled]; - [button setValue:disabledBezelColor forThemeAttribute:@"bezel-color" inState:CPThemeStateDisabled]; - [button setValue:disabledBezelColor forThemeAttribute:@"bezel-color" inState:CPThemeStateDefault|CPThemeStateDisabled]; + [button setValue:disabledBezelColor forThemeAttribute:@"bezel-color" inState:CPThemeStateBordered|CPThemeStateDisabled]; + [button setValue:defaultDisabledBezelColor forThemeAttribute:@"bezel-color" inState:CPThemeStateBordered|CPThemeStateDefault|CPThemeStateDisabled]; [button setValue:defaultBezelColor forThemeAttribute:@"bezel-color" inState:CPThemeStateBordered|CPThemeStateDefault]; [button setValue:defaultHighlightedBezelColor forThemeAttribute:@"bezel-color" inState:CPThemeStateBordered|CPThemeStateHighlighted|CPThemeStateDefault]; [button setValue:[CPColor colorWithCalibratedRed:13.0/255.0 green:51.0/255.0 blue:70.0/255.0 alpha:1.0] forThemeAttribute:@"text-color" inState:CPThemeStateDefault]; + [button setValue:[CPColor colorWithCalibratedRed:13.0/255.0 green:51.0/255.0 blue:70.0/255.0 alpha:0.6] forThemeAttribute:@"text-color" inState:CPThemeStateDisabled|CPThemeStateDefault]; [button setValue:CGSizeMake(0.0, 24.0) forThemeAttribute:@"min-size"]; [button setValue:CGSizeMake(-1.0, 24.0) forThemeAttribute:@"max-size"]; @@ -116,10 +126,20 @@ [_CPCibCustomResource imageResourceWithName:"popup-bezel-right.png" size:CGSizeMake(27.0, 24.0)] ] isVertical:NO]]; - + + var disabledBezelColor = [CPColor colorWithPatternImage:[[CPThreePartImage alloc] initWithImageSlices: + [ + [_CPCibCustomResource imageResourceWithName:"button-bezel-disabled-left.png" size:CGSizeMake(4.0, 24.0)], + [_CPCibCustomResource imageResourceWithName:"button-bezel-disabled-center.png" size:CGSizeMake(1.0, 24.0)], + [_CPCibCustomResource imageResourceWithName:"popup-bezel-disabled-right.png" size:CGSizeMake(27.0, 24.0)] + ] + isVertical:NO]]; + [button setTitle:@"Pop Up"]; [button setValue:color forThemeAttribute:@"bezel-color" inState:CPThemeStateBordered]; + [button setValue:disabledBezelColor forThemeAttribute:@"bezel-color" inState:CPThemeStateBordered|CPThemeStateDisabled]; + [button setValue:CGInsetMake(0, 27.0 + 5.0, 0, 5.0) forThemeAttribute:@"content-inset" inState:CPThemeStateBordered]; [button setValue:[CPFont boldSystemFontOfSize:12.0] forThemeAttribute:@"font"]; [button setValue:[CPColor colorWithCalibratedWhite:79.0 / 255.0 alpha:1.0] forThemeAttribute:@"text-color"]; @@ -130,6 +150,9 @@ [button setValue:CGSizeMake(-1.0, 24.0) forThemeAttribute:@"max-size"]; [button addItemWithTitle:@"item"]; + + [button setValue:[CPColor colorWithCalibratedWhite:0.6 alpha:1.0] forThemeAttribute:@"text-color" inState:CPThemeStateBordered|CPThemeStateDisabled]; + [button setValue:[CPColor colorWithCalibratedWhite:240.0 / 255.0 alpha:0.6] forThemeAttribute:@"text-shadow-color" inState:CPThemeStateBordered|CPThemeStateDisabled]; return button; } @@ -145,9 +168,19 @@ ] isVertical:NO]]; + var disabledColor = [CPColor colorWithPatternImage:[[CPThreePartImage alloc] initWithImageSlices: + [ + [_CPCibCustomResource imageResourceWithName:"button-bezel-disabled-left.png" size:CGSizeMake(4.0, 24.0)], + [_CPCibCustomResource imageResourceWithName:"button-bezel-disabled-center.png" size:CGSizeMake(1.0, 24.0)], + [_CPCibCustomResource imageResourceWithName:"popup-bezel-disabled-right-pullsdown.png" size:CGSizeMake(27.0, 24.0)] + ] + isVertical:NO]]; + [button setTitle:@"Pull Down"]; [button setValue:color forThemeAttribute:@"bezel-color" inState:CPPopUpButtonStatePullsDown|CPThemeStateBordered]; + [button setValue:disabledColor forThemeAttribute:@"bezel-color" inState:CPThemeStateDisabled|CPPopUpButtonStatePullsDown|CPThemeStateBordered]; + [button setValue:CGInsetMake(0, 27.0 + 5.0, 0, 5.0) forThemeAttribute:@"content-inset" inState:CPThemeStateBordered]; [button setValue:[CPFont boldSystemFontOfSize:12.0] forThemeAttribute:@"font"]; [button setValue:[CPColor colorWithCalibratedWhite:79.0 / 255.0 alpha:1.0] forThemeAttribute:@"text-color"]; @@ -157,6 +190,9 @@ [button setValue:CGSizeMake(32.0, 24.0) forThemeAttribute:@"min-size"]; [button setValue:CGSizeMake(-1.0, 24.0) forThemeAttribute:@"max-size"]; + [button setValue:[CPColor colorWithCalibratedWhite:0.6 alpha:1.0] forThemeAttribute:@"text-color" inState:CPThemeStateBordered|CPThemeStateDisabled]; + [button setValue:[CPColor colorWithCalibratedWhite:240.0 / 255.0 alpha:0.6] forThemeAttribute:@"text-shadow-color" inState:CPThemeStateBordered|CPThemeStateDisabled]; + [button addItemWithTitle:@"item"]; return button; @@ -200,8 +236,17 @@ [_CPCibCustomResource imageResourceWithName:"scroller-vertical-knob-bottom.png" size:CGSizeMake(15.0, 10.0)] ] isVertical:YES]); + + var knobDisabledColor = PatternColor([[CPThreePartImage alloc] initWithImageSlices: + [ + [_CPCibCustomResource imageResourceWithName:"scroller-vertical-knob-disabled-top.png" size:CGSizeMake(15.0, 10.0)], + [_CPCibCustomResource imageResourceWithName:"scroller-vertical-knob-disabled-center.png" size:CGSizeMake(15.0, 1.0)], + [_CPCibCustomResource imageResourceWithName:"scroller-vertical-knob-disabled-bottom.png" size:CGSizeMake(15.0, 10.0)] + ] + isVertical:YES]); [scroller setValue:knobColor forThemeAttribute:@"knob-color" inState:CPThemeStateVertical]; + [scroller setValue:knobDisabledColor forThemeAttribute:@"knob-color" inState:CPThemeStateVertical|CPThemeStateDisabled]; [scroller setFloatValue:0.1]; [scroller setKnobProportion:0.5]; @@ -248,7 +293,16 @@ ] isVertical:NO]); + var knobDisabledColor = PatternColor([[CPThreePartImage alloc] initWithImageSlices: + [ + [_CPCibCustomResource imageResourceWithName:"scroller-horizontal-knob-disabled-left.png" size:CGSizeMake(10.0, 15.0)], + [_CPCibCustomResource imageResourceWithName:"scroller-horizontal-knob-disabled-center.png" size:CGSizeMake(1.0, 15.0)], + [_CPCibCustomResource imageResourceWithName:"scroller-horizontal-knob-disabled-right.png" size:CGSizeMake(10.0, 15.0)] + ] + isVertical:NO]); + [scroller setValue:knobColor forThemeAttribute:@"knob-color"]; + [scroller setValue:knobDisabledColor forThemeAttribute:@"knob-color" inState:CPThemeStateDisabled]; [scroller setFloatValue:0.1]; [scroller setKnobProportion:0.5]; @@ -351,6 +405,8 @@ [button setTitle:@"Hello Friend!"]; + [button setValue:[CPColor colorWithCalibratedWhite:79.0 / 255.0 alpha:1.0] forThemeAttribute:@"text-color" inState:CPThemeStateDisabled]; + var bezelColor = PatternColor([[CPThreePartImage alloc] initWithImageSlices: [ [_CPCibCustomResource imageResourceWithName:"radio-bezel.png" size:CGSizeMake(17.0, 17.0)], nil, nil @@ -403,6 +459,8 @@ [button setTitle:@"Another option"]; + [button setValue:[CPColor colorWithCalibratedWhite:79.0 / 255.0 alpha:1.0] forThemeAttribute:@"text-color" inState:CPThemeStateDisabled]; + var bezelColor = PatternColor([[CPThreePartImage alloc] initWithImageSlices: [ [_CPCibCustomResource imageResourceWithName:"check-box-bezel.png" size:CGSizeMake(15.0, 16.0)], nil, nil @@ -449,6 +507,41 @@ return button; } ++ (CPRadioButton)themedMixedCheckBoxButton +{ + button = [self themedCheckBoxButton]; + + [button setAllowsMixedState:YES]; + [button setState:CPMixedState]; + + var mixedSelectedColor = + PatternColor([[CPThreePartImage alloc] initWithImageSlices: + [ + [_CPCibCustomResource imageResourceWithName:"check-box-bezel-mixed-highlighted.png" size:CGSizeMake(15.0, 16.0)], nil, nil + ] + isVertical:NO]), + + mixedDisabledColor = + PatternColor([[CPThreePartImage alloc] initWithImageSlices: + [ + [_CPCibCustomResource imageResourceWithName:"check-box-bezel-mixed-disabled.png" size:CGSizeMake(15.0, 16.0)], nil, nil + ] + isVertical:NO]), + + mixedColor = + PatternColor([[CPThreePartImage alloc] initWithImageSlices: + [ + [_CPCibCustomResource imageResourceWithName:"check-box-bezel-mixed.png" size:CGSizeMake(15.0, 16.0)], nil, nil + ] + isVertical:NO]); + + [button setValue:mixedSelectedColor forThemeAttribute:@"bezel-color" inState:CPThemeStateBordered|CPThemeState("mixed")|CPThemeStateHighlighted]; + [button setValue:mixedColor forThemeAttribute:@"bezel-color" inState:CPThemeStateBordered|CPThemeState("mixed")]; + [button setValue:mixedDisabledColor forThemeAttribute:@"bezel-color" inState:CPThemeStateBordered|CPThemeState("mixed")|CPThemeStateDisabled]; + + return button; +} + + (CPPopUpButton)themedSegmentedControl { var segmentedControl = [[CPSegmentedControl alloc] initWithFrame:CGRectMake(0.0, 0.0, 0.0, 24.0)]; @@ -464,7 +557,7 @@ [segmentedControl setLabel:@"bar" forSegment:1]; [segmentedControl setTag:2 forSegment:1]; - [segmentedControl setWidth:25.0 forSegment:2]; + [segmentedControl setWidth:35.0 forSegment:2]; [segmentedControl setLabel:@"1" forSegment:2]; [segmentedControl setTag:3 forSegment:2]; @@ -475,10 +568,14 @@ dividerHighlightedBezelColor = PatternColor([_CPCibCustomResource imageResourceWithName:@"segmented-control-bezel-highlighted-divider.png" size:CGSizeMake(1.0, 24.0)]), leftHighlightedBezelColor = PatternColor([_CPCibCustomResource imageResourceWithName:@"segmented-control-bezel-highlighted-left.png" size:CGSizeMake(4.0, 24.0)]), rightHighlightedBezelColor = PatternColor([_CPCibCustomResource imageResourceWithName:@"segmented-control-bezel-highlighted-right.png" size:CGSizeMake(4.0, 24.0)]), - inactiveCenterBezelColor = PatternColor([_CPCibCustomResource imageResourceWithName:@"segmented-control-bezel-inactive-center.png" size:CGSizeMake(1.0, 24.0)]), - inactiveDividerBezelColor = PatternColor([_CPCibCustomResource imageResourceWithName:@"segmented-control-bezel-inactive-divider.png" size:CGSizeMake(1.0, 24.0)]), - inactiveLeftBezelColor = PatternColor([_CPCibCustomResource imageResourceWithName:@"segmented-control-bezel-inactive-left.png" size:CGSizeMake(4.0, 24.0)]), - inactiveRightBezelColor = PatternColor([_CPCibCustomResource imageResourceWithName:@"segmented-control-bezel-inactive-right.png" size:CGSizeMake(4.0, 24.0)]), + inactiveCenterBezelColor = PatternColor([_CPCibCustomResource imageResourceWithName:@"segmented-control-bezel-disabled-center.png" size:CGSizeMake(1.0, 24.0)]), + inactiveDividerBezelColor = PatternColor([_CPCibCustomResource imageResourceWithName:@"segmented-control-bezel-disabled-divider.png" size:CGSizeMake(1.0, 24.0)]), + inactiveLeftBezelColor = PatternColor([_CPCibCustomResource imageResourceWithName:@"segmented-control-bezel-disabled-left.png" size:CGSizeMake(4.0, 24.0)]), + inactiveRightBezelColor = PatternColor([_CPCibCustomResource imageResourceWithName:@"segmented-control-bezel-disabled-right.png" size:CGSizeMake(4.0, 24.0)]), + inactiveHighlightedCenterBezelColor = PatternColor([_CPCibCustomResource imageResourceWithName:@"segmented-control-bezel-highlighted-disabled-center.png" size:CGSizeMake(1.0, 24.0)]), + inactiveHighlightedDividerBezelColor = PatternColor([_CPCibCustomResource imageResourceWithName:@"segmented-control-bezel-highlighted-disabled-divider.png" size:CGSizeMake(1.0, 24.0)]), + inactiveHighlightedLeftBezelColor = PatternColor([_CPCibCustomResource imageResourceWithName:@"segmented-control-bezel-highlighted-disabled-left.png" size:CGSizeMake(4.0, 24.0)]), + inactiveHighlightedRightBezelColor = PatternColor([_CPCibCustomResource imageResourceWithName:@"segmented-control-bezel-highlighted-disabled-right.png" size:CGSizeMake(4.0, 24.0)]), leftBezelColor = PatternColor([_CPCibCustomResource imageResourceWithName:@"segmented-control-bezel-left.png" size:CGSizeMake(4.0, 24.0)]), rightBezelColor = PatternColor([_CPCibCustomResource imageResourceWithName:@"segmented-control-bezel-right.png" size:CGSizeMake(4.0, 24.0)]), pushedCenterBezelColor = PatternColor([_CPCibCustomResource imageResourceWithName:@"segmented-control-bezel-pushed-center.png" size:CGSizeMake(1.0, 24.0)]), @@ -490,23 +587,27 @@ [segmentedControl setValue:centerBezelColor forThemeAttribute:@"center-segment-bezel-color" inState:CPThemeStateNormal]; [segmentedControl setValue:inactiveCenterBezelColor forThemeAttribute:@"center-segment-bezel-color" inState:CPThemeStateDisabled]; + [segmentedControl setValue:inactiveHighlightedCenterBezelColor forThemeAttribute:@"center-segment-bezel-color" inState:CPThemeStateSelected|CPThemeStateDisabled]; [segmentedControl setValue:centerHighlightedBezelColor forThemeAttribute:@"center-segment-bezel-color" inState:CPThemeStateSelected]; [segmentedControl setValue:pushedCenterBezelColor forThemeAttribute:@"center-segment-bezel-color" inState:CPThemeStateHighlighted]; [segmentedControl setValue:pushedHighlightedCenterBezelColor forThemeAttribute:@"center-segment-bezel-color" inState:CPThemeStateHighlighted|CPThemeStateSelected]; [segmentedControl setValue:dividerBezelColor forThemeAttribute:@"divider-bezel-color" inState:CPThemeStateNormal]; [segmentedControl setValue:inactiveDividerBezelColor forThemeAttribute:@"divider-bezel-color" inState:CPThemeStateDisabled]; + [segmentedControl setValue:inactiveHighlightedDividerBezelColor forThemeAttribute:@"divider-bezel-color" inState:CPThemeStateSelected|CPThemeStateDisabled]; [segmentedControl setValue:dividerHighlightedBezelColor forThemeAttribute:@"divider-bezel-color" inState:CPThemeStateSelected]; [segmentedControl setValue:dividerBezelColor forThemeAttribute:@"divider-bezel-color" inState:CPThemeStateHighlighted]; [segmentedControl setValue:rightBezelColor forThemeAttribute:@"right-segment-bezel-color" inState:CPThemeStateNormal]; [segmentedControl setValue:inactiveRightBezelColor forThemeAttribute:@"right-segment-bezel-color" inState:CPThemeStateDisabled]; + [segmentedControl setValue:inactiveHighlightedRightBezelColor forThemeAttribute:@"right-segment-bezel-color" inState:CPThemeStateSelected|CPThemeStateDisabled]; [segmentedControl setValue:rightHighlightedBezelColor forThemeAttribute:@"right-segment-bezel-color" inState:CPThemeStateSelected]; [segmentedControl setValue:pushedRightBezelColor forThemeAttribute:@"right-segment-bezel-color" inState:CPThemeStateHighlighted]; [segmentedControl setValue:pushedHighlightedRightBezelColor forThemeAttribute:@"right-segment-bezel-color" inState:CPThemeStateHighlighted|CPThemeStateSelected]; [segmentedControl setValue:leftBezelColor forThemeAttribute:@"left-segment-bezel-color" inState:CPThemeStateNormal]; [segmentedControl setValue:inactiveLeftBezelColor forThemeAttribute:@"left-segment-bezel-color" inState:CPThemeStateDisabled]; + [segmentedControl setValue:inactiveHighlightedLeftBezelColor forThemeAttribute:@"left-segment-bezel-color" inState:CPThemeStateSelected|CPThemeStateDisabled]; [segmentedControl setValue:leftHighlightedBezelColor forThemeAttribute:@"left-segment-bezel-color" inState:CPThemeStateSelected]; [segmentedControl setValue:pushedLeftBezelColor forThemeAttribute:@"left-segment-bezel-color" inState:CPThemeStateHighlighted]; [segmentedControl setValue:pushedHighlightedLeftBezelColor forThemeAttribute:@"left-segment-bezel-color" inState:CPThemeStateHighlighted|CPThemeStateSelected]; @@ -536,17 +637,28 @@ [_CPCibCustomResource imageResourceWithName:"horizontal-track-center.png" size:CGSizeMake(1.0, 5.0)], [_CPCibCustomResource imageResourceWithName:"horizontal-track-right.png" size:CGSizeMake(4.0, 5.0)] ] + isVertical:NO]], + + trackDisabledColor = [CPColor colorWithPatternImage:[[CPThreePartImage alloc] initWithImageSlices: + [ + [_CPCibCustomResource imageResourceWithName:"horizontal-track-disabled-left.png" size:CGSizeMake(4.0, 5.0)], + [_CPCibCustomResource imageResourceWithName:"horizontal-track-disabled-center.png" size:CGSizeMake(1.0, 5.0)], + [_CPCibCustomResource imageResourceWithName:"horizontal-track-disabled-right.png" size:CGSizeMake(4.0, 5.0)] + ] isVertical:NO]]; [slider setValue:5.0 forThemeAttribute:@"track-width"]; [slider setValue:trackColor forThemeAttribute:@"track-color"]; + [slider setValue:trackDisabledColor forThemeAttribute:@"track-color" inState:CPThemeStateDisabled]; - var knobColor = [CPColor colorWithPatternImage:[_CPCibCustomResource imageResourceWithName:"knob.png" size:CGSizeMake(23.0, 24.0)]]; - knobHighlightedColor = [CPColor colorWithPatternImage:[_CPCibCustomResource imageResourceWithName:"knob-highlighted.png" size:CGSizeMake(23.0, 24.0)]]; + var knobColor = [CPColor colorWithPatternImage:[_CPCibCustomResource imageResourceWithName:"knob.png" size:CGSizeMake(23.0, 24.0)]], + knobHighlightedColor = [CPColor colorWithPatternImage:[_CPCibCustomResource imageResourceWithName:"knob-highlighted.png" size:CGSizeMake(23.0, 24.0)]], + knobDisabledColor = [CPColor colorWithPatternImage:[_CPCibCustomResource imageResourceWithName:"knob-disabled.png" size:CGSizeMake(23.0, 24.0)]]; [slider setValue:CGSizeMake(23.0, 24.0) forThemeAttribute:@"knob-size"]; [slider setValue:knobColor forThemeAttribute:@"knob-color"]; [slider setValue:knobHighlightedColor forThemeAttribute:@"knob-color" inState:CPThemeStateHighlighted]; + [slider setValue:knobDisabledColor forThemeAttribute:@"knob-color" inState:CPThemeStateDisabled]; return slider; } @@ -560,17 +672,27 @@ [_CPCibCustomResource imageResourceWithName:"vertical-track-center.png" size:CGSizeMake(5.0, 1.0)], [_CPCibCustomResource imageResourceWithName:"vertical-track-bottom.png" size:CGSizeMake(5.0, 4.0)] ] + isVertical:YES]], + trackDisabledColor = [CPColor colorWithPatternImage:[[CPThreePartImage alloc] initWithImageSlices: + [ + [_CPCibCustomResource imageResourceWithName:"vertical-track-disabled-top.png" size:CGSizeMake(5.0, 6.0)], + [_CPCibCustomResource imageResourceWithName:"vertical-track-disabled-center.png" size:CGSizeMake(5.0, 1.0)], + [_CPCibCustomResource imageResourceWithName:"vertical-track-disabled-bottom.png" size:CGSizeMake(5.0, 4.0)] + ] isVertical:YES]]; [slider setValue:5.0 forThemeAttribute:@"track-width"]; [slider setValue:trackColor forThemeAttribute:@"track-color" inState:CPThemeStateVertical]; + [slider setValue:trackDisabledColor forThemeAttribute:@"track-color" inState:CPThemeStateDisabled]; - var knobColor = [CPColor colorWithPatternImage:[_CPCibCustomResource imageResourceWithName:"knob.png" size:CGSizeMake(23.0, 24.0)]]; - knobHighlightedColor = [CPColor colorWithPatternImage:[_CPCibCustomResource imageResourceWithName:"knob-highlighted.png" size:CGSizeMake(23.0, 24.0)]]; - + var knobColor = [CPColor colorWithPatternImage:[_CPCibCustomResource imageResourceWithName:"knob.png" size:CGSizeMake(23.0, 24.0)]], + knobHighlightedColor = [CPColor colorWithPatternImage:[_CPCibCustomResource imageResourceWithName:"knob-highlighted.png" size:CGSizeMake(23.0, 24.0)]], + knobDisabledColor = [CPColor colorWithPatternImage:[_CPCibCustomResource imageResourceWithName:"knob-disabled.png" size:CGSizeMake(23.0, 24.0)]]; + [slider setValue:CGSizeMake(23.0, 24.0) forThemeAttribute:@"knob-size"]; [slider setValue:knobColor forThemeAttribute:@"knob-color"]; [slider setValue:knobHighlightedColor forThemeAttribute:@"knob-color" inState:CPThemeStateHighlighted]; + [slider setValue:knobDisabledColor forThemeAttribute:@"knob-color" inState:CPThemeStateDisabled]; return slider; } @@ -578,16 +700,20 @@ + (CPSlider)themedCircularSlider { var slider = [[CPSlider alloc] initWithFrame:CGRectMake(0.0, 0.0, 34.0, 34.0)], - trackColor = PatternColor([_CPCibCustomResource imageResourceWithName:"slider-circular-bezel.png" size:CGSizeMake(34.0, 34.0)]); + trackColor = PatternColor([_CPCibCustomResource imageResourceWithName:"slider-circular-bezel.png" size:CGSizeMake(34.0, 34.0)]), + trackDisabledColor = PatternColor([_CPCibCustomResource imageResourceWithName:"slider-circular-disabled-bezel.png" size:CGSizeMake(34.0, 34.0)]); [slider setSliderType:CPCircularSlider]; [slider setValue:trackColor forThemeAttribute:@"track-color" inState:CPThemeStateCircular]; + [slider setValue:trackDisabledColor forThemeAttribute:@"track-color" inState:CPThemeStateDisabled|CPThemeStateCircular]; var knobColor = [CPColor colorWithPatternImage:[_CPCibCustomResource imageResourceWithName:"slider-circular-knob.png" size:CGSizeMake(5.0, 5.0)]], + knobDisabledColor = [CPColor colorWithPatternImage:[_CPCibCustomResource imageResourceWithName:"slider-circular-disabled-knob.png" size:CGSizeMake(5.0, 5.0)]], knobHighlightedColor = knobColor; [slider setValue:CGSizeMake(5.0, 5.0) forThemeAttribute:@"knob-size" inState:CPThemeStateCircular]; [slider setValue:knobColor forThemeAttribute:@"knob-color" inState:CPThemeStateCircular]; + [slider setValue:knobDisabledColor forThemeAttribute:@"knob-color" inState:CPThemeStateDisabled|CPThemeStateCircular]; [slider setValue:knobHighlightedColor forThemeAttribute:@"knob-color" inState:CPThemeStateCircular|CPThemeStateHighlighted]; return slider; diff --git a/Foundation/CPArray.j b/Foundation/CPArray.j index 566eb57bb..6f17532a9 100755 --- a/Foundation/CPArray.j +++ b/Foundation/CPArray.j @@ -602,6 +602,23 @@ objj_msgSend(self[index], aSelector, anObject); } +- (void)makeObjectsPerformSelector:(SEL)aSelector withObjects:(CPArray)objects +{ + if (!aSelector) + [CPException raise:CPInvalidArgumentException reason:"makeObjectsPerformSelector:withObjects: 'aSelector' can't be nil"]; + + var index = 0, + count = length, + argumentsArray = [nil, aSelector].concat(objects || []); + + for(; index < count; ++index) + { + argumentsArray[0] = self[index]; + objj_msgSend.apply(this, argumentsArray); + } +} + + // Comparing arrays /*! Returns the first object found in the receiver (starting at index 0) which is present in the diff --git a/Foundation/CPDate.j b/Foundation/CPDate.j index 436c51a0c..3d712734c 100644 --- a/Foundation/CPDate.j +++ b/Foundation/CPDate.j @@ -127,6 +127,11 @@ var CPDateReferenceDate = new Date(Date.UTC(2001,1,1,0,0,0,0)); return [[CPDate date] timeIntervalSinceReferenceDate]; } +- (BOOL)isEqual:(CPDate)aDate +{ + return [self isEqualToDate:aDate]; +} + - (BOOL)isEqualToDate:(CPDate)anotherDate { return !(self < anotherDate || self > anotherDate); diff --git a/Foundation/CPSortDescriptor.j b/Foundation/CPSortDescriptor.j index 415e0b01a..c7b78c840 100755 --- a/Foundation/CPSortDescriptor.j +++ b/Foundation/CPSortDescriptor.j @@ -136,7 +136,7 @@ CPOrderedDescending = 1; */ - (CPComparisonResult)compareObject:(id)lhsObject withObject:(id)rhsObject { - return (_ascending ? 1 : -1) * [[lhsObject valueForKey:_key] performSelector:_selector withObject:[rhsObject valueForKey:_key]]; + return (_ascending ? 1 : -1) * [[lhsObject valueForKeyPath:_key] performSelector:_selector withObject:[rhsObject valueForKeyPath:_key]]; } /*! diff --git a/Objective-J/Tools/objj/lib-js/objj/objjc.js b/Objective-J/Tools/objj/lib-js/objj/objjc.js index 553d60e7a..461d2c3a3 100644 --- a/Objective-J/Tools/objj/lib-js/objj/objjc.js +++ b/Objective-J/Tools/objj/lib-js/objj/objjc.js @@ -3,8 +3,9 @@ var FILE = require("file"), objj = require("./objj"), objj_preprocess = objj.objj_preprocess, IS_FILE = objj.IS_FILE, - GET_CODE = objj.GET_CODE, IS_LOCAL = objj.IS_LOCAL, + GET_CODE = objj.GET_CODE, + GET_FILE = objj.GET_FILE, MARKER_IMPORT_STD = objj.MARKER_IMPORT_STD, MARKER_IMPORT_LOCAL = objj.MARKER_IMPORT_LOCAL, MARKER_CODE = objj.MARKER_CODE, diff --git a/Objective-J/utilities.js b/Objective-J/utilities.js index ac2e74386..f4315bf0d 100644 --- a/Objective-J/utilities.js +++ b/Objective-J/utilities.js @@ -168,10 +168,7 @@ var _sprintf_justify = function(sign, prefix, string, suffix, width, leftJustify var _sprintf_pad = function(n, ch) { - var result = ""; - for (var i = 0; i < n; i++) - result += ch; - return result; + return Array(MAX(0,n)+1).join(ch); } // Base64 encoding and decoding diff --git a/Tests/Foundation/CPExceptionTest.j b/Tests/Foundation/CPExceptionTest.j new file mode 100644 index 000000000..6451e32b5 --- /dev/null +++ b/Tests/Foundation/CPExceptionTest.j @@ -0,0 +1,49 @@ +@import + +@implementation CPExceptionTest : OJTestCase +{ +} + +- (void)setUp +{ + exception = [CPException exceptionWithName:@"CPGenericException" + reason:@"Margins must be positive" + userInfo:nil]; +} + +- (void)testInitWithNameReasonUserInfo +{ + var exception = [[CPException alloc] initWithName:@"CPUnsupportedMethodException" + reason:@"setHeaderCell: is not supported. -setHeaderCell:aView instead." + userInfo:nil]; + [self assert:[exception name] equals:"CPUnsupportedMethodException"]; + [self assert:[exception reason] equals:"setHeaderCell: is not supported. -setHeaderCell:aView instead."]; + [self assertThrows:function(){[exception raise]}]; +} + +- (void)testRaiseReason +{ + [self assertThrows:function(){[CPException raise:@"CPGenericException" reason:@"Margins must be positive"];}]; +} + +- (void)testName +{ + [self assert:[exception name] equals:@"CPGenericException"]; +} + +- (void)testReason +{ + [self assert:[exception reason] equals:@"Margins must be positive"]; +} + +- (void)testUserInfo +{ + [self assert:[exception userInfo] equals:null]; +} + +- (void)testRaise +{ + [self assertThrows:function(){[exception raise];}]; +} + +@end diff --git a/Tools/capp/Generate.j b/Tools/capp/Generate.j index d112de349..dc4bfb914 100644 --- a/Tools/capp/Generate.j +++ b/Tools/capp/Generate.j @@ -3,6 +3,9 @@ var File = require("file"); +var FILE = File; +var OBJJ = require("objj/objj"); +var SYSTEM = require("system"); function gen(/*va_args*/) { @@ -58,12 +61,12 @@ function gen(/*va_args*/) config = {}; if (File.isFile(configFile)) config = JSON.parse(File.read(configFile)); - print(config.FrameworksPath) + var destinationProject = new java.io.File(destination), configuration = noConfig ? [Configuration defaultConfiguration] : [Configuration userConfiguration]; if (justFrameworks) - createFrameworksInFile(destinationProject, shouldSymbolicallyLink, force); + createFrameworksInFile(String(destinationProject.getCanonicalPath()), shouldSymbolicallyLink, force); else if (!destinationProject.exists()) { @@ -106,65 +109,60 @@ function gen(/*va_args*/) print("Directory already exists"); } - -function createFrameworksInFile(/*String*/ aFile, /*Boolean*/ shouldSymbolicallyLink, /*Boolean*/ force) +function createFrameworksInFile(/*String*/ aFile, /*Boolean*/ symlink, /*Boolean*/ force) { - if (!File.isDirectory(aFile)) - throw new Error("Can't create Frameworks. Directory does not exist: " + aFile); + var destination = FILE.path(aFile); + + if (!destination.isDirectory()) + throw new Error("Can't create Frameworks. Directory does not exist: " + destination); + + if (symlink && !(SYSTEM.env["CAPP_BUILD"] || SYSTEM.env["STEAM_BUILD"])) + throw "CAPP_BUILD or STEAM_BUILD must be defined"; + + var installedFrameworks = FILE.path(FILE.join(OBJJ.OBJJ_HOME, "lib", "Frameworks")), + builtFrameworks = FILE.path(SYSTEM.env["CAPP_BUILD"] || SYSTEM.env["STEAM_BUILD"]); + + var sourceFrameworks = symlink ? builtFrameworks.join("Release") : installedFrameworks, + sourceDebugFrameworks = symlink ? builtFrameworks.join("Debug") : installedFrameworks.join("Debug"); - var destinationFrameworks = new java.io.File(aFile+ "/Frameworks"), - destinationDebugFrameworks = new java.io.File(aFile + "/Frameworks/Debug"); - - if (destinationFrameworks.exists()) { + var destinationFrameworks = destination.join("Frameworks"), + destinationDebugFrameworks = destination.join("Frameworks", "Debug"); + + print("Creating Frameworks directory in " + destinationFrameworks + "."); + + //destinationFrameworks.mkdirs(); // redundant + destinationDebugFrameworks.mkdirs(); + + ["Objective-J", "Foundation", "AppKit"].forEach(function(framework) { + installFramework( + sourceFrameworks.join(framework), + destinationFrameworks.join(framework), + force, symlink); + installFramework( + sourceDebugFrameworks.join(framework), + destinationDebugFrameworks.join(framework), + force, symlink); + }); +} + +function installFramework(source, dest, force, symlink) { + if (dest.exists()) { if (force) { - print("Updating existing Frameworks directory."); - exec(["rm", "-rf", destinationFrameworks], true); - } - else { - print("Frameworks directory already exists. Use --force to overwrite."); + dest.rmtree(); + } else { + print("Warning: " + dest + " already exists. Use --force to overwrite."); return; } - } else { - print("Creating Frameworks directory in "+destinationFrameworks+"."); } - - if (!shouldSymbolicallyLink) - { - var sourceFrameworks = new java.io.File(OBJJ_HOME + "/lib/Frameworks"); - - exec(["cp", "-R", sourceFrameworks.getCanonicalPath(), destinationFrameworks], true); - - return; + if (source.exists()) { + print((symlink ? "Symlinking " : "Copying ") + source + " to " + dest); + if (symlink) + FILE.symlink(source, dest); + else + FILE.copyTree(source, dest); } - - var BUILD = system.env["CAPP_BUILD"] || system.env["STEAM_BUILD"]; - - if (!BUILD) - throw "CAPP_BUILD or STEAM_BUILD must be defined"; - - // Release Frameworks - new java.io.File(destinationFrameworks).mkdir(); - - exec(["ln", "-s", new java.io.File(BUILD + "/Release/Objective-J").getCanonicalPath(), - new java.io.File(aFile + "/Frameworks/Objective-J").getCanonicalPath()], true); - - exec(["ln", "-s", new java.io.File(BUILD + "/Release/Foundation").getCanonicalPath(), - new java.io.File(aFile + "/Frameworks/Foundation").getCanonicalPath()], true); - - exec(["ln", "-s", new java.io.File(BUILD + "/Release/AppKit").getCanonicalPath(), - new java.io.File(aFile + "/Frameworks/AppKit").getCanonicalPath()], true); - - // Debug Frameworks - new java.io.File(destinationDebugFrameworks).mkdir(); - - exec(["ln", "-s", new java.io.File(BUILD + "/Debug/Objective-J").getCanonicalPath(), - new java.io.File(aFile + "/Frameworks/Debug/Objective-J").getCanonicalPath()], true); - - exec(["ln", "-s", new java.io.File(BUILD + "/Debug/Foundation").getCanonicalPath(), - new java.io.File(aFile + "/Frameworks/Debug/Foundation").getCanonicalPath()], true); - - exec(["ln", "-s", new java.io.File(BUILD + "/Debug/AppKit").getCanonicalPath(), - new java.io.File(aFile + "/Frameworks/Debug/AppKit").getCanonicalPath()], true); + else + print("Warning: "+source+" doesn't exist."); } function toIdentifier(/*String*/ aString) diff --git a/Tools/nib2cib/NSAppKit.j b/Tools/nib2cib/NSAppKit.j index a18c503d8..1852ebf55 100644 --- a/Tools/nib2cib/NSAppKit.j +++ b/Tools/nib2cib/NSAppKit.j @@ -32,6 +32,7 @@ @import "NSCustomObject.j" @import "NSCustomResource.j" @import "NSCustomView.j" +@import "NSEvent.j" @import "NSFont.j" @import "NSIBObjectData.j" @import "NSImageView.j" diff --git a/Tools/nib2cib/NSCell.j b/Tools/nib2cib/NSCell.j index a25a3f25f..604afeadf 100644 --- a/Tools/nib2cib/NSCell.j +++ b/Tools/nib2cib/NSCell.j @@ -27,6 +27,7 @@ @import "NSFont.j" + @implementation NSCell : CPObject { int _state @accessors(readonly, getter=state); diff --git a/Tools/nib2cib/NSEvent.j b/Tools/nib2cib/NSEvent.j new file mode 100644 index 000000000..f669a2ed4 --- /dev/null +++ b/Tools/nib2cib/NSEvent.j @@ -0,0 +1,62 @@ +/* + * NSEvent.j + * nib2cib + * + * Created by Francisco Tolmasky. + * Copyright 2009, 280 North, Inc. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +NSAlphaShiftKeyMask = 1 << 16; +NSShiftKeyMask = 1 << 17; +NSControlKeyMask = 1 << 18; +NSAlternateKeyMask = 1 << 19; +NSCommandKeyMask = 1 << 20; +NSNumericPadKeyMask = 1 << 21; +NSHelpKeyMask = 1 << 22; +NSFunctionKeyMask = 1 << 23; +NSDeviceIndependentModifierFlagsMask = 0xffff0000; + +function CP_NSMapKeyMask(anNSKeyMask) +{ + var keyMask = 0; + + if (anNSKeyMask & NSAlphaShiftKeyMask) + keyMask |= CPAlphaShiftKeyMask; + + if (anNSKeyMask & NSShiftKeyMask) + keyMask |= CPShiftKeyMask; + + if (anNSKeyMask & NSControlKeyMask) + keyMask |= CPControlKeyMask; + + if (anNSKeyMask & NSAlternateKeyMask) + keyMask |= CPAlternateKeyMask; + + if (anNSKeyMask & NSCommandKeyMask) + keyMask |= CPCommandKeyMask; + + if (anNSKeyMask & NSNumericPadKeyMask) + keyMask |= CPNumericPadKeyMask; + + if (anNSKeyMask & NSHelpKeyMask) + keyMask |= CPHelpKeyMask; + + if (anNSKeyMask & NSFunctionKeyMask) + keyMask |= CPFunctionKeyMask; + + return keyMask; +} diff --git a/Tools/nib2cib/NSIBObjectData.j b/Tools/nib2cib/NSIBObjectData.j index 6c7214bbb..26d2e5481 100644 --- a/Tools/nib2cib/NSIBObjectData.j +++ b/Tools/nib2cib/NSIBObjectData.j @@ -22,6 +22,8 @@ @import +@import "NSCell.j" + @implementation _CPCibObjectData (NSCoding) @@ -58,6 +60,8 @@ _objectsKeys = [aCoder decodeObjectForKey:@"NSObjectsKeys"]; _objectsValues = [aCoder decodeObjectForKey:@"NSObjectsValues"]; + [self removeCellsFromObjectGraph]; + //_oidKeys = [aCoder decodeObjectForKey:@"NSOidsKeys"]; //_oidValues = [aCoder decodeObjectForKey:@"NSOidsValues"]; @@ -68,6 +72,68 @@ return self; } +- (void)removeCellsFromObjectGraph +{ + // FIXME: Remove from top level objects and connections? + + // Most cell references should be naturally removed by the fact that we don't manually + // encode them anywhere, however, they remain in our object graph. For each cell found, + // take its children and promote them to our parent object's children. + var count = _objectsKeys.length, + parentForCellUIDs = { }, + promotedChildrenForCellUIDs = { }; + + while (count--) + { + var child = _objectsKeys[count]; + + if (!child) + continue; + + var parent = _objectsValues[count]; + + // If this object is a cell, remember it's parent. + if ([child isKindOfClass:[NSCell class]]) + { + parentForCellUIDs[[child UID]] = parent; + continue; + } + + // If parent also isn't a cell, we don't care about it. + if (![parent isKindOfClass:[NSCell class]]) + continue; + + // Remember this child for later promotion. + var parentUID = [parent UID], + children = promotedChildrenForCellUIDs[parentUID]; + + if (!children) + { + children = []; + promotedChildrenForCellUIDs[parentUID] = children; + } + + children.push(child); + + _objectsKeys.splice(count, 1); + _objectsValues.splice(count, 1); + } + + for (var cellUID in promotedChildrenForCellUIDs) + if (promotedChildrenForCellUIDs.hasOwnProperty(cellUID)) + { + var children = promotedChildrenForCellUIDs[cellUID], + parent = parentForCellUIDs[cellUID]; + + children.forEach(function(aChild) + { + CPLog.warn("Promoted " + aChild + " to child of " + parent); + _objectsKeys.push(aChild); + _objectsValues.push(parent); + }); + } +} + @end @implementation NSIBObjectData : _CPCibObjectData diff --git a/Tools/nib2cib/NSMenuItem.j b/Tools/nib2cib/NSMenuItem.j index 56ed373a2..9630cf35f 100644 --- a/Tools/nib2cib/NSMenuItem.j +++ b/Tools/nib2cib/NSMenuItem.j @@ -22,6 +22,7 @@ @import +@import "NSEvent.j" @import "NSMenu.j" @@ -57,9 +58,9 @@ _submenu = [aCoder decodeObjectForKey:"NSSubmenu"]; _menu = [aCoder decodeObjectForKey:"NSMenu"]; -// _keyEquivalent = [aCoder decodeObjectForKey:"NSKeyEquiv"]; -// _keyEquivalentModifierMask = [aCoder decodeObjectForKey:"NSKeyEquivModMask"]; - + _keyEquivalent = [aCoder decodeObjectForKey:"NSKeyEquiv"]; + _keyEquivalentModifierMask = CP_NSMapKeyMask([aCoder decodeObjectForKey:"NSKeyEquivModMask"]); + // _mnemonicLocation = [aCoder decodeObjectForKey:"NSMnemonicLoc"]; // _isAlternate = [aCoder decodeBoolForKey:"NSIsAlternate"]; diff --git a/Tools/nib2cib/NSViewController.j b/Tools/nib2cib/NSViewController.j index 18fcc5527..a0e522a7a 100644 --- a/Tools/nib2cib/NSViewController.j +++ b/Tools/nib2cib/NSViewController.j @@ -27,14 +27,21 @@ - (id)NS_initWithCoder:(CPCoder)aCoder { - return [super NS_initWithCoder:aCoder]; + self = [super NS_initWithCoder:aCoder]; + + if (self) + { + _title = [aCoder decodeObjectForKey:@"NSTitle"]; + _cibName = [aCoder decodeObjectForKey:@"NSNibName"]; + _cibBundle = [CPBundle bundleWithPath:[aCoder decodeObjectForKey:@"NSNibBundleIdentifier"]]; + } + + return self; } @end @implementation NSViewController : CPViewController -{ -} - (id)initWithCoder:(CPCoder)aCoder { @@ -43,7 +50,7 @@ - (Class)classForKeyedArchiver { - return [CPCollectionView class]; + return [CPViewController class]; } @end