API 0.9.5
AppKit/CPApplication.j
Go to the documentation of this file.
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
 All Classes Files Functions Variables Defines