![]() |
API 0.9.5
|
00001 /* 00002 * CPApplication.j 00003 * AppKit 00004 * 00005 * Created by Francisco Tolmasky. 00006 * Copyright 2008, 280 North, Inc. 00007 * 00008 * This library is free software; you can redistribute it and/or 00009 * modify it under the terms of the GNU Lesser General Public 00010 * License as published by the Free Software Foundation; either 00011 * version 2.1 of the License, or (at your option) any later version. 00012 * 00013 * This library is distributed in the hope that it will be useful, 00014 * but WITHOUT ANY WARRANTY; without even the implied warranty of 00015 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 00016 * Lesser General Public License for more details. 00017 * 00018 * You should have received a copy of the GNU Lesser General Public 00019 * License along with this library; if not, write to the Free Software 00020 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA 00021 */ 00022 00023 00024 00025 00026 var CPMainCibFile = @"CPMainCibFile", 00027 CPMainCibFileHumanFriendly = @"Main cib file base name"; 00028 00029 CPApp = nil; 00030 00031 CPApplicationWillFinishLaunchingNotification = @"CPApplicationWillFinishLaunchingNotification"; 00032 CPApplicationDidFinishLaunchingNotification = @"CPApplicationDidFinishLaunchingNotification"; 00033 CPApplicationWillTerminateNotification = @"CPApplicationWillTerminateNotification"; 00034 CPApplicationWillBecomeActiveNotification = @"CPApplicationWillBecomeActiveNotification"; 00035 CPApplicationDidBecomeActiveNotification = @"CPApplicationDidBecomeActiveNotification"; 00036 CPApplicationWillResignActiveNotification = @"CPApplicationWillResignActiveNotification"; 00037 CPApplicationDidResignActiveNotification = @"CPApplicationDidResignActiveNotification"; 00038 00039 CPTerminateNow = YES; 00040 CPTerminateCancel = NO; 00041 CPTerminateLater = -1; // not currently supported 00042 00043 CPRunStoppedResponse = -1000; 00044 CPRunAbortedResponse = -1001; 00045 CPRunContinuesResponse = -1002; 00046 00072 @implementation CPApplication : CPResponder 00073 { 00074 CPArray _eventListeners; 00075 00076 CPEvent _currentEvent; 00077 00078 CPArray _windows; 00079 CPWindow _keyWindow; 00080 CPWindow _mainWindow; 00081 CPWindow _previousKeyWindow; 00082 CPWindow _previousMainWindow; 00083 00084 CPDocumentController _documentController; 00085 00086 CPModalSession _currentSession; 00087 00088 // 00089 id _delegate; 00090 BOOL _finishedLaunching; 00091 BOOL _isActive; 00092 00093 CPDictionary _namedArgs; 00094 CPArray _args; 00095 CPString _fullArgsString; 00096 00097 CPImage _applicationIconImage; 00098 00099 CPPanel _aboutPanel; 00100 00101 CPThemeBlend _themeBlend; 00102 } 00103 00109 + (CPApplication)sharedApplication 00110 { 00111 if (!CPApp) 00112 CPApp = [[CPApplication alloc] init]; 00113 00114 return CPApp; 00115 } 00116 00122 - (id)init 00123 { 00124 self = [super init]; 00125 00126 CPApp = self; 00127 00128 if (self) 00129 { 00130 _eventListeners = []; 00131 00132 _windows = []; 00133 00134 [_windows addObject:nil]; 00135 } 00136 00137 return self; 00138 } 00139 00140 // Configuring Applications 00141 00148 - (void)setDelegate:(id)aDelegate 00149 { 00150 if (_delegate == aDelegate) 00151 return; 00152 00153 var defaultCenter = [CPNotificationCenter defaultCenter], 00154 delegateNotifications = 00155 [ 00156 CPApplicationWillFinishLaunchingNotification, @selector(applicationWillFinishLaunching:), 00157 CPApplicationDidFinishLaunchingNotification, @selector(applicationDidFinishLaunching:), 00158 CPApplicationWillBecomeActiveNotification, @selector(applicationWillBecomeActive:), 00159 CPApplicationDidBecomeActiveNotification, @selector(applicationDidBecomeActive:), 00160 CPApplicationWillResignActiveNotification, @selector(applicationWillResignActive:), 00161 CPApplicationDidResignActiveNotification, @selector(applicationDidResignActive:), 00162 CPApplicationWillTerminateNotification, @selector(applicationWillTerminate:) 00163 ], 00164 count = [delegateNotifications count]; 00165 00166 if (_delegate) 00167 { 00168 var index = 0; 00169 00170 for (; index < count; index += 2) 00171 { 00172 var notificationName = delegateNotifications[index], 00173 selector = delegateNotifications[index + 1]; 00174 00175 if ([_delegate respondsToSelector:selector]) 00176 [defaultCenter removeObserver:_delegate name:notificationName object:self]; 00177 } 00178 } 00179 00180 _delegate = aDelegate; 00181 00182 var index = 0; 00183 00184 for (; index < count; index += 2) 00185 { 00186 var notificationName = delegateNotifications[index], 00187 selector = delegateNotifications[index + 1]; 00188 00189 if ([_delegate respondsToSelector:selector]) 00190 [defaultCenter addObserver:_delegate selector:selector name:notificationName object:self]; 00191 } 00192 } 00193 00197 - (id)delegate 00198 { 00199 return _delegate; 00200 } 00201 00208 - (void)finishLaunching 00209 { 00210 // At this point we clear the window.status to eliminate Safari's "Cancelled" error message 00211 // The message shouldn't be displayed, because only an XHR is cancelled, but it is a usability issue. 00212 // We do it here so that applications can change it in willFinish or didFinishLaunching 00213 window.status = " "; 00214 00215 // We also want to set the default cursor on the body, so that buttons and things don't have an iBeam 00216 [[CPCursor arrowCursor] set]; 00217 00218 var bundle = [CPBundle mainBundle], 00219 types = [bundle objectForInfoDictionaryKey:@"CPBundleDocumentTypes"]; 00220 00221 if ([types count] > 0) 00222 _documentController = [CPDocumentController sharedDocumentController]; 00223 00224 var delegateClassName = [bundle objectForInfoDictionaryKey:@"CPApplicationDelegateClass"]; 00225 00226 if (delegateClassName) 00227 { 00228 var delegateClass = objj_getClass(delegateClassName); 00229 00230 if (delegateClass) 00231 if ([_documentController class] == delegateClass) 00232 [self setDelegate:_documentController]; 00233 else 00234 [self setDelegate:[[delegateClass alloc] init]]; 00235 } 00236 00237 var defaultCenter = [CPNotificationCenter defaultCenter]; 00238 00239 [defaultCenter 00240 postNotificationName:CPApplicationWillFinishLaunchingNotification 00241 object:self]; 00242 00243 var needsUntitled = !!_documentController, 00244 URLStrings = window.cpOpeningURLStrings && window.cpOpeningURLStrings(), 00245 index = 0, 00246 count = [URLStrings count]; 00247 00248 for (; index < count; ++index) 00249 needsUntitled = ![self _openURL:[CPURL URLWithString:URLStrings[index]]] || needsUntitled; 00250 00251 if (needsUntitled && [_delegate respondsToSelector:@selector(applicationShouldOpenUntitledFile:)]) 00252 needsUntitled = [_delegate applicationShouldOpenUntitledFile:self]; 00253 00254 if (needsUntitled) 00255 [_documentController newDocument:self]; 00256 00257 [_documentController _updateRecentDocumentsMenu]; 00258 00259 [defaultCenter 00260 postNotificationName:CPApplicationDidFinishLaunchingNotification 00261 object:self]; 00262 00263 [[CPRunLoop currentRunLoop] limitDateForMode:CPDefaultRunLoopMode]; 00264 00265 _finishedLaunching = YES; 00266 } 00267 00268 - (void)terminate:(id)aSender 00269 { 00270 [[CPNotificationCenter defaultCenter] 00271 postNotificationName:CPApplicationWillTerminateNotification 00272 object:self]; 00273 00274 if (![CPPlatform isBrowser]) 00275 { 00276 [[CPDocumentController sharedDocumentController] closeAllDocumentsWithDelegate:self 00277 didCloseAllSelector:@selector(_documentController:didCloseAll:context:) 00278 contextInfo:nil]; 00279 } 00280 else 00281 { 00282 [[[self keyWindow] platformWindow] _propagateCurrentDOMEvent:YES]; 00283 } 00284 } 00285 00292 - (void)setApplicationIconImage:(CPImage)anImage 00293 { 00294 _applicationIconImage = anImage; 00295 } 00296 00301 - (CPImage)applicationIconImage 00302 { 00303 if (_applicationIconImage) 00304 return _applicationIconImage; 00305 00306 var imagePath = [[CPBundle mainBundle] objectForInfoDictionaryKey:@"CPApplicationIcon"]; 00307 if (imagePath) 00308 _applicationIconImage = [[CPImage alloc] initWithContentsOfFile:imagePath]; 00309 00310 return _applicationIconImage; 00311 } 00312 00316 - (void)orderFrontStandardAboutPanel:(id)sender 00317 { 00318 [self orderFrontStandardAboutPanelWithOptions:nil]; 00319 } 00320 00345 - (void)orderFrontStandardAboutPanelWithOptions:(CPDictionary)options 00346 { 00347 if (!_aboutPanel) 00348 { 00349 var mainInfo = [[CPBundle mainBundle] infoDictionary], 00350 applicationTitle = [options objectForKey:"ApplicationName"] || [mainInfo objectForKey:@"CPBundleName"], 00351 applicationIcon = [options objectForKey:@"ApplicationIcon"] || [self applicationIconImage], 00352 version = [options objectForKey:@"Version"] || [mainInfo objectForKey:@"CPBundleVersion"], 00353 applicationVersion = [options objectForKey:@"ApplicationVersion"] || [mainInfo objectForKey:@"CPBundleShortVersionString"], 00354 copyright = [options objectForKey:@"Copyright"] || [mainInfo objectForKey:@"CPHumanReadableCopyright"]; 00355 00356 var aboutPanelPath = [[CPBundle bundleForClass:[CPWindowController class]] pathForResource:@"AboutPanel.cib"], 00357 aboutPanelController = [CPWindowController alloc], 00358 aboutPanelController = [aboutPanelController initWithWindowCibPath:aboutPanelPath owner:aboutPanelController], 00359 aboutPanel = [aboutPanelController window], 00360 contentView = [aboutPanel contentView], 00361 imageView = [contentView viewWithTag:1], 00362 applicationLabel = [contentView viewWithTag:2], 00363 versionLabel = [contentView viewWithTag:3], 00364 copyrightLabel = [contentView viewWithTag:4], 00365 standardPath = [[CPBundle bundleForClass:[self class]] pathForResource:@"standardApplicationIcon.png"]; 00366 00367 // FIXME move this into the CIB eventually 00368 [applicationLabel setFont:[CPFont boldSystemFontOfSize:14.0]]; 00369 [applicationLabel setAlignment:CPCenterTextAlignment]; 00370 [versionLabel setAlignment:CPCenterTextAlignment]; 00371 [copyrightLabel setAlignment:CPCenterTextAlignment]; 00372 00373 [imageView setImage:applicationIcon || [[CPImage alloc] initWithContentsOfFile:standardPath 00374 size:CGSizeMake(256, 256)]]; 00375 00376 [applicationLabel setStringValue:applicationTitle || ""]; 00377 00378 if (applicationVersion && version) 00379 [versionLabel setStringValue:@"Version " + applicationVersion + " (" + version + ")"]; 00380 else if (applicationVersion || version) 00381 [versionLabel setStringValue:@"Version " + (applicationVersion || version)]; 00382 else 00383 [versionLabel setStringValue:@""]; 00384 00385 [copyrightLabel setStringValue:copyright || ""]; 00386 [aboutPanel center]; 00387 00388 _aboutPanel = aboutPanel; 00389 } 00390 00391 [_aboutPanel orderFront:self]; 00392 } 00393 00394 00395 - (void)_documentController:(NSDocumentController *)docController didCloseAll:(BOOL)didCloseAll context:(Object)info 00396 { 00397 // callback method for terminate: 00398 if (didCloseAll) 00399 { 00400 if ([_delegate respondsToSelector:@selector(applicationShouldTerminate:)]) 00401 [self replyToApplicationShouldTerminate:[_delegate applicationShouldTerminate:self]]; 00402 else 00403 [self replyToApplicationShouldTerminate:YES]; 00404 } 00405 } 00406 00407 - (void)replyToApplicationShouldTerminate:(BOOL)terminate 00408 { 00409 if (terminate == CPTerminateNow) 00410 { 00411 [[CPNotificationCenter defaultCenter] postNotificationName:CPApplicationWillTerminateNotification object:self]; 00412 [CPPlatform terminateApplication]; 00413 } 00414 } 00415 00416 - (void)activateIgnoringOtherApps:(BOOL)shouldIgnoreOtherApps 00417 { 00418 [self _willBecomeActive]; 00419 00420 [CPPlatform activateIgnoringOtherApps:shouldIgnoreOtherApps]; 00421 _isActive = YES; 00422 00423 [self _willResignActive]; 00424 } 00425 00426 - (void)deactivate 00427 { 00428 [self _willResignActive]; 00429 00430 [CPPlatform deactivate]; 00431 _isActive = NO; 00432 00433 [self _didResignActive]; 00434 } 00435 00436 - (void)isActive 00437 { 00438 return _isActive; 00439 } 00440 00441 - (void)hideOtherApplications:(id)aSender 00442 { 00443 [CPPlatform hideOtherApplications:self]; 00444 } 00445 00450 - (void)run 00451 { 00452 [self finishLaunching]; 00453 } 00454 00455 // Managing the Event Loop 00460 - (void)runModalForWindow:(CPWindow)aWindow 00461 { 00462 [self runModalSession:[self beginModalSessionForWindow:aWindow]]; 00463 } 00464 00470 - (void)stopModalWithCode:(int)aCode 00471 { 00472 if (!_currentSession) 00473 { 00474 return; 00475 // raise exception; 00476 } 00477 00478 _currentSession._state = aCode; 00479 _currentSession = _currentSession._previous; 00480 00481 // if (aCode == CPRunAbortedResponse) 00482 [self _removeRunModalLoop]; 00483 } 00484 00485 /* @ignore */ 00486 - (void)_removeRunModalLoop 00487 { 00488 var count = _eventListeners.length; 00489 00490 while (count--) 00491 if (_eventListeners[count]._callback === _CPRunModalLoop) 00492 { 00493 _eventListeners.splice(count, 1); 00494 00495 return; 00496 } 00497 } 00498 00502 - (void)stopModal 00503 { 00504 [self stopModalWithCode:CPRunStoppedResponse] 00505 } 00506 00510 - (void)abortModal 00511 { 00512 [self stopModalWithCode:CPRunAbortedResponse]; 00513 } 00514 00519 - (CPModalSession)beginModalSessionForWindow:(CPWindow)aWindow 00520 { 00521 return _CPModalSessionMake(aWindow, 0); 00522 } 00523 00528 - (void)runModalSession:(CPModalSession)aModalSession 00529 { 00530 aModalSession._previous = _currentSession; 00531 _currentSession = aModalSession; 00532 00533 var theWindow = aModalSession._window; 00534 00535 [theWindow center]; 00536 [theWindow makeKeyWindow]; 00537 [theWindow orderFront:self]; 00538 00539 // [theWindow._bridge _obscureWindowsBelowModalWindow]; 00540 00541 [CPApp setCallback:_CPRunModalLoop forNextEventMatchingMask:CPAnyEventMask untilDate:nil inMode:0 dequeue:NO]; 00542 } 00543 00548 - (CPWindow)modalWindow 00549 { 00550 if (!_currentSession) 00551 return nil; 00552 00553 return _currentSession._window; 00554 } 00555 00556 /* @ignore */ 00557 - (BOOL)_handleKeyEquivalent:(CPEvent)anEvent 00558 { 00559 return [[self keyWindow] performKeyEquivalent:anEvent] || 00560 [[self mainMenu] performKeyEquivalent:anEvent]; 00561 } 00562 00567 - (void)sendEvent:(CPEvent)anEvent 00568 { 00569 _currentEvent = anEvent; 00570 00571 var willPropagate = [[[anEvent window] platformWindow] _willPropagateCurrentDOMEvent]; 00572 00573 // temporarily pretend we won't propagate the event. we'll restore the saved value later 00574 // we do this outside the if so that changes user code might make in _handleKeyEquiv. are preserved 00575 [[[anEvent window] platformWindow] _propagateCurrentDOMEvent:NO]; 00576 00577 // Check if this is a candidate for key equivalent... 00578 if ([anEvent _couldBeKeyEquivalent] && [self _handleKeyEquivalent:anEvent]) 00579 { 00580 var characters = [anEvent characters], 00581 modifierFlags = [anEvent modifierFlags]; 00582 00583 // Unconditionally propagate on these keys to solve browser copy paste bugs 00584 if ((characters == "c" || characters == "x" || characters == "v") && (modifierFlags & CPPlatformActionKeyMask)) 00585 [[[anEvent window] platformWindow] _propagateCurrentDOMEvent:YES]; 00586 00587 return; 00588 } 00589 00590 // if we make it this far, then restore the original willPropagate value 00591 [[[anEvent window] platformWindow] _propagateCurrentDOMEvent:willPropagate]; 00592 00593 if (_eventListeners.length) 00594 { 00595 if (_eventListeners[_eventListeners.length - 1]._mask & (1 << [anEvent type])) 00596 _eventListeners.pop()._callback(anEvent); 00597 00598 return; 00599 } 00600 00601 [[anEvent window] sendEvent:anEvent]; 00602 } 00603 00608 - (void)doCommandBySelector:(SEL)aSelector 00609 { 00610 if ([_delegate respondsToSelector:aSelector]) 00611 [_delegate performSelector:aSelector]; 00612 else 00613 [super doCommandBySelector:aSelector]; 00614 } 00615 00619 - (CPWindow)keyWindow 00620 { 00621 return _keyWindow; 00622 } 00623 00627 - (CPWindow)mainWindow 00628 { 00629 return _mainWindow; 00630 } 00631 00635 - (CPWindow)windowWithWindowNumber:(int)aWindowNumber 00636 { 00637 return _windows[aWindowNumber]; 00638 } 00639 00643 - (CPArray)windows 00644 { 00645 return _windows; 00646 } 00647 00651 - (CPArray)orderedWindows 00652 { 00653 #if PLATFORM(DOM) 00654 return CPWindowObjectList(); 00655 #else 00656 return []; 00657 #endif 00658 } 00659 00660 - (void)hide:(id)aSender 00661 { 00662 [CPPlatform hide:self]; 00663 } 00664 00665 // Accessing the Main Menu 00669 - (CPMenu)mainMenu 00670 { 00671 return [self menu]; 00672 } 00673 00678 - (void)setMainMenu:(CPMenu)aMenu 00679 { 00680 [self setMenu:aMenu]; 00681 } 00682 00683 - (void)setMenu:(CPMenu)aMenu 00684 { 00685 if ([aMenu _menuName] === "CPMainMenu") 00686 { 00687 if ([self menu] === aMenu) 00688 return; 00689 00690 [super setMenu:aMenu]; 00691 00692 if ([CPPlatform supportsNativeMainMenu]) 00693 window.cpSetMainMenu([self menu]); 00694 } 00695 else 00696 [aMenu _setMenuName:@"CPMainMenu"]; 00697 } 00698 00703 - (void)orderFrontColorPanel:(id)aSender 00704 { 00705 [[CPColorPanel sharedColorPanel] orderFront:self]; 00706 } 00707 00708 // Posting Actions 00718 - (BOOL)tryToPerform:(SEL)anAction with:(id)anObject 00719 { 00720 if (!anAction) 00721 return NO; 00722 00723 if ([super tryToPerform:anAction with:anObject]) 00724 return YES; 00725 00726 if ([_delegate respondsToSelector:anAction]) 00727 { 00728 [_delegate performSelector:anAction withObject:anObject]; 00729 00730 return YES; 00731 } 00732 00733 return NO; 00734 } 00735 00743 - (BOOL)sendAction:(SEL)anAction to:(id)aTarget from:(id)aSender 00744 { 00745 var target = [self targetForAction:anAction to:aTarget from:aSender]; 00746 00747 if (!target) 00748 return NO; 00749 00750 [target performSelector:anAction withObject:aSender]; 00751 00752 return YES; 00753 } 00754 00766 - (id)targetForAction:(SEL)anAction to:(id)aTarget from:(id)aSender 00767 { 00768 if (!anAction) 00769 return nil; 00770 00771 if (aTarget) 00772 return aTarget; 00773 00774 return [self targetForAction:anAction]; 00775 } 00776 00794 - (id)_targetForWindow:(CPWindow)aWindow action:(SEL)anAction 00795 { 00796 var responder = [aWindow firstResponder], 00797 checkWindow = YES; 00798 00799 while (responder) 00800 { 00801 if ([responder respondsToSelector:anAction]) 00802 return responder; 00803 00804 if (responder == aWindow) 00805 checkWindow = NO; 00806 00807 responder = [responder nextResponder]; 00808 } 00809 00810 if (checkWindow && [aWindow respondsToSelector:anAction]) 00811 return aWindow; 00812 00813 var delegate = [aWindow delegate]; 00814 00815 if ([delegate respondsToSelector:anAction]) 00816 return delegate; 00817 00818 var windowController = [aWindow windowController]; 00819 00820 if ([windowController respondsToSelector:anAction]) 00821 return windowController; 00822 00823 var theDocument = [windowController document]; 00824 if (theDocument !== delegate && [theDocument respondsToSelector:anAction]) 00825 return theDocument; 00826 00827 return nil; 00828 } 00829 00844 - (id)targetForAction:(SEL)anAction 00845 { 00846 if (!anAction) 00847 return nil; 00848 00849 var target = [self _targetForWindow:[self keyWindow] action:anAction]; 00850 00851 if (target) 00852 return target; 00853 00854 target = [self _targetForWindow:[self mainWindow] action:anAction]; 00855 00856 if (target) 00857 return target; 00858 00859 if ([self respondsToSelector:anAction]) 00860 return self; 00861 00862 if ([_delegate respondsToSelector:anAction]) 00863 return _delegate; 00864 00865 if ([_documentController respondsToSelector:anAction]) 00866 return _documentController; 00867 00868 return nil; 00869 } 00870 00879 - (void)setCallback:(Function)aCallback forNextEventMatchingMask:(unsigned int)aMask untilDate:(CPDate)anExpiration inMode:(CPString)aMode dequeue:(BOOL)shouldDequeue 00880 { 00881 _eventListeners.push(_CPEventListenerMake(aMask, aCallback)); 00882 } 00883 00895 - (void)setTarget:(id)aTarget selector:(SEL)aSelector forNextEventMatchingMask:(unsigned int)aMask untilDate:(CPDate)anExpiration inMode:(CPString)aMode dequeue:(BOOL)shouldDequeue 00896 { 00897 _eventListeners.push(_CPEventListenerMake(aMask, function (anEvent) { objj_msgSend(aTarget, aSelector, anEvent); })); 00898 } 00899 00903 - (CPEvent)currentEvent 00904 { 00905 return _currentEvent; 00906 } 00907 00908 // Managing Sheets 00909 00918 - (void)beginSheet:(CPWindow)aSheet modalForWindow:(CPWindow)aWindow modalDelegate:(id)aModalDelegate didEndSelector:(SEL)aDidEndSelector contextInfo:(id)aContextInfo 00919 { 00920 var styleMask = [aSheet styleMask]; 00921 if (!(styleMask & CPDocModalWindowMask)) 00922 { 00923 [CPException raise:CPInternalInconsistencyException reason:@"Currently only CPDocModalWindowMask style mask is supported for attached sheets"]; 00924 return; 00925 } 00926 00927 [aWindow orderFront:self]; 00928 [aSheet setPlatformWindow:[aWindow platformWindow]]; 00929 [aWindow _attachSheet:aSheet modalDelegate:aModalDelegate didEndSelector:aDidEndSelector contextInfo:aContextInfo]; 00930 } 00931 00945 - (void)endSheet:(CPWindow)sheet returnCode:(int)returnCode 00946 { 00947 var count = [_windows count]; 00948 00949 while (--count >= 0) 00950 { 00951 var aWindow = [_windows objectAtIndex:count], 00952 context = aWindow._sheetContext; 00953 00954 if (context != nil && context["sheet"] === sheet) 00955 { 00956 context["returnCode"] = returnCode; 00957 [aWindow _detachSheetWindow]; 00958 return; 00959 } 00960 } 00961 } 00962 00967 - (void)endSheet:(CPWindow)sheet 00968 { 00969 // FIX ME: this is wrong: by Cocoa this should be: CPRunStoppedResponse. 00970 [self endSheet:sheet returnCode:0]; 00971 } 00972 00988 - (CPArray)arguments 00989 { 00990 if (_fullArgsString !== window.location.hash) 00991 [self _reloadArguments]; 00992 00993 return _args; 00994 } 00995 01012 - (void)setArguments:(CPArray)args 01013 { 01014 if (!args || args.length == 0) 01015 { 01016 _args = []; 01017 window.location.hash = @"#"; 01018 01019 return; 01020 } 01021 01022 if (![args isKindOfClass:CPArray]) 01023 args = [CPArray arrayWithObject:args]; 01024 01025 _args = args; 01026 01027 var toEncode = [_args copy]; 01028 for (var i = 0, count = toEncode.length; i < count; i++) 01029 toEncode[i] = encodeURIComponent(toEncode[i]); 01030 01031 var hash = [toEncode componentsJoinedByString:@"/"]; 01032 01033 window.location.hash = @"#" + hash; 01034 } 01035 01036 - (void)_reloadArguments 01037 { 01038 _fullArgsString = window.location.hash; 01039 01040 if (_fullArgsString.length) 01041 { 01042 var args = _fullArgsString.substring(1).split("/"); 01043 01044 for (var i = 0, count = args.length; i < count; i++) 01045 args[i] = decodeURIComponent(args[i]); 01046 01047 _args = args; 01048 } 01049 else 01050 _args = []; 01051 } 01052 01070 - (CPDictionary)namedArguments 01071 { 01072 return _namedArgs; 01073 } 01074 01075 - (BOOL)_openURL:(CPURL)aURL 01076 { 01077 if (_delegate && [_delegate respondsToSelector:@selector(application:openFile:)]) 01078 { 01079 CPLog.warn("application:openFile: is deprecated, use application:openURL: instead."); 01080 return [_delegate application:self openFile:[aURL absoluteString]]; 01081 } 01082 01083 if (_delegate && [_delegate respondsToSelector:@selector(application:openURL:)]) 01084 return [_delegate application:self openURL:aURL]; 01085 01086 return !![_documentController openDocumentWithContentsOfURL:aURL display:YES error:NULL]; 01087 } 01088 01089 - (void)_willBecomeActive 01090 { 01091 [[CPNotificationCenter defaultCenter] postNotificationName:CPApplicationWillBecomeActiveNotification 01092 object:self 01093 userInfo:nil]; 01094 } 01095 01096 - (void)_didBecomeActive 01097 { 01098 if (![self keyWindow] && _previousKeyWindow && 01099 [[self windows] indexOfObjectIdenticalTo:_previousKeyWindow] !== CPNotFound) 01100 [_previousKeyWindow makeKeyWindow]; 01101 01102 if (![self mainWindow] && _previousMainWindow && 01103 [[self windows] indexOfObjectIdenticalTo:_previousMainWindow] !== CPNotFound) 01104 [_previousMainWindow makeMainWindow]; 01105 01106 if ([self keyWindow]) 01107 [[self keyWindow] orderFront:self]; 01108 else if ([self mainWindow]) 01109 [[self mainWindow] makeKeyAndOrderFront:self]; 01110 else 01111 [[self mainMenu]._menuWindow makeKeyWindow]; //FIXME this may not actually work 01112 01113 _previousKeyWindow = nil; 01114 _previousMainWindow = nil; 01115 01116 [[CPNotificationCenter defaultCenter] postNotificationName:CPApplicationDidBecomeActiveNotification 01117 object:self 01118 userInfo:nil]; 01119 } 01120 01121 - (void)_willResignActive 01122 { 01123 [[CPNotificationCenter defaultCenter] postNotificationName:CPApplicationWillResignActiveNotification 01124 object:self 01125 userInfo:nil]; 01126 } 01127 01128 - (void)_didResignActive 01129 { 01130 if (self._activeMenu) 01131 [self._activeMenu cancelTracking]; 01132 01133 if ([self keyWindow]) 01134 { 01135 _previousKeyWindow = [self keyWindow]; 01136 [_previousKeyWindow resignKeyWindow]; 01137 } 01138 01139 if ([self mainWindow]) 01140 { 01141 _previousMainWindow = [self mainWindow]; 01142 [_previousMainWindow resignMainWindow]; 01143 } 01144 01145 [[CPNotificationCenter defaultCenter] postNotificationName:CPApplicationDidResignActiveNotification 01146 object:self 01147 userInfo:nil]; 01148 } 01149 01150 + (CPString)defaultThemeName 01151 { 01152 return ([[CPBundle mainBundle] objectForInfoDictionaryKey:"CPDefaultTheme"] || @"Aristo"); 01153 } 01154 01155 @end 01156 01157 var _CPModalSessionMake = function(aWindow, aStopCode) 01158 { 01159 return { _window:aWindow, _state:CPRunContinuesResponse , _previous:nil }; 01160 } 01161 01162 var _CPEventListenerMake = function(anEventMask, aCallback) 01163 { 01164 return { _mask:anEventMask, _callback:aCallback }; 01165 } 01166 01167 // Make this a global for use in CPPlatformWindow+DOM.j. 01168 _CPRunModalLoop = function(anEvent) 01169 { 01170 [CPApp setCallback:_CPRunModalLoop forNextEventMatchingMask:CPAnyEventMask untilDate:nil inMode:0 dequeue:NO]; 01171 01172 var theWindow = [anEvent window], 01173 modalSession = CPApp._currentSession; 01174 01175 if (theWindow == modalSession._window || [theWindow worksWhenModal]) 01176 [theWindow sendEvent:anEvent]; 01177 } 01178 01186 function CPApplicationMain(args, namedArgs) 01187 { 01188 01189 #if PLATFORM(DOM) 01190 // hook to allow recorder, etc to manipulate things before starting AppKit 01191 if (window.parent !== window && typeof window.parent._childAppIsStarting === "function") 01192 window.parent._childAppIsStarting(window); 01193 #endif 01194 01195 var mainBundle = [CPBundle mainBundle], 01196 principalClass = [mainBundle principalClass]; 01197 01198 if (!principalClass) 01199 principalClass = [CPApplication class]; 01200 01201 [principalClass sharedApplication]; 01202 01203 if ([args containsObject:"debug"]) 01204 CPLogRegister(CPLogPopup); 01205 01206 CPApp._args = args; 01207 CPApp._namedArgs = namedArgs; 01208 01209 [_CPAppBootstrapper performActions]; 01210 } 01211 01212 var _CPAppBootstrapperActions = nil; 01213 @implementation _CPAppBootstrapper : CPObject 01214 { 01215 id __doxygen__; 01216 } 01217 01218 + (CPArray)actions 01219 { 01220 return [@selector(bootstrapPlatform), @selector(loadDefaultTheme), @selector(loadMainCibFile)]; 01221 } 01222 01223 + (void)performActions 01224 { 01225 if (!_CPAppBootstrapperActions) 01226 _CPAppBootstrapperActions = [self actions]; 01227 01228 while (_CPAppBootstrapperActions.length) 01229 { 01230 var action = _CPAppBootstrapperActions.shift(); 01231 01232 if (objj_msgSend(self, action)) 01233 return; 01234 } 01235 01236 [CPApp run]; 01237 } 01238 01239 + (BOOL)bootstrapPlatform 01240 { 01241 return [CPPlatform bootstrap]; 01242 } 01243 01244 + (BOOL)loadDefaultTheme 01245 { 01246 var defaultThemeName = [CPApplication defaultThemeName], 01247 themeURL = nil; 01248 01249 if (defaultThemeName === @"Aristo") 01250 themeURL = [[CPBundle bundleForClass:[CPApplication class]] pathForResource:defaultThemeName + @".blend"]; 01251 else 01252 themeURL = [[CPBundle mainBundle] pathForResource:defaultThemeName + @".blend"]; 01253 01254 var blend = [[CPThemeBlend alloc] initWithContentsOfURL:themeURL]; 01255 [blend loadWithDelegate:self]; 01256 01257 return YES; 01258 } 01259 01260 + (void)blendDidFinishLoading:(CPThemeBlend)aThemeBlend 01261 { 01262 [[CPApplication sharedApplication] setThemeBlend:aThemeBlend]; 01263 [CPTheme setDefaultTheme:[CPTheme themeNamed:[CPApplication defaultThemeName]]]; 01264 01265 [self performActions]; 01266 } 01267 01268 + (BOOL)loadMainCibFile 01269 { 01270 var mainBundle = [CPBundle mainBundle], 01271 mainCibFile = [mainBundle objectForInfoDictionaryKey:CPMainCibFile] || [mainBundle objectForInfoDictionaryKey:CPMainCibFileHumanFriendly]; 01272 01273 if (mainCibFile) 01274 { 01275 [mainBundle loadCibFile:mainCibFile 01276 externalNameTable:[CPDictionary dictionaryWithObject:CPApp forKey:CPCibOwner] 01277 loadDelegate:self]; 01278 01279 return YES; 01280 } 01281 else 01282 [self loadCiblessBrowserMainMenu]; 01283 01284 return NO; 01285 } 01286 01287 + (void)loadCiblessBrowserMainMenu 01288 { 01289 var mainMenu = [[CPMenu alloc] initWithTitle:@"MainMenu"]; 01290 01291 // FIXME: We should implement autoenabling. 01292 [mainMenu setAutoenablesItems:NO]; 01293 01294 var bundle = [CPBundle bundleForClass:[CPApplication class]], 01295 newMenuItem = [[CPMenuItem alloc] initWithTitle:@"New" action:@selector(newDocument:) keyEquivalent:@"n"]; 01296 01297 [newMenuItem setImage:[[CPImage alloc] initWithContentsOfFile:[bundle pathForResource:@"CPApplication/New.png"] size:CGSizeMake(16.0, 16.0)]]; 01298 [newMenuItem setAlternateImage:[[CPImage alloc] initWithContentsOfFile:[bundle pathForResource:@"CPApplication/NewHighlighted.png"] size:CGSizeMake(16.0, 16.0)]]; 01299 01300 [mainMenu addItem:newMenuItem]; 01301 01302 var openMenuItem = [[CPMenuItem alloc] initWithTitle:@"Open" action:@selector(openDocument:) keyEquivalent:@"o"]; 01303 01304 [openMenuItem setImage:[[CPImage alloc] initWithContentsOfFile:[bundle pathForResource:@"CPApplication/Open.png"] size:CGSizeMake(16.0, 16.0)]]; 01305 [openMenuItem setAlternateImage:[[CPImage alloc] initWithContentsOfFile:[bundle pathForResource:@"CPApplication/OpenHighlighted.png"] size:CGSizeMake(16.0, 16.0)]]; 01306 01307 [mainMenu addItem:openMenuItem]; 01308 01309 var saveMenu = [[CPMenu alloc] initWithTitle:@"Save"], 01310 saveMenuItem = [[CPMenuItem alloc] initWithTitle:@"Save" action:@selector(saveDocument:) keyEquivalent:nil]; 01311 01312 [saveMenuItem setImage:[[CPImage alloc] initWithContentsOfFile:[bundle pathForResource:@"CPApplication/Save.png"] size:CGSizeMake(16.0, 16.0)]]; 01313 [saveMenuItem setAlternateImage:[[CPImage alloc] initWithContentsOfFile:[bundle pathForResource:@"CPApplication/SaveHighlighted.png"] size:CGSizeMake(16.0, 16.0)]]; 01314 01315 [saveMenu addItem:[[CPMenuItem alloc] initWithTitle:@"Save" action:@selector(saveDocument:) keyEquivalent:@"s"]]; 01316 [saveMenu addItem:[[CPMenuItem alloc] initWithTitle:@"Save As" action:@selector(saveDocumentAs:) keyEquivalent:nil]]; 01317 01318 [saveMenuItem setSubmenu:saveMenu]; 01319 01320 [mainMenu addItem:saveMenuItem]; 01321 01322 var editMenuItem = [[CPMenuItem alloc] initWithTitle:@"Edit" action:nil keyEquivalent:nil], 01323 editMenu = [[CPMenu alloc] initWithTitle:@"Edit"], 01324 01325 undoMenuItem = [[CPMenuItem alloc] initWithTitle:@"Undo" action:@selector(undo:) keyEquivalent:CPUndoKeyEquivalent], 01326 redoMenuItem = [[CPMenuItem alloc] initWithTitle:@"Redo" action:@selector(redo:) keyEquivalent:CPRedoKeyEquivalent]; 01327 01328 [undoMenuItem setKeyEquivalentModifierMask:CPUndoKeyEquivalentModifierMask]; 01329 [redoMenuItem setKeyEquivalentModifierMask:CPRedoKeyEquivalentModifierMask]; 01330 01331 [editMenu addItem:undoMenuItem]; 01332 [editMenu addItem:redoMenuItem]; 01333 01334 [editMenu addItem:[[CPMenuItem alloc] initWithTitle:@"Cut" action:@selector(cut:) keyEquivalent:@"x"]]; 01335 [editMenu addItem:[[CPMenuItem alloc] initWithTitle:@"Copy" action:@selector(copy:) keyEquivalent:@"c"]]; 01336 [editMenu addItem:[[CPMenuItem alloc] initWithTitle:@"Paste" action:@selector(paste:) keyEquivalent:@"v"]]; 01337 01338 [editMenuItem setSubmenu:editMenu]; 01339 [editMenuItem setHidden:YES]; 01340 01341 [mainMenu addItem:editMenuItem]; 01342 01343 [mainMenu addItem:[CPMenuItem separatorItem]]; 01344 01345 [CPApp setMainMenu:mainMenu]; 01346 } 01347 01348 + (void)cibDidFinishLoading:(CPCib)aCib 01349 { 01350 [self performActions]; 01351 } 01352 01353 + (void)cibDidFailToLoad:(CPCib)aCib 01354 { 01355 throw new Error("Could not load main cib file (Did you forget to nib2cib it?)."); 01356 } 01357 01358 + (void)reset 01359 { 01360 _CPAppBootstrapperActions = nil; 01361 } 01362 01363 @end 01364 01365 @implementation CPApplication (CPSynthesizedAccessors) 01366 01370 - (CPThemeBlend)themeBlend 01371 { 01372 return _themeBlend; 01373 } 01374 01378 - (void)setThemeBlend:(CPThemeBlend)aValue 01379 { 01380 _themeBlend = aValue; 01381 } 01382 01383 @end