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
00031
00032 CPApp = nil;
00033
00034 CPApplicationWillFinishLaunchingNotification = @"CPApplicationWillFinishLaunchingNotification";
00035 CPApplicationDidFinishLaunchingNotification = @"CPApplicationDidFinishLaunchingNotification";
00036
00037 CPRunStoppedResponse = -1000;
00038 CPRunAbortedResponse = -1001;
00039 CPRunContinuesResponse = -1002;
00040
00064 @implementation CPApplication : CPResponder
00065 {
00066 CPArray _eventListeners;
00067
00068 CPEvent _currentEvent;
00069
00070 CPArray _windows;
00071 CPWindow _keyWindow;
00072 CPWindow _mainWindow;
00073
00074 CPMenu _mainMenu;
00075 CPDocumentController _documentController;
00076
00077 CPModalSession _currentSession;
00078
00079
00080 id _delegate;
00081
00082 CPDictionary _namedArgs;
00083 CPArray _args;
00084 }
00085
00091 + (CPApplication)sharedApplication
00092 {
00093 if (!CPApp)
00094 CPApp = [[CPApplication alloc] init];
00095
00096 return CPApp;
00097 }
00098
00104 - (id)init
00105 {
00106 self = [super init];
00107
00108 if (self)
00109 {
00110 _eventListeners = [];
00111
00112 _windows = [];
00113
00114 [_windows addObject:nil];
00115
00116
00117 _mainMenu = [[CPMenu alloc] initWithTitle:@"MainMenu"];
00118
00119
00120 [_mainMenu setAutoenablesItems:NO];
00121
00122 var bundle = [CPBundle bundleForClass:[CPApplication class]],
00123 newMenuItem = [[CPMenuItem alloc] initWithTitle:@"New" action:@selector(newDocument:) keyEquivalent:@"N"];
00124
00125 [newMenuItem setImage:[[CPImage alloc] initWithContentsOfFile:[bundle pathForResource:@"CPApplication/New.png"] size:CGSizeMake(16.0, 16.0)]];
00126 [newMenuItem setAlternateImage:[[CPImage alloc] initWithContentsOfFile:[bundle pathForResource:@"CPApplication/NewHighlighted.png"] size:CGSizeMake(16.0, 16.0)]];
00127
00128 [_mainMenu addItem:newMenuItem];
00129
00130 var openMenuItem = [[CPMenuItem alloc] initWithTitle:@"Open" action:@selector(openDocument:) keyEquivalent:@"O"];
00131
00132 [openMenuItem setImage:[[CPImage alloc] initWithContentsOfFile:[bundle pathForResource:@"CPApplication/Open.png"] size:CGSizeMake(16.0, 16.0)]];
00133 [openMenuItem setAlternateImage:[[CPImage alloc] initWithContentsOfFile:[bundle pathForResource:@"CPApplication/OpenHighlighted.png"] size:CGSizeMake(16.0, 16.0)]];
00134
00135 [_mainMenu addItem:openMenuItem];
00136
00137 var saveMenu = [[CPMenu alloc] initWithTitle:@"Save"],
00138 saveMenuItem = [[CPMenuItem alloc] initWithTitle:@"Save" action:@selector(saveDocument:) keyEquivalent:@""];
00139
00140 [saveMenuItem setImage:[[CPImage alloc] initWithContentsOfFile:[bundle pathForResource:@"CPApplication/Save.png"] size:CGSizeMake(16.0, 16.0)]];
00141 [saveMenuItem setAlternateImage:[[CPImage alloc] initWithContentsOfFile:[bundle pathForResource:@"CPApplication/SaveHighlighted.png"] size:CGSizeMake(16.0, 16.0)]];
00142
00143 [saveMenu addItem:[[CPMenuItem alloc] initWithTitle:@"Save" action:@selector(saveDocument:) keyEquivalent:@"S"]];
00144 [saveMenu addItem:[[CPMenuItem alloc] initWithTitle:@"Save As" action:@selector(saveDocumentAs:) keyEquivalent:@""]];
00145
00146 [saveMenuItem setSubmenu:saveMenu];
00147
00148 [_mainMenu addItem:saveMenuItem];
00149
00150 var editMenuItem = [[CPMenuItem alloc] initWithTitle:@"Edit" action:nil keyEquivalent:@""],
00151 editMenu = [[CPMenu alloc] initWithTitle:@"Edit"],
00152
00153 undoMenuItem = [[CPMenuItem alloc] initWithTitle:@"Undo" action:@selector(undo:) keyEquivalent:CPUndoKeyEquivalent],
00154 redoMenuItem = [[CPMenuItem alloc] initWithTitle:@"Redo" action:@selector(redo:) keyEquivalent:CPRedoKeyEquivalent];
00155
00156 [undoMenuItem setKeyEquivalentModifierMask:CPUndoKeyEquivalentModifierMask];
00157 [redoMenuItem setKeyEquivalentModifierMask:CPRedoKeyEquivalentModifierMask];
00158
00159 [editMenu addItem:undoMenuItem];
00160 [editMenu addItem:redoMenuItem];
00161
00162 [editMenu addItem:[[CPMenuItem alloc] initWithTitle:@"Cut" action:@selector(cut:) keyEquivalent:@"X"]],
00163 [editMenu addItem:[[CPMenuItem alloc] initWithTitle:@"Copy" action:@selector(copy:) keyEquivalent:@"C"]],
00164 [editMenu addItem:[[CPMenuItem alloc] initWithTitle:@"Paste" action:@selector(paste:) keyEquivalent:@"V"]];
00165
00166 [editMenuItem setSubmenu:editMenu];
00167 [editMenuItem setHidden:YES];
00168
00169 [_mainMenu addItem:editMenuItem];
00170
00171 [_mainMenu addItem:[CPMenuItem separatorItem]];
00172 }
00173
00174 return self;
00175 }
00176
00177
00178
00185 - (void)setDelegate:(id)aDelegate
00186 {
00187 if (_delegate == aDelegate)
00188 return;
00189
00190 var defaultCenter = [CPNotificationCenter defaultCenter];
00191
00192 if (_delegate)
00193 {
00194 [defaultCenter
00195 removeObserver:_delegate
00196 name:CPApplicationWillFinishLaunchingNotification
00197 object:self];
00198
00199 [defaultCenter
00200 removeObserver:_delegate
00201 name:CPApplicationDidFinishLaunchingNotification
00202 object:self];
00203 }
00204
00205 _delegate = aDelegate;
00206
00207 if ([_delegate respondsToSelector:@selector(applicationWillFinishLaunching:)])
00208 [defaultCenter
00209 addObserver:_delegate
00210 selector:@selector(applicationWillFinishLaunching:)
00211 name:CPApplicationWillFinishLaunchingNotification
00212 object:self];
00213
00214 if ([_delegate respondsToSelector:@selector(applicationDidFinishLaunching:)])
00215 [defaultCenter
00216 addObserver:_delegate
00217 selector:@selector(applicationDidFinishLaunching:)
00218 name:CPApplicationDidFinishLaunchingNotification
00219 object:self];
00220 }
00221
00225 - (id)delegate
00226 {
00227 return _delegate;
00228 }
00229
00236 - (void)finishLaunching
00237 {
00238 var bundle = [CPBundle mainBundle],
00239 types = [bundle objectForInfoDictionaryKey:@"CPBundleDocumentTypes"];
00240
00241 if ([types count] > 0)
00242 _documentController = [CPDocumentController sharedDocumentController];
00243
00244 var delegateClassName = [bundle objectForInfoDictionaryKey:@"CPApplicationDelegateClass"];
00245
00246 if (delegateClassName)
00247 {
00248 var delegateClass = objj_getClass(delegateClassName);
00249
00250 if (delegateClass)
00251 if ([_documentController class] == delegateClass)
00252 [self setDelegate:_documentController];
00253 else
00254 [self setDelegate:[[delegateClass alloc] init]];
00255 }
00256
00257 var defaultCenter = [CPNotificationCenter defaultCenter];
00258
00259 [defaultCenter
00260 postNotificationName:CPApplicationWillFinishLaunchingNotification
00261 object:self];
00262
00263 if (_documentController)
00264 [_documentController newDocument:self];
00265
00266 [defaultCenter
00267 postNotificationName:CPApplicationDidFinishLaunchingNotification
00268 object:self];
00269
00270 [[CPRunLoop currentRunLoop] limitDateForMode:CPDefaultRunLoopMode];
00271 }
00272
00277 - (void)run
00278 {
00279 [self finishLaunching];
00280 }
00281
00282
00287 - (void)runModalForWindow:(CPWindow)aWindow
00288 {
00289 [self runModalSession:[self beginModalSessionForWindow:aWindow]];
00290 }
00291
00297 - (void)stopModalWithCode:(int)aCode
00298 {
00299 if (!_currentSession)
00300 {
00301 return;
00302
00303 }
00304
00305 _currentSession._state = aCode;
00306 _currentSession = _currentSession._previous;
00307
00308 if (aCode == CPRunAbortedResponse)
00309 [self _removeRunModalLoop];
00310 }
00311
00312
00313 - (void)_removeRunModalLoop
00314 {
00315 var count = _eventListeners.length;
00316
00317 while (count--)
00318 if (_eventListeners[count]._callback == _CPRunModalLoop)
00319 {
00320 _eventListeners.splice(count, 1);
00321
00322 return;
00323 }
00324 }
00325
00329 - (void)stopModal
00330 {
00331 [self stopModalWithCode:CPRunStoppedResponse]
00332 }
00333
00337 - (void)abortModal
00338 {
00339 [self stopModalWithCode:CPRunAbortedResponse];
00340 }
00341
00346 - (CPModalSession)beginModalSessionForWindow:(CPWindow)aWindow
00347 {
00348 return _CPModalSessionMake(aWindow, 0);
00349 }
00350
00355 - (void)runModalSession:(CPModalSession)aModalSession
00356 {
00357 aModalSession._previous = _currentSession;
00358 _currentSession = aModalSession;
00359
00360 var theWindow = aModalSession._window;
00361
00362 [theWindow center];
00363 [theWindow makeKeyAndOrderFront:self];
00364
00365
00366
00367 [CPApp setCallback:_CPRunModalLoop forNextEventMatchingMask:CPAnyEventMask untilDate:nil inMode:0 dequeue:NO];
00368 }
00369
00374 - (CPWindow)modalWindow
00375 {
00376 if (!_currentSession)
00377 return nil;
00378
00379 return _currentSession._window;
00380 }
00381
00382
00383 - (BOOL)_handleKeyEquivalent:(CPEvent)anEvent
00384 {
00385 if ([_mainMenu performKeyEquivalent:anEvent])
00386 return YES;
00387
00388 return NO;
00389 }
00390
00395 - (void)sendEvent:(CPEvent)anEvent
00396 {
00397
00398 if ([anEvent type] == CPKeyDown &&
00399 [anEvent modifierFlags] & (CPCommandKeyMask | CPControlKeyMask) &&
00400 [[anEvent characters] length] > 0 &&
00401 [self _handleKeyEquivalent:anEvent])
00402 return;
00403
00404 if (_eventListeners.length)
00405 {
00406 if (_eventListeners[_eventListeners.length - 1]._mask & (1 << [anEvent type]))
00407 _eventListeners.pop()._callback(anEvent);
00408
00409 return;
00410 }
00411
00412 [[anEvent window] sendEvent:anEvent];
00413 }
00414
00415 - (void)doCommandBySelector:(SEL)aSelector
00416 {
00417 if ([_delegate respondsToSelector:aSelector])
00418 [_delegate performSelector:aSelector];
00419 else
00420 [super doCommandBySelector:aSelector];
00421 }
00422
00426 - (CPWindow)keyWindow
00427 {
00428 return _keyWindow;
00429 }
00430
00434 - (CPWindow)mainWindow
00435 {
00436 return _mainWindow;
00437 }
00438
00442 - (CPWindow)windowWithWindowNumber:(int)aWindowNumber
00443 {
00444 return _windows[aWindowNumber];
00445 }
00446
00450 - (CPArray)windows
00451 {
00452 return _windows;
00453 }
00454
00455
00459 - (CPMenu)mainMenu
00460 {
00461 return _mainMenu;
00462 }
00463
00468 - (void)setMainMenu:(CPMenu)aMenu
00469 {
00470 _mainMenu = aMenu;
00471 }
00472
00473
00483 - (BOOL)tryToPerform:(SEL)anAction with:(id)anObject
00484 {
00485 if (!anAction)
00486 return NO;
00487
00488 if ([self tryToPerform:anAction with:anObject])
00489 return YES;
00490
00491 if([_delegate respondsToSelector:aSelector])
00492 {
00493 [_delegate performSelector:aSelector withObject:anObject];
00494
00495 return YES;
00496 }
00497
00498 return NO;
00499 }
00500
00508 - (BOOL)sendAction:(SEL)anAction to:(id)aTarget from:(id)aSender
00509 {
00510 var target = [self targetForAction:anAction to:aTarget from:aSender];
00511
00512 if (!target)
00513 return NO;
00514
00515 [target performSelector:anAction withObject:aSender];
00516
00517 return YES;
00518 }
00519
00531 - (id)targetForAction:(SEL)anAction to:(id)aTarget from:(id)aSender
00532 {
00533 if (!anAction)
00534 return nil;
00535
00536 if (aTarget)
00537 return aTarget;
00538
00539 return [self targetForAction:anAction];
00540 }
00541
00559 - (id)_targetForWindow:(CPWindow)aWindow action:(SEL)anAction
00560 {
00561 var responder = [aWindow firstResponder],
00562 checkWindow = YES;
00563
00564 while (responder)
00565 {
00566 if ([responder respondsToSelector:anAction])
00567 return responder;
00568
00569 if (responder == aWindow)
00570 checkWindow = NO;
00571
00572 responder = [responder nextResponder];
00573 }
00574
00575 if (checkWindow && [aWindow respondsToSelector:anAction])
00576 return aWindow;
00577
00578 var delegate = [aWindow delegate];
00579
00580 if ([delegate respondsToSelector:anAction])
00581 return delegate;
00582
00583 var windowController = [aWindow windowController];
00584
00585 if ([windowController respondsToSelector:anAction])
00586 return windowController;
00587
00588 var theDocument = [windowController document];
00589
00590 if (theDocument != delegate && [theDocument respondsToSelector:anAction])
00591 return theDocument;
00592
00593 return nil;
00594 }
00595
00610 - (id)targetForAction:(SEL)anAction
00611 {
00612 if (!anAction)
00613 return nil;
00614
00615 var target = [self _targetForWindow:[self keyWindow] action:anAction];
00616
00617 if (target)
00618 return target;
00619
00620 target = [self _targetForWindow:[self mainWindow] action:anAction];
00621
00622 if (target)
00623 return target;
00624
00625 if ([self respondsToSelector:anAction])
00626 return self;
00627
00628 if ([_delegate respondsToSelector:anAction])
00629 return _delegate;
00630
00631 if ([_documentController respondsToSelector:anAction])
00632 return _documentController;
00633
00634 return nil;
00635 }
00636
00637 - (void)setCallback:(Function)aCallback forNextEventMatchingMask:(unsigned int)aMask untilDate:(CPDate)anExpiration inMode:(CPString)aMode dequeue:(BOOL)shouldDequeue
00638 {
00639 _eventListeners.push(_CPEventListenerMake(aMask, aCallback));
00640
00641 if (_eventListeners.length == 3) objj_debug_print_backtrace();
00642 }
00643
00644 - (CPEvent)setTarget:(id)aTarget selector:(SEL)aSelector forNextEventMatchingMask:(unsigned int)aMask untilDate:(CPDate)anExpiration inMode:(CPString)aMode dequeue:(BOOL)shouldDequeue
00645 {
00646 _eventListeners.push(_CPEventListenerMake(aMask, function (anEvent) { objj_msgSend(aTarget, aSelector, anEvent); }));
00647 }
00648
00649
00650
00659 - (void)beginSheet:(CPWindow)aSheet modalForWindow:(CPWindow)aWindow modalDelegate:(id)aModalDelegate didEndSelector:(SEL)aDidEndSelector contextInfo:(id)aContextInfo
00660 {
00661 [aWindow _attachSheet:aSheet modalDelegate:aModalDelegate didEndSelector:aDidEndSelector contextInfo:aContextInfo];
00662 }
00663
00664 - (CPArray)arguments
00665 {
00666 return _args;
00667 }
00668
00669 - (CPDictionary)namedArguments
00670 {
00671 return _namedArgs;
00672 }
00673
00674 @end
00675
00676 var _CPModalSessionMake = function(aWindow, aStopCode)
00677 {
00678 return { _window:aWindow, _state:CPRunContinuesResponse , _previous:nil };
00679 }
00680
00681 var _CPEventListenerMake = function(anEventMask, aCallback)
00682 {
00683 return { _mask:anEventMask, _callback:aCallback };
00684 }
00685
00686 var _CPRunModalLoop = function(anEvent)
00687 {
00688 [CPApp setCallback:_CPRunModalLoop forNextEventMatchingMask:CPAnyEventMask untilDate:nil inMode:0 dequeue:NO];
00689
00690
00691 var theWindow = [anEvent window],
00692 modalSession = CPApp._currentSession;
00693
00694 if (theWindow == modalSession._window || [theWindow worksWhenModal])
00695 [theWindow sendEvent:anEvent];
00696
00697
00698
00699 if (modalSession._state != CPRunContinuesResponse)
00700 [CPApp _removeRunModalLoop];
00701 }
00702
00709 function CPApplicationMain(args, namedArgs)
00710 {
00711 var mainBundle = [CPBundle mainBundle],
00712 principalClass = [mainBundle principalClass];
00713
00714 if (!principalClass)
00715 principalClass = [CPApplication class];
00716
00717 [principalClass sharedApplication];
00718
00719
00720
00721
00722 if (!args && !namedArgs)
00723 {
00724 var args = window.location.hash.replace("#", "").split("/").slice(0),
00725 searchParams = window.location.search.substring(1).split("&");
00726 namedArgs = [CPDictionary dictionary];
00727
00728 for(var i=0, count = args.length; i<count; i++)
00729 args[i] = decodeURIComponent(args[i]);
00730
00731 if([args containsObject:"debug"])
00732 CPLogRegister(CPLogPopup);
00733
00734 for(var i=0; i<searchParams.length; i++)
00735 {
00736 var index = searchParams[i].indexOf('=');
00737 if(index == -1)
00738 [namedArgs setObject: "" forKey:searchParams[i]];
00739 else
00740 [namedArgs setObject: searchParams[i].substring(index+1) forKey: searchParams[i].substring(0, index)];
00741 }
00742 }
00743
00744 CPApp._args = args;
00745 CPApp._namedArgs = namedArgs;
00746
00747 [CPApp run];
00748 }