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)orderFrontColorPanel:(id)aSender
00480 {
00481 [[CPColorPanel sharedColorPanel] orderFront:self];
00482 }
00483
00484 - (void)orderFrontStandardAboutPanel:(id)aSender
00485 {
00486 [self orderFrontStandardAboutPanelWithOptions:nil];
00487 }
00488
00489 - (void)orderFrontStandardAboutPanelWithOptions:(CPDictionary)aDictionary
00490 {
00491
00492 }
00493
00494
00504 - (BOOL)tryToPerform:(SEL)anAction with:(id)anObject
00505 {
00506 if (!anAction)
00507 return NO;
00508
00509 if ([super tryToPerform:anAction with:anObject])
00510 return YES;
00511
00512 if([_delegate respondsToSelector:anAction])
00513 {
00514 [_delegate performSelector:anAction withObject:anObject];
00515
00516 return YES;
00517 }
00518
00519 return NO;
00520 }
00521
00529 - (BOOL)sendAction:(SEL)anAction to:(id)aTarget from:(id)aSender
00530 {
00531 var target = [self targetForAction:anAction to:aTarget from:aSender];
00532
00533 if (!target)
00534 return NO;
00535
00536 [target performSelector:anAction withObject:aSender];
00537
00538 return YES;
00539 }
00540
00552 - (id)targetForAction:(SEL)anAction to:(id)aTarget from:(id)aSender
00553 {
00554 if (!anAction)
00555 return nil;
00556
00557 if (aTarget)
00558 return aTarget;
00559
00560 return [self targetForAction:anAction];
00561 }
00562
00580 - (id)_targetForWindow:(CPWindow)aWindow action:(SEL)anAction
00581 {
00582 var responder = [aWindow firstResponder],
00583 checkWindow = YES;
00584
00585 while (responder)
00586 {
00587 if ([responder respondsToSelector:anAction])
00588 return responder;
00589
00590 if (responder == aWindow)
00591 checkWindow = NO;
00592
00593 responder = [responder nextResponder];
00594 }
00595
00596 if (checkWindow && [aWindow respondsToSelector:anAction])
00597 return aWindow;
00598
00599 var delegate = [aWindow delegate];
00600
00601 if ([delegate respondsToSelector:anAction])
00602 return delegate;
00603
00604 var windowController = [aWindow windowController];
00605
00606 if ([windowController respondsToSelector:anAction])
00607 return windowController;
00608
00609 var theDocument = [windowController document];
00610
00611 if (theDocument != delegate && [theDocument respondsToSelector:anAction])
00612 return theDocument;
00613
00614 return nil;
00615 }
00616
00631 - (id)targetForAction:(SEL)anAction
00632 {
00633 if (!anAction)
00634 return nil;
00635
00636 var target = [self _targetForWindow:[self keyWindow] action:anAction];
00637
00638 if (target)
00639 return target;
00640
00641 target = [self _targetForWindow:[self mainWindow] action:anAction];
00642
00643 if (target)
00644 return target;
00645
00646 if ([self respondsToSelector:anAction])
00647 return self;
00648
00649 if ([_delegate respondsToSelector:anAction])
00650 return _delegate;
00651
00652 if ([_documentController respondsToSelector:anAction])
00653 return _documentController;
00654
00655 return nil;
00656 }
00657
00658 - (void)setCallback:(Function)aCallback forNextEventMatchingMask:(unsigned int)aMask untilDate:(CPDate)anExpiration inMode:(CPString)aMode dequeue:(BOOL)shouldDequeue
00659 {
00660 _eventListeners.push(_CPEventListenerMake(aMask, aCallback));
00661
00662 if (_eventListeners.length == 3) objj_debug_print_backtrace();
00663 }
00664
00665 - (CPEvent)setTarget:(id)aTarget selector:(SEL)aSelector forNextEventMatchingMask:(unsigned int)aMask untilDate:(CPDate)anExpiration inMode:(CPString)aMode dequeue:(BOOL)shouldDequeue
00666 {
00667 _eventListeners.push(_CPEventListenerMake(aMask, function (anEvent) { objj_msgSend(aTarget, aSelector, anEvent); }));
00668 }
00669
00670
00671
00680 - (void)beginSheet:(CPWindow)aSheet modalForWindow:(CPWindow)aWindow modalDelegate:(id)aModalDelegate didEndSelector:(SEL)aDidEndSelector contextInfo:(id)aContextInfo
00681 {
00682 [aWindow _attachSheet:aSheet modalDelegate:aModalDelegate didEndSelector:aDidEndSelector contextInfo:aContextInfo];
00683 }
00684
00685 - (CPArray)arguments
00686 {
00687 if(_fullArgsString != window.location.hash)
00688 [self _reloadArguments];
00689
00690 return _args;
00691 }
00692
00693 - (void)setArguments:(CPArray)args
00694 {
00695 if(!args || args.length == 0)
00696 {
00697 _args = [];
00698 window.location.hash = @"#";
00699
00700 return;
00701 }
00702
00703 if([args class] != CPArray)
00704 args = [CPArray arrayWithObject:args];
00705
00706 _args = args;
00707
00708 var toEncode = [_args copy];
00709 for(var i=0, count = toEncode.length; i<count; i++)
00710 toEncode[i] = encodeURIComponent(toEncode[i]);
00711
00712 var hash = [toEncode componentsJoinedByString:@"/"];
00713
00714 window.location.hash = @"#" + hash;
00715 }
00716
00717 - (void)_reloadArguments
00718 {
00719 _fullArgsString = window.location.hash;
00720 var args = _fullArgsString.replace("#", "").split("/").slice(0);
00721
00722 for(var i=0, count = args.length; i<count; i++)
00723 args[i] = decodeURIComponent(args[i]);
00724
00725 _args = args;
00726 }
00727
00728 - (CPDictionary)namedArguments
00729 {
00730 return _namedArgs;
00731 }
00732
00733 @end
00734
00735 var _CPModalSessionMake = function(aWindow, aStopCode)
00736 {
00737 return { _window:aWindow, _state:CPRunContinuesResponse , _previous:nil };
00738 }
00739
00740 var _CPEventListenerMake = function(anEventMask, aCallback)
00741 {
00742 return { _mask:anEventMask, _callback:aCallback };
00743 }
00744
00745 var _CPRunModalLoop = function(anEvent)
00746 {
00747 [CPApp setCallback:_CPRunModalLoop forNextEventMatchingMask:CPAnyEventMask untilDate:nil inMode:0 dequeue:NO];
00748
00749 var theWindow = [anEvent window],
00750 modalSession = CPApp._currentSession;
00751
00752 if (theWindow == modalSession._window || [theWindow worksWhenModal])
00753 [theWindow sendEvent:anEvent];
00754 }
00755
00763 function CPApplicationMain(args, namedArgs)
00764 {
00765 var mainBundle = [CPBundle mainBundle],
00766 principalClass = [mainBundle principalClass];
00767
00768 if (!principalClass)
00769 principalClass = [CPApplication class];
00770
00771 [principalClass sharedApplication];
00772
00773
00774 if (!args && !namedArgs)
00775 {
00776 var args = [CPApp arguments],
00777 searchParams = window.location.search.substring(1).split("&");
00778 namedArgs = [CPDictionary dictionary];
00779
00780 if([args containsObject:"debug"])
00781 CPLogRegister(CPLogPopup);
00782
00783 for(var i=0; i<searchParams.length; i++)
00784 {
00785 var index = searchParams[i].indexOf('=');
00786 if(index == -1)
00787 [namedArgs setObject: "" forKey:searchParams[i]];
00788 else
00789 [namedArgs setObject: searchParams[i].substring(index+1) forKey: searchParams[i].substring(0, index)];
00790 }
00791 }
00792
00793 CPApp._args = args;
00794 CPApp._namedArgs = namedArgs;
00795
00796 [_CPAppBootstrapper performActions];
00797 }
00798
00799 var _CPAppBootstrapperActions = nil;
00800
00801 @implementation _CPAppBootstrapper : CPObject
00802 {
00803 }
00804
00805 + (void)actions
00806 {
00807 return [@selector(loadDefaultTheme), @selector(loadMainCibFile)];
00808 }
00809
00810 + (void)performActions
00811 {
00812 if (!_CPAppBootstrapperActions)
00813 _CPAppBootstrapperActions = [self actions];
00814
00815 while (_CPAppBootstrapperActions.length)
00816 {
00817 var action = _CPAppBootstrapperActions.shift();
00818
00819 if (objj_msgSend(self, action))
00820 return;
00821 }
00822
00823 [CPApp run];
00824 }
00825
00826 + (BOOL)loadDefaultTheme
00827 {
00828 var blend = [[CPThemeBlend alloc] initWithContentsOfURL:[[CPBundle bundleForClass:[CPApplication class]] pathForResource:@"Aristo.blend"]];
00829
00830 [blend loadWithDelegate:self];
00831
00832 return YES;
00833 }
00834
00835 + (void)blendDidFinishLoading:(CPBundle)aBundle
00836 {
00837 [CPTheme setDefaultTheme:[CPTheme themeNamed:@"Aristo"]];
00838
00839 [self performActions];
00840 }
00841
00842 + (BOOL)loadMainCibFile
00843 {
00844 var mainBundle = [CPBundle mainBundle],
00845 mainCibFile = [mainBundle objectForInfoDictionaryKey:CPMainCibFile] || [mainBundle objectForInfoDictionaryKey:CPMainCibFileHumanFriendly];
00846
00847 if (mainCibFile)
00848 {
00849 [mainBundle loadCibFile:mainCibFile
00850 externalNameTable:[CPDictionary dictionaryWithObject:CPApp forKey:CPCibOwner]
00851 loadDelegate:self];
00852
00853 return YES;
00854 }
00855
00856 return NO;
00857 }
00858
00859 + (void)cibDidFinishLoading:(CPCib)aCib
00860 {
00861 [self performActions];
00862 }
00863
00864 @end