00001
00002
00003
00004
00005
00006
00007
00008
00009
00010
00011
00012
00013
00014
00015
00016
00017
00018
00019
00020
00021
00022
00023 @import <Foundation/CPBundle.j>
00024
00025 @import "CPCompatibility.j"
00026 @import "CPEvent.j"
00027 @import "CPMenu.j"
00028 @import "CPResponder.j"
00029 @import "CPDocumentController.j"
00030 @import "CPThemeBlend.j"
00031
00032 var CPMainCibFile = @"CPMainCibFile",
00033 CPMainCibFileHumanFriendly = @"Main cib file base name";
00034
00035 CPApp = nil;
00036
00037 CPApplicationWillFinishLaunchingNotification = @"CPApplicationWillFinishLaunchingNotification";
00038 CPApplicationDidFinishLaunchingNotification = @"CPApplicationDidFinishLaunchingNotification";
00039
00040 CPRunStoppedResponse = -1000;
00041 CPRunAbortedResponse = -1001;
00042 CPRunContinuesResponse = -1002;
00043
00069 @implementation CPApplication : CPResponder
00070 {
00071 CPArray _eventListeners;
00072
00073 CPEvent _currentEvent;
00074
00075 CPArray _windows;
00076 CPWindow _keyWindow;
00077 CPWindow _mainWindow;
00078
00079 CPMenu _mainMenu;
00080 CPDocumentController _documentController;
00081
00082 CPModalSession _currentSession;
00083
00084
00085 id _delegate;
00086
00087 CPDictionary _namedArgs;
00088 CPArray _args;
00089 CPString _fullArgsString;
00090 }
00091
00097 + (CPApplication)sharedApplication
00098 {
00099 if (!CPApp)
00100 CPApp = [[CPApplication alloc] init];
00101
00102 return CPApp;
00103 }
00104
00110 - (id)init
00111 {
00112 self = [super init];
00113
00114 if (self)
00115 {
00116 _eventListeners = [];
00117
00118 _windows = [];
00119
00120 [_windows addObject:nil];
00121
00122
00123 _mainMenu = [[CPMenu alloc] initWithTitle:@"MainMenu"];
00124
00125
00126 [_mainMenu setAutoenablesItems:NO];
00127
00128 var bundle = [CPBundle bundleForClass:[CPApplication class]],
00129 newMenuItem = [[CPMenuItem alloc] initWithTitle:@"New" action:@selector(newDocument:) keyEquivalent:@"N"];
00130
00131 [newMenuItem setImage:[[CPImage alloc] initWithContentsOfFile:[bundle pathForResource:@"CPApplication/New.png"] size:CGSizeMake(16.0, 16.0)]];
00132 [newMenuItem setAlternateImage:[[CPImage alloc] initWithContentsOfFile:[bundle pathForResource:@"CPApplication/NewHighlighted.png"] size:CGSizeMake(16.0, 16.0)]];
00133
00134 [_mainMenu addItem:newMenuItem];
00135
00136 var openMenuItem = [[CPMenuItem alloc] initWithTitle:@"Open" action:@selector(openDocument:) keyEquivalent:@"O"];
00137
00138 [openMenuItem setImage:[[CPImage alloc] initWithContentsOfFile:[bundle pathForResource:@"CPApplication/Open.png"] size:CGSizeMake(16.0, 16.0)]];
00139 [openMenuItem setAlternateImage:[[CPImage alloc] initWithContentsOfFile:[bundle pathForResource:@"CPApplication/OpenHighlighted.png"] size:CGSizeMake(16.0, 16.0)]];
00140
00141 [_mainMenu addItem:openMenuItem];
00142
00143 var saveMenu = [[CPMenu alloc] initWithTitle:@"Save"],
00144 saveMenuItem = [[CPMenuItem alloc] initWithTitle:@"Save" action:@selector(saveDocument:) keyEquivalent:nil];
00145
00146 [saveMenuItem setImage:[[CPImage alloc] initWithContentsOfFile:[bundle pathForResource:@"CPApplication/Save.png"] size:CGSizeMake(16.0, 16.0)]];
00147 [saveMenuItem setAlternateImage:[[CPImage alloc] initWithContentsOfFile:[bundle pathForResource:@"CPApplication/SaveHighlighted.png"] size:CGSizeMake(16.0, 16.0)]];
00148
00149 [saveMenu addItem:[[CPMenuItem alloc] initWithTitle:@"Save" action:@selector(saveDocument:) keyEquivalent:@"S"]];
00150 [saveMenu addItem:[[CPMenuItem alloc] initWithTitle:@"Save As" action:@selector(saveDocumentAs:) keyEquivalent:nil]];
00151
00152 [saveMenuItem setSubmenu:saveMenu];
00153
00154 [_mainMenu addItem:saveMenuItem];
00155
00156 var editMenuItem = [[CPMenuItem alloc] initWithTitle:@"Edit" action:nil keyEquivalent:nil],
00157 editMenu = [[CPMenu alloc] initWithTitle:@"Edit"],
00158
00159 undoMenuItem = [[CPMenuItem alloc] initWithTitle:@"Undo" action:@selector(undo:) keyEquivalent:CPUndoKeyEquivalent],
00160 redoMenuItem = [[CPMenuItem alloc] initWithTitle:@"Redo" action:@selector(redo:) keyEquivalent:CPRedoKeyEquivalent];
00161
00162 [undoMenuItem setKeyEquivalentModifierMask:CPUndoKeyEquivalentModifierMask];
00163 [redoMenuItem setKeyEquivalentModifierMask:CPRedoKeyEquivalentModifierMask];
00164
00165 [editMenu addItem:undoMenuItem];
00166 [editMenu addItem:redoMenuItem];
00167
00168 [editMenu addItem:[[CPMenuItem alloc] initWithTitle:@"Cut" action:@selector(cut:) keyEquivalent:@"X"]],
00169 [editMenu addItem:[[CPMenuItem alloc] initWithTitle:@"Copy" action:@selector(copy:) keyEquivalent:@"C"]],
00170 [editMenu addItem:[[CPMenuItem alloc] initWithTitle:@"Paste" action:@selector(paste:) keyEquivalent:@"V"]];
00171
00172 [editMenuItem setSubmenu:editMenu];
00173 [editMenuItem setHidden:YES];
00174
00175 [_mainMenu addItem:editMenuItem];
00176
00177 [_mainMenu addItem:[CPMenuItem separatorItem]];
00178 }
00179
00180 return self;
00181 }
00182
00183
00184
00191 - (void)setDelegate:(id)aDelegate
00192 {
00193 if (_delegate == aDelegate)
00194 return;
00195
00196 var defaultCenter = [CPNotificationCenter defaultCenter];
00197
00198 if (_delegate)
00199 {
00200 [defaultCenter
00201 removeObserver:_delegate
00202 name:CPApplicationWillFinishLaunchingNotification
00203 object:self];
00204
00205 [defaultCenter
00206 removeObserver:_delegate
00207 name:CPApplicationDidFinishLaunchingNotification
00208 object:self];
00209 }
00210
00211 _delegate = aDelegate;
00212
00213 if ([_delegate respondsToSelector:@selector(applicationWillFinishLaunching:)])
00214 [defaultCenter
00215 addObserver:_delegate
00216 selector:@selector(applicationWillFinishLaunching:)
00217 name:CPApplicationWillFinishLaunchingNotification
00218 object:self];
00219
00220 if ([_delegate respondsToSelector:@selector(applicationDidFinishLaunching:)])
00221 [defaultCenter
00222 addObserver:_delegate
00223 selector:@selector(applicationDidFinishLaunching:)
00224 name:CPApplicationDidFinishLaunchingNotification
00225 object:self];
00226 }
00227
00231 - (id)delegate
00232 {
00233 return _delegate;
00234 }
00235
00242 - (void)finishLaunching
00243 {
00244 var bundle = [CPBundle mainBundle],
00245 types = [bundle objectForInfoDictionaryKey:@"CPBundleDocumentTypes"];
00246
00247 if ([types count] > 0)
00248 _documentController = [CPDocumentController sharedDocumentController];
00249
00250 var delegateClassName = [bundle objectForInfoDictionaryKey:@"CPApplicationDelegateClass"];
00251
00252 if (delegateClassName)
00253 {
00254 var delegateClass = objj_getClass(delegateClassName);
00255
00256 if (delegateClass)
00257 if ([_documentController class] == delegateClass)
00258 [self setDelegate:_documentController];
00259 else
00260 [self setDelegate:[[delegateClass alloc] init]];
00261 }
00262
00263 var defaultCenter = [CPNotificationCenter defaultCenter];
00264
00265 [defaultCenter
00266 postNotificationName:CPApplicationWillFinishLaunchingNotification
00267 object:self];
00268
00269 if (_documentController)
00270 [_documentController newDocument:self];
00271
00272 [defaultCenter
00273 postNotificationName:CPApplicationDidFinishLaunchingNotification
00274 object:self];
00275
00276 [[CPRunLoop currentRunLoop] limitDateForMode:CPDefaultRunLoopMode];
00277 }
00278
00283 - (void)run
00284 {
00285 [self finishLaunching];
00286 }
00287
00288
00293 - (void)runModalForWindow:(CPWindow)aWindow
00294 {
00295 [self runModalSession:[self beginModalSessionForWindow:aWindow]];
00296 }
00297
00303 - (void)stopModalWithCode:(int)aCode
00304 {
00305 if (!_currentSession)
00306 {
00307 return;
00308
00309 }
00310
00311 _currentSession._state = aCode;
00312 _currentSession = _currentSession._previous;
00313
00314
00315 [self _removeRunModalLoop];
00316 }
00317
00318
00319 - (void)_removeRunModalLoop
00320 {
00321 var count = _eventListeners.length;
00322
00323 while (count--)
00324 if (_eventListeners[count]._callback === _CPRunModalLoop)
00325 {
00326 _eventListeners.splice(count, 1);
00327
00328 return;
00329 }
00330 }
00331
00335 - (void)stopModal
00336 {
00337 [self stopModalWithCode:CPRunStoppedResponse]
00338 }
00339
00343 - (void)abortModal
00344 {
00345 [self stopModalWithCode:CPRunAbortedResponse];
00346 }
00347
00352 - (CPModalSession)beginModalSessionForWindow:(CPWindow)aWindow
00353 {
00354 return _CPModalSessionMake(aWindow, 0);
00355 }
00356
00361 - (void)runModalSession:(CPModalSession)aModalSession
00362 {
00363 aModalSession._previous = _currentSession;
00364 _currentSession = aModalSession;
00365
00366 var theWindow = aModalSession._window;
00367
00368 [theWindow center];
00369 [theWindow makeKeyAndOrderFront:self];
00370
00371
00372
00373 [CPApp setCallback:_CPRunModalLoop forNextEventMatchingMask:CPAnyEventMask untilDate:nil inMode:0 dequeue:NO];
00374 }
00375
00380 - (CPWindow)modalWindow
00381 {
00382 if (!_currentSession)
00383 return nil;
00384
00385 return _currentSession._window;
00386 }
00387
00388
00389 - (BOOL)_handleKeyEquivalent:(CPEvent)anEvent
00390 {
00391 if ([_mainMenu performKeyEquivalent:anEvent])
00392 return YES;
00393
00394 return NO;
00395 }
00396
00401 - (void)sendEvent:(CPEvent)anEvent
00402 {
00403
00404 if ([anEvent type] == CPKeyDown &&
00405 [anEvent modifierFlags] & (CPCommandKeyMask | CPControlKeyMask) &&
00406 [[anEvent characters] length] > 0 &&
00407 [self _handleKeyEquivalent:anEvent])
00408 return;
00409
00410 if (_eventListeners.length)
00411 {
00412 if (_eventListeners[_eventListeners.length - 1]._mask & (1 << [anEvent type]))
00413 _eventListeners.pop()._callback(anEvent);
00414
00415 return;
00416 }
00417
00418 [[anEvent window] sendEvent:anEvent];
00419 }
00420
00421 - (void)doCommandBySelector:(SEL)aSelector
00422 {
00423 if ([_delegate respondsToSelector:aSelector])
00424 [_delegate performSelector:aSelector];
00425 else
00426 [super doCommandBySelector:aSelector];
00427 }
00428
00432 - (CPWindow)keyWindow
00433 {
00434 return _keyWindow;
00435 }
00436
00440 - (CPWindow)mainWindow
00441 {
00442 return _mainWindow;
00443 }
00444
00448 - (CPWindow)windowWithWindowNumber:(int)aWindowNumber
00449 {
00450 return _windows[aWindowNumber];
00451 }
00452
00456 - (CPArray)windows
00457 {
00458 return _windows;
00459 }
00460
00461
00465 - (CPMenu)mainMenu
00466 {
00467 return _mainMenu;
00468 }
00469
00474 - (void)setMainMenu:(CPMenu)aMenu
00475 {
00476 _mainMenu = aMenu;
00477 }
00478
00479 - (void)orderFrontStandardAboutPanel:(id)aSender
00480 {
00481 [self orderFrontStandardAboutPanelWithOptions:nil];
00482 }
00483
00484 - (void)orderFrontStandardAboutPanelWithOptions:(CPDictionary)aDictionary
00485 {
00486
00487 }
00488
00489
00499 - (BOOL)tryToPerform:(SEL)anAction with:(id)anObject
00500 {
00501 if (!anAction)
00502 return NO;
00503
00504 if ([super tryToPerform:anAction with:anObject])
00505 return YES;
00506
00507 if([_delegate respondsToSelector:anAction])
00508 {
00509 [_delegate performSelector:anAction withObject:anObject];
00510
00511 return YES;
00512 }
00513
00514 return NO;
00515 }
00516
00524 - (BOOL)sendAction:(SEL)anAction to:(id)aTarget from:(id)aSender
00525 {
00526 var target = [self targetForAction:anAction to:aTarget from:aSender];
00527
00528 if (!target)
00529 return NO;
00530
00531 [target performSelector:anAction withObject:aSender];
00532
00533 return YES;
00534 }
00535
00547 - (id)targetForAction:(SEL)anAction to:(id)aTarget from:(id)aSender
00548 {
00549 if (!anAction)
00550 return nil;
00551
00552 if (aTarget)
00553 return aTarget;
00554
00555 return [self targetForAction:anAction];
00556 }
00557
00575 - (id)_targetForWindow:(CPWindow)aWindow action:(SEL)anAction
00576 {
00577 var responder = [aWindow firstResponder],
00578 checkWindow = YES;
00579
00580 while (responder)
00581 {
00582 if ([responder respondsToSelector:anAction])
00583 return responder;
00584
00585 if (responder == aWindow)
00586 checkWindow = NO;
00587
00588 responder = [responder nextResponder];
00589 }
00590
00591 if (checkWindow && [aWindow respondsToSelector:anAction])
00592 return aWindow;
00593
00594 var delegate = [aWindow delegate];
00595
00596 if ([delegate respondsToSelector:anAction])
00597 return delegate;
00598
00599 var windowController = [aWindow windowController];
00600
00601 if ([windowController respondsToSelector:anAction])
00602 return windowController;
00603
00604 var theDocument = [windowController document];
00605
00606 if (theDocument != delegate && [theDocument respondsToSelector:anAction])
00607 return theDocument;
00608
00609 return nil;
00610 }
00611
00626 - (id)targetForAction:(SEL)anAction
00627 {
00628 if (!anAction)
00629 return nil;
00630
00631 var target = [self _targetForWindow:[self keyWindow] action:anAction];
00632
00633 if (target)
00634 return target;
00635
00636 target = [self _targetForWindow:[self mainWindow] action:anAction];
00637
00638 if (target)
00639 return target;
00640
00641 if ([self respondsToSelector:anAction])
00642 return self;
00643
00644 if ([_delegate respondsToSelector:anAction])
00645 return _delegate;
00646
00647 if ([_documentController respondsToSelector:anAction])
00648 return _documentController;
00649
00650 return nil;
00651 }
00652
00653 - (void)setCallback:(Function)aCallback forNextEventMatchingMask:(unsigned int)aMask untilDate:(CPDate)anExpiration inMode:(CPString)aMode dequeue:(BOOL)shouldDequeue
00654 {
00655 _eventListeners.push(_CPEventListenerMake(aMask, aCallback));
00656
00657 if (_eventListeners.length == 3) objj_debug_print_backtrace();
00658 }
00659
00660 - (CPEvent)setTarget:(id)aTarget selector:(SEL)aSelector forNextEventMatchingMask:(unsigned int)aMask untilDate:(CPDate)anExpiration inMode:(CPString)aMode dequeue:(BOOL)shouldDequeue
00661 {
00662 _eventListeners.push(_CPEventListenerMake(aMask, function (anEvent) { objj_msgSend(aTarget, aSelector, anEvent); }));
00663 }
00664
00665
00666
00675 - (void)beginSheet:(CPWindow)aSheet modalForWindow:(CPWindow)aWindow modalDelegate:(id)aModalDelegate didEndSelector:(SEL)aDidEndSelector contextInfo:(id)aContextInfo
00676 {
00677 [aWindow _attachSheet:aSheet modalDelegate:aModalDelegate didEndSelector:aDidEndSelector contextInfo:aContextInfo];
00678 }
00679
00680 - (CPArray)arguments
00681 {
00682 if(_fullArgsString != window.location.hash)
00683 [self _reloadArguments];
00684
00685 return _args;
00686 }
00687
00688 - (void)setArguments:(CPArray)args
00689 {
00690 if(!args || args.length == 0)
00691 {
00692 _args = [];
00693 window.location.hash = @"#";
00694
00695 return;
00696 }
00697
00698 if([args class] != CPArray)
00699 args = [CPArray arrayWithObject:args];
00700
00701 _args = args;
00702
00703 var toEncode = [_args copy];
00704 for(var i=0, count = toEncode.length; i<count; i++)
00705 toEncode[i] = encodeURIComponent(toEncode[i]);
00706
00707 var hash = [toEncode componentsJoinedByString:@"/"];
00708
00709 window.location.hash = @"#" + hash;
00710 }
00711
00712 - (void)_reloadArguments
00713 {
00714 _fullArgsString = window.location.hash;
00715 var args = _fullArgsString.replace("#", "").split("/").slice(0);
00716
00717 for(var i=0, count = args.length; i<count; i++)
00718 args[i] = decodeURIComponent(args[i]);
00719
00720 _args = args;
00721 }
00722
00723 - (CPDictionary)namedArguments
00724 {
00725 return _namedArgs;
00726 }
00727
00728 @end
00729
00730 var _CPModalSessionMake = function(aWindow, aStopCode)
00731 {
00732 return { _window:aWindow, _state:CPRunContinuesResponse , _previous:nil };
00733 }
00734
00735 var _CPEventListenerMake = function(anEventMask, aCallback)
00736 {
00737 return { _mask:anEventMask, _callback:aCallback };
00738 }
00739
00740 var _CPRunModalLoop = function(anEvent)
00741 {
00742 [CPApp setCallback:_CPRunModalLoop forNextEventMatchingMask:CPAnyEventMask untilDate:nil inMode:0 dequeue:NO];
00743
00744 var theWindow = [anEvent window],
00745 modalSession = CPApp._currentSession;
00746
00747 if (theWindow == modalSession._window || [theWindow worksWhenModal])
00748 [theWindow sendEvent:anEvent];
00749 }
00750
00758 function CPApplicationMain(args, namedArgs)
00759 {
00760 var mainBundle = [CPBundle mainBundle],
00761 principalClass = [mainBundle principalClass];
00762
00763 if (!principalClass)
00764 principalClass = [CPApplication class];
00765
00766 [principalClass sharedApplication];
00767
00768
00769 if (!args && !namedArgs)
00770 {
00771 var args = [CPApp arguments],
00772 searchParams = window.location.search.substring(1).split("&");
00773 namedArgs = [CPDictionary dictionary];
00774
00775 if([args containsObject:"debug"])
00776 CPLogRegister(CPLogPopup);
00777
00778 for(var i=0; i<searchParams.length; i++)
00779 {
00780 var index = searchParams[i].indexOf('=');
00781 if(index == -1)
00782 [namedArgs setObject: "" forKey:searchParams[i]];
00783 else
00784 [namedArgs setObject: searchParams[i].substring(index+1) forKey: searchParams[i].substring(0, index)];
00785 }
00786 }
00787
00788 CPApp._args = args;
00789 CPApp._namedArgs = namedArgs;
00790
00791 [_CPAppBootstrapper performActions];
00792 }
00793
00794 var _CPAppBootstrapperActions = nil;
00795
00796 @implementation _CPAppBootstrapper : CPObject
00797 {
00798 }
00799
00800 + (void)actions
00801 {
00802 return [@selector(loadDefaultTheme), @selector(loadMainCibFile)];
00803 }
00804
00805 + (void)performActions
00806 {
00807 if (!_CPAppBootstrapperActions)
00808 _CPAppBootstrapperActions = [self actions];
00809
00810 while (_CPAppBootstrapperActions.length)
00811 {
00812 var action = _CPAppBootstrapperActions.shift();
00813
00814 if (objj_msgSend(self, action))
00815 return;
00816 }
00817
00818 [CPApp run];
00819 }
00820
00821 + (BOOL)loadDefaultTheme
00822 {
00823 var blend = [[CPThemeBlend alloc] initWithContentsOfURL:[[CPBundle bundleForClass:[CPApplication class]] pathForResource:@"Aristo.blend"]];
00824
00825 [blend loadWithDelegate:self];
00826
00827 return YES;
00828 }
00829
00830 + (void)blendDidFinishLoading:(CPBundle)aBundle
00831 {
00832 [CPTheme setDefaultTheme:[CPTheme themeNamed:@"Aristo"]];
00833
00834 [self performActions];
00835 }
00836
00837 + (BOOL)loadMainCibFile
00838 {
00839 var mainBundle = [CPBundle mainBundle],
00840 mainCibFile = [mainBundle objectForInfoDictionaryKey:CPMainCibFile] || [mainBundle objectForInfoDictionaryKey:CPMainCibFileHumanFriendly];
00841
00842 if (mainCibFile)
00843 {
00844 [CPBundle loadCibFile:[mainBundle pathForResource:mainCibFile]
00845 externalNameTable:[CPDictionary dictionaryWithObject:CPApp forKey:CPCibOwner]
00846 loadDelegate:self];
00847
00848 return YES;
00849 }
00850
00851 return NO;
00852 }
00853
00854 + (void)cibDidFinishLoading:(CPCib)aCib
00855 {
00856 [self performActions];
00857 }
00858
00859 @end