API  0.9.6
 All Classes Files Functions Variables Macros Groups Pages
CPApplication.j
Go to the documentation of this file.
1 /*
2  * CPApplication.j
3  * AppKit
4  *
5  * Created by Francisco Tolmasky.
6  * Copyright 2008, 280 North, Inc.
7  *
8  * This library is free software; you can redistribute it and/or
9  * modify it under the terms of the GNU Lesser General Public
10  * License as published by the Free Software Foundation; either
11  * version 2.1 of the License, or (at your option) any later version.
12  *
13  * This library is distributed in the hope that it will be useful,
14  * but WITHOUT ANY WARRANTY; without even the implied warranty of
15  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
16  * Lesser General Public License for more details.
17  *
18  * You should have received a copy of the GNU Lesser General Public
19  * License along with this library; if not, write to the Free Software
20  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
21  */
22 
23 #import "../Foundation/CPRange.h"
24 
25 
26 
27 
28 var CPMainCibFile = @"CPMainCibFile",
29  CPMainCibFileHumanFriendly = @"Main cib file base name",
31 
32 CPApp = nil;
33 
34 CPApplicationWillFinishLaunchingNotification = @"CPApplicationWillFinishLaunchingNotification";
35 CPApplicationDidFinishLaunchingNotification = @"CPApplicationDidFinishLaunchingNotification";
36 CPApplicationWillTerminateNotification = @"CPApplicationWillTerminateNotification";
37 CPApplicationWillBecomeActiveNotification = @"CPApplicationWillBecomeActiveNotification";
38 CPApplicationDidBecomeActiveNotification = @"CPApplicationDidBecomeActiveNotification";
39 CPApplicationWillResignActiveNotification = @"CPApplicationWillResignActiveNotification";
40 CPApplicationDidResignActiveNotification = @"CPApplicationDidResignActiveNotification";
41 
44 CPTerminateLater = -1; // not currently supported
45 
49 
75 @implementation CPApplication : CPResponder
76 {
77  CPArray _eventListeners;
78 
79  CPEvent _currentEvent;
80  CPWindow _lastMouseMoveWindow;
81 
82  CPArray _windows;
83  CPWindow _keyWindow;
84  CPWindow _mainWindow;
85  CPWindow _previousKeyWindow;
86  CPWindow _previousMainWindow;
87 
88  CPDocumentController _documentController;
89 
90  CPModalSession _currentSession;
91 
92  //
93  id _delegate;
94  BOOL _finishedLaunching;
95  BOOL _isActive;
96 
97  CPDictionary _namedArgs;
98  CPArray _args;
99  CPString _fullArgsString;
100 
101  CPImage _applicationIconImage;
102 
103  CPPanel _aboutPanel;
104 
105  CPThemeBlend _themeBlend;
106 }
107 
113 + (CPApplication)sharedApplication
114 {
115  if (!CPApp)
116  CPApp = [[CPApplication alloc] init];
117 
118  return CPApp;
119 }
120 
126 - (id)init
127 {
128  self = [super init];
129 
130  CPApp = self;
131 
132  if (self)
133  {
134  _eventListeners = [];
135 
136  _windows = [[CPNull null]];
137  }
138 
139  return self;
140 }
141 
142 // Configuring Applications
143 
150 - (void)setDelegate:(id)aDelegate
151 {
152  if (_delegate == aDelegate)
153  return;
154 
155  var defaultCenter = [CPNotificationCenter defaultCenter],
156  delegateNotifications =
157  [
158  CPApplicationWillFinishLaunchingNotification, @selector(applicationWillFinishLaunching:),
159  CPApplicationDidFinishLaunchingNotification, @selector(applicationDidFinishLaunching:),
160  CPApplicationWillBecomeActiveNotification, @selector(applicationWillBecomeActive:),
161  CPApplicationDidBecomeActiveNotification, @selector(applicationDidBecomeActive:),
162  CPApplicationWillResignActiveNotification, @selector(applicationWillResignActive:),
163  CPApplicationDidResignActiveNotification, @selector(applicationDidResignActive:),
164  CPApplicationWillTerminateNotification, @selector(applicationWillTerminate:)
165  ],
166  count = [delegateNotifications count];
167 
168  if (_delegate)
169  {
170  var index = 0;
171 
172  for (; index < count; index += 2)
173  {
174  var notificationName = delegateNotifications[index],
175  selector = delegateNotifications[index + 1];
176 
177  if ([_delegate respondsToSelector:selector])
178  [defaultCenter removeObserver:_delegate name:notificationName object:self];
179  }
180  }
181 
182  _delegate = aDelegate;
183 
184  var index = 0;
185 
186  for (; index < count; index += 2)
187  {
188  var notificationName = delegateNotifications[index],
189  selector = delegateNotifications[index + 1];
190 
191  if ([_delegate respondsToSelector:selector])
192  [defaultCenter addObserver:_delegate selector:selector name:notificationName object:self];
193  }
194 }
195 
199 - (id)delegate
200 {
201  return _delegate;
202 }
203 
210 - (void)finishLaunching
211 {
212  // At this point we clear the window.status to eliminate Safari's "Cancelled" error message
213  // The message shouldn't be displayed, because only an XHR is cancelled, but it is a usability issue.
214  // We do it here so that applications can change it in willFinish or didFinishLaunching
215 #if PLATFORM(DOM)
216  window.status = " ";
217 #endif
218 
219  // We also want to set the default cursor on the body, so that buttons and things don't have an iBeam
221 
222  var bundle = [CPBundle mainBundle],
223  delegateClassName = [bundle objectForInfoDictionaryKey:@"CPApplicationDelegateClass"];
224 
225  if (delegateClassName)
226  {
227  var delegateClass = objj_getClass(delegateClassName);
228 
229  if (delegateClass)
230  [self setDelegate:[[delegateClass alloc] init]];
231  }
232 
233  var defaultCenter = [CPNotificationCenter defaultCenter];
234 
235  [defaultCenter
236  postNotificationName:CPApplicationWillFinishLaunchingNotification
237  object:self];
238 
239  var types = [bundle objectForInfoDictionaryKey:@"CPBundleDocumentTypes"];
240 
241  if ([types count] > 0)
242  _documentController = [CPDocumentController sharedDocumentController];
243 
244  var needsUntitled = !!_documentController,
245  URLStrings = nil;
246 
247 #if PLATFORM(DOM)
248  URLStrings = window.cpOpeningURLStrings && window.cpOpeningURLStrings();
249 #endif
250 
251  var index = 0,
252  count = [URLStrings count];
253 
254  for (; index < count; ++index)
255  needsUntitled = ![self _openURL:[CPURL URLWithString:URLStrings[index]]] && needsUntitled;
256 
257  if (needsUntitled && [_delegate respondsToSelector:@selector(applicationShouldOpenUntitledFile:)])
258  needsUntitled = [_delegate applicationShouldOpenUntitledFile:self];
259 
260  if (needsUntitled)
261  [_documentController newDocument:self];
262 
263  [_documentController _updateRecentDocumentsMenu];
264 
265  [defaultCenter
266  postNotificationName:CPApplicationDidFinishLaunchingNotification
267  object:self];
268 
269  [[CPRunLoop currentRunLoop] limitDateForMode:CPDefaultRunLoopMode];
270 
271  _finishedLaunching = YES;
272 }
273 
274 - (void)terminate:(id)aSender
275 {
277  postNotificationName:CPApplicationWillTerminateNotification
278  object:self];
279 
280  if (![CPPlatform isBrowser])
281  {
282  [[CPDocumentController sharedDocumentController] closeAllDocumentsWithDelegate:self
283  didCloseAllSelector:@selector(_documentController:didCloseAll:context:)
284  contextInfo:nil];
285  }
286  else
287  {
288  [[[self keyWindow] platformWindow] _propagateCurrentDOMEvent:YES];
289  }
290 }
291 
298 - (void)setApplicationIconImage:(CPImage)anImage
299 {
300  _applicationIconImage = anImage;
301 }
302 
307 - (CPImage)applicationIconImage
308 {
309  if (_applicationIconImage)
310  return _applicationIconImage;
311 
312  var imagePath = [[CPBundle mainBundle] objectForInfoDictionaryKey:@"CPApplicationIcon"];
313  if (imagePath)
314  _applicationIconImage = [[CPImage alloc] initWithContentsOfFile:imagePath];
315 
316  return _applicationIconImage;
317 }
318 
322 - (void)orderFrontStandardAboutPanel:(id)sender
323 {
325 }
326 
351 - (void)orderFrontStandardAboutPanelWithOptions:(CPDictionary)options
352 {
353  if (!_aboutPanel)
354  {
355  var mainInfo = [[CPBundle mainBundle] infoDictionary],
356  applicationTitle = [options objectForKey:"ApplicationName"] || [mainInfo objectForKey:@"CPBundleName"],
357  applicationIcon = [options objectForKey:@"ApplicationIcon"] || [self applicationIconImage],
358  version = [options objectForKey:@"Version"] || [mainInfo objectForKey:@"CPBundleVersion"],
359  applicationVersion = [options objectForKey:@"ApplicationVersion"] || [mainInfo objectForKey:@"CPBundleShortVersionString"],
360  copyright = [options objectForKey:@"Copyright"] || [mainInfo objectForKey:@"CPHumanReadableCopyright"];
361 
362  var aboutPanelPath = [[CPBundle bundleForClass:[CPWindowController class]] pathForResource:@"AboutPanel.cib"],
363  aboutPanelController = [CPWindowController alloc],
364  aboutPanelController = [aboutPanelController initWithWindowCibPath:aboutPanelPath owner:aboutPanelController],
365  aboutPanel = [aboutPanelController window],
366  contentView = [aboutPanel contentView],
367  imageView = [contentView viewWithTag:1],
368  applicationLabel = [contentView viewWithTag:2],
369  versionLabel = [contentView viewWithTag:3],
370  copyrightLabel = [contentView viewWithTag:4],
371  standardPath = [[CPBundle bundleForClass:[self class]] pathForResource:@"standardApplicationIcon.png"];
372 
373  // FIXME move this into the CIB eventually
374  [applicationLabel setFont:[CPFont boldSystemFontOfSize:[CPFont systemFontSize] + 2]];
375  [applicationLabel setAlignment:CPCenterTextAlignment];
376  [versionLabel setAlignment:CPCenterTextAlignment];
377  [copyrightLabel setAlignment:CPCenterTextAlignment];
378 
379  [imageView setImage:applicationIcon || [[CPImage alloc] initWithContentsOfFile:standardPath
380  size:CGSizeMake(256, 256)]];
381 
382  [applicationLabel setStringValue:applicationTitle || ""];
383 
384  if (applicationVersion && version)
385  [versionLabel setStringValue:@"Version " + applicationVersion + " (" + version + ")"];
386  else if (applicationVersion || version)
387  [versionLabel setStringValue:@"Version " + (applicationVersion || version)];
388  else
389  [versionLabel setStringValue:@""];
390 
391  [copyrightLabel setStringValue:copyright || ""];
392  [aboutPanel center];
393 
394  _aboutPanel = aboutPanel;
395  }
396 
397  [_aboutPanel orderFront:self];
398 }
399 
400 
401 - (void)_documentController:(NSDocumentController *)docController didCloseAll:(BOOL)didCloseAll context:(Object)info
402 {
403  // callback method for terminate:
404  if (didCloseAll)
405  {
406  if ([_delegate respondsToSelector:@selector(applicationShouldTerminate:)])
407  [self replyToApplicationShouldTerminate:[_delegate applicationShouldTerminate:self]];
408  else
409  [self replyToApplicationShouldTerminate:YES];
410  }
411 }
412 
413 - (void)replyToApplicationShouldTerminate:(BOOL)terminate
414 {
415  if (terminate == CPTerminateNow)
416  {
417  [[CPNotificationCenter defaultCenter] postNotificationName:CPApplicationWillTerminateNotification object:self];
419  }
420 }
421 
422 - (void)activateIgnoringOtherApps:(BOOL)shouldIgnoreOtherApps
423 {
424  [self _willBecomeActive];
425 
426  [CPPlatform activateIgnoringOtherApps:shouldIgnoreOtherApps];
427  _isActive = YES;
428 
429  [self _willResignActive];
430 }
431 
432 - (void)deactivate
433 {
434  [self _willResignActive];
435 
437  _isActive = NO;
438 
439  [self _didResignActive];
440 }
441 
442 - (void)isActive
443 {
444  return _isActive;
445 }
446 
447 - (void)hideOtherApplications:(id)aSender
448 {
450 }
451 
456 - (void)run
457 {
458  [self finishLaunching];
459 }
460 
461 // Managing the Event Loop
466 - (void)runModalForWindow:(CPWindow)aWindow
467 {
468  [self runModalSession:[self beginModalSessionForWindow:aWindow]];
469 }
470 
476 - (void)stopModalWithCode:(int)aCode
477 {
478  if (!_currentSession)
479  {
480  return;
481  // raise exception;
482  }
483 
484  _currentSession._state = aCode;
485  _currentSession = _currentSession._previous;
486 
487 // if (aCode == CPRunAbortedResponse)
488  [self _removeRunModalLoop];
489 }
490 
491 /* @ignore */
492 - (void)_removeRunModalLoop
493 {
494  var count = _eventListeners.length;
495 
496  while (count--)
497  if (_eventListeners[count]._callback === _CPRunModalLoop)
498  {
499  _eventListeners.splice(count, 1);
500 
501  return;
502  }
503 }
504 
508 - (void)stopModal
509 {
510  [self stopModalWithCode:CPRunStoppedResponse]
511 }
512 
516 - (void)abortModal
517 {
518  [self stopModalWithCode:CPRunAbortedResponse];
519 }
520 
525 - (CPModalSession)beginModalSessionForWindow:(CPWindow)aWindow
526 {
527  return _CPModalSessionMake(aWindow, 0);
528 }
529 
534 - (void)runModalSession:(CPModalSession)aModalSession
535 {
536  aModalSession._previous = _currentSession;
537  _currentSession = aModalSession;
538 
539  var theWindow = aModalSession._window;
540 
541  [theWindow center];
542  [theWindow makeKeyWindow];
543  [theWindow orderFront:self];
544 
545 // [theWindow._bridge _obscureWindowsBelowModalWindow];
546 
547  [CPApp setCallback:_CPRunModalLoop forNextEventMatchingMask:CPAnyEventMask untilDate:nil inMode:0 dequeue:NO];
548 }
549 
554 - (CPWindow)modalWindow
555 {
556  if (!_currentSession)
557  return nil;
558 
559  return _currentSession._window;
560 }
561 
562 /* @ignore */
563 - (BOOL)_handleKeyEquivalent:(CPEvent)anEvent
564 {
565  return [[self keyWindow] performKeyEquivalent:anEvent] ||
566  [[self mainMenu] performKeyEquivalent:anEvent];
567 }
568 
573 - (void)sendEvent:(CPEvent)anEvent
574 {
575  _currentEvent = anEvent;
577 
578  var theWindow = [anEvent window];
579 
580 #if PLATFORM(DOM)
581  var willPropagate = [[theWindow platformWindow] _willPropagateCurrentDOMEvent];
582 
583  // temporarily pretend we won't propagate the event. we'll restore the saved value later
584  // we do this outside the if so that changes user code might make in _handleKeyEquiv. are preserved
585  [[theWindow platformWindow] _propagateCurrentDOMEvent:NO];
586 #endif
587 
588  // Check if this is a candidate for key equivalent...
589  if ([anEvent _couldBeKeyEquivalent] && [self _handleKeyEquivalent:anEvent])
590  {
591 #if PLATFORM(DOM)
592  var characters = [anEvent characters],
593  modifierFlags = [anEvent modifierFlags];
594 
595  // Unconditionally propagate on these keys to solve browser copy paste bugs
596  if ((characters == "c" || characters == "x" || characters == "v") && (modifierFlags & CPPlatformActionKeyMask))
597  [[theWindow platformWindow] _propagateCurrentDOMEvent:YES];
598 #endif
599 
600  return;
601  }
602 
603 #if PLATFORM(DOM)
604  // if we make it this far, then restore the original willPropagate value
605  [[theWindow platformWindow] _propagateCurrentDOMEvent:willPropagate];
606 #endif
607 
608  if (_eventListeners.length)
609  {
610  if (_eventListeners[_eventListeners.length - 1]._mask & (1 << [anEvent type]))
611  _eventListeners.pop()._callback(anEvent);
612 
613  return;
614  }
615 
616  if ([anEvent type] == CPMouseMoved)
617  {
618  if (theWindow !== _lastMouseMoveWindow)
619  [_lastMouseMoveWindow _mouseExitedResizeRect];
620 
621  _lastMouseMoveWindow = theWindow;
622  }
623 
624  if (theWindow)
625  [theWindow sendEvent:anEvent];
626 }
627 
632 - (void)doCommandBySelector:(SEL)aSelector
633 {
634  if ([_delegate respondsToSelector:aSelector])
635  [_delegate performSelector:aSelector];
636  else
637  [super doCommandBySelector:aSelector];
638 }
639 
643 - (CPWindow)keyWindow
644 {
645  return _keyWindow;
646 }
647 
651 - (CPWindow)mainWindow
652 {
653  return _mainWindow;
654 }
655 
659 - (CPWindow)windowWithWindowNumber:(int)aWindowNumber
660 {
661  // Never allow _windows[0] to be returned - it's an internal CPNull placeholder.
662  if (!aWindowNumber)
663  return nil;
664 
665  return _windows[aWindowNumber];
666 }
667 
671 - (CPArray)windows
672 {
673  // Return all windows, but not the CPNull placeholder in _windows[0].
674  return [_windows subarrayWithRange:_CPMakeRange(1, [_windows count] - 1)];
675 }
676 
680 - (CPArray)orderedWindows
681 {
682 #if PLATFORM(DOM)
683  return CPWindowObjectList();
684 #else
685  return [];
686 #endif
687 }
688 
689 - (void)hide:(id)aSender
690 {
691  [CPPlatform hide:self];
692 }
693 
694 // Accessing the Main Menu
698 - (CPMenu)mainMenu
699 {
700  return [self menu];
701 }
702 
707 - (void)setMainMenu:(CPMenu)aMenu
708 {
709  [self setMenu:aMenu];
710 }
711 
712 - (void)setMenu:(CPMenu)aMenu
713 {
714  if ([aMenu _menuName] === "CPMainMenu")
715  {
716  if ([self menu] === aMenu)
717  return;
718 
719  [super setMenu:aMenu];
720 
721  if ([CPPlatform supportsNativeMainMenu])
722  window.cpSetMainMenu([self menu]);
723  }
724  else
725  [aMenu _setMenuName:@"CPMainMenu"];
726 }
727 
732 - (void)orderFrontColorPanel:(id)aSender
733 {
735 }
736 
737 // Posting Actions
747 - (BOOL)tryToPerform:(SEL)anAction with:(id)anObject
748 {
749  if (!anAction)
750  return NO;
751 
752  if ([super tryToPerform:anAction with:anObject])
753  return YES;
754 
755  if ([_delegate respondsToSelector:anAction])
756  {
757  [_delegate performSelector:anAction withObject:anObject];
758 
759  return YES;
760  }
761 
762  return NO;
763 }
764 
772 - (BOOL)sendAction:(SEL)anAction to:(id)aTarget from:(id)aSender
773 {
774  var target = [self targetForAction:anAction to:aTarget from:aSender];
775 
776  if (!target)
777  return NO;
778 
779  [target performSelector:anAction withObject:aSender];
780 
781  return YES;
782 }
783 
795 - (id)targetForAction:(SEL)anAction to:(id)aTarget from:(id)aSender
796 {
797  if (!anAction)
798  return nil;
799 
800  if (aTarget)
801  return aTarget;
802 
803  return [self targetForAction:anAction];
804 }
805 
823 - (id)_targetForWindow:(CPWindow)aWindow action:(SEL)anAction
824 {
825  var responder = [aWindow firstResponder],
826  checkWindow = YES;
827 
828  while (responder)
829  {
830  if ([responder respondsToSelector:anAction])
831  return responder;
832 
833  if (responder == aWindow)
834  checkWindow = NO;
835 
836  responder = [responder nextResponder];
837  }
838 
839  if (checkWindow && [aWindow respondsToSelector:anAction])
840  return aWindow;
841 
842  var delegate = [aWindow delegate];
843 
844  if ([delegate respondsToSelector:anAction])
845  return delegate;
846 
847  var windowController = [aWindow windowController];
848 
849  if ([windowController respondsToSelector:anAction])
850  return windowController;
851 
852  var theDocument = [windowController document];
853  if (theDocument !== delegate && [theDocument respondsToSelector:anAction])
854  return theDocument;
855 
856  return nil;
857 }
858 
873 - (id)targetForAction:(SEL)anAction
874 {
875  if (!anAction)
876  return nil;
877 
878  var target = [self _targetForWindow:[self keyWindow] action:anAction];
879 
880  if (target)
881  return target;
882 
883  target = [self _targetForWindow:[self mainWindow] action:anAction];
884 
885  if (target)
886  return target;
887 
888  if ([self respondsToSelector:anAction])
889  return self;
890 
891  if ([_delegate respondsToSelector:anAction])
892  return _delegate;
893 
894  if ([_documentController respondsToSelector:anAction])
895  return _documentController;
896 
897  return nil;
898 }
899 
908 - (void)setCallback:(Function)aCallback forNextEventMatchingMask:(unsigned int)aMask untilDate:(CPDate)anExpiration inMode:(CPString)aMode dequeue:(BOOL)shouldDequeue
909 {
910  _eventListeners.push(_CPEventListenerMake(aMask, aCallback));
911 }
912 
924 - (void)setTarget:(id)aTarget selector:(SEL)aSelector forNextEventMatchingMask:(unsigned int)aMask untilDate:(CPDate)anExpiration inMode:(CPString)aMode dequeue:(BOOL)shouldDequeue
925 {
926  _eventListeners.push(_CPEventListenerMake(aMask, function (anEvent) { objj_msgSend(aTarget, aSelector, anEvent); }));
927 }
928 
932 - (CPEvent)currentEvent
933 {
934  return _currentEvent;
935 }
936 
937 // Managing Sheets
938 
947 - (void)beginSheet:(CPWindow)aSheet modalForWindow:(CPWindow)aWindow modalDelegate:(id)aModalDelegate didEndSelector:(SEL)aDidEndSelector contextInfo:(id)aContextInfo
948 {
949  if ([aWindow isSheet])
950  {
951  [CPException raise:CPInternalInconsistencyException reason:@"The target window of beginSheet: cannot be a sheet"];
952  return;
953  }
954 
955  [aSheet._windowView _enableSheet:YES];
956 
957  // -dw- if a sheet is already visible, we skip this since it serves no purpose and causes
958  // orderOut: to be called on the sheet, which is not what we want.
959  if (![aWindow isVisible])
960  {
961  [aWindow orderFront:self];
962  [aSheet setPlatformWindow:[aWindow platformWindow]];
963  }
964 
965  [aWindow _attachSheet:aSheet modalDelegate:aModalDelegate didEndSelector:aDidEndSelector contextInfo:aContextInfo];
966 }
967 
981 - (void)endSheet:(CPWindow)sheet returnCode:(int)returnCode
982 {
983  var count = [_windows count];
984 
985  while (--count >= 0)
986  {
987  var aWindow = [_windows objectAtIndex:count],
988  context = aWindow._sheetContext;
989 
990  if (context != nil && context["sheet"] === sheet)
991  {
992  context["returnCode"] = returnCode;
993  [aWindow _endSheet];
994  return;
995  }
996  }
997 }
998 
1003 - (void)endSheet:(CPWindow)sheet
1004 {
1005  // FIX ME: this is wrong: by Cocoa this should be: CPRunStoppedResponse.
1006  [self endSheet:sheet returnCode:0];
1007 }
1008 
1024 - (CPArray)arguments
1025 {
1026  // FIXME This should probably not access the window object #if !PLATFORM(DOM), but the unit tests rely on it.
1027  if (window && window.location && _fullArgsString !== window.location.hash)
1028  [self _reloadArguments];
1029 
1030  return _args;
1031 }
1032 
1049 - (void)setArguments:(CPArray)args
1050 {
1051  if (!args || args.length == 0)
1052  {
1053  _args = [];
1054  // Don't use if PLATFORM(DOM) here - the unit test fakes window.location so we should play along.
1055  if (window && window.location)
1056  window.location.hash = @"#";
1057  return;
1058  }
1059 
1060  if (![args isKindOfClass:CPArray])
1061  args = [CPArray arrayWithObject:args];
1062 
1063  _args = args;
1064 
1065  var toEncode = [_args copy];
1066  for (var i = 0, count = toEncode.length; i < count; i++)
1067  toEncode[i] = encodeURIComponent(toEncode[i]);
1068 
1069  var hash = [toEncode componentsJoinedByString:@"/"];
1070 
1071  // Don't use if PLATFORM(DOM) here - the unit test fakes window.location so we should play along.
1072  if (window && window.location)
1073  window.location.hash = @"#" + hash;
1074 }
1075 
1076 - (void)_reloadArguments
1077 {
1078  // FIXME This should probably not access the window object #if !PLATFORM(DOM), but the unit tests rely on it.
1079  _fullArgsString = (window && window.location) ? window.location.hash : "";
1080 
1081  if (_fullArgsString.length)
1082  {
1083  var args = _fullArgsString.substring(1).split("/");
1084 
1085  for (var i = 0, count = args.length; i < count; i++)
1086  args[i] = decodeURIComponent(args[i]);
1087 
1088  _args = args;
1089  }
1090  else
1091  _args = [];
1092 }
1093 
1111 - (CPDictionary)namedArguments
1112 {
1113  return _namedArgs;
1114 }
1115 
1116 - (BOOL)_openURL:(CPURL)aURL
1117 {
1118  if (_delegate && [_delegate respondsToSelector:@selector(application:openFile:)])
1119  {
1120  CPLog.warn("application:openFile: is deprecated, use application:openURL: instead.");
1121  return [_delegate application:self openFile:[aURL absoluteString]];
1122  }
1123 
1124  if (_delegate && [_delegate respondsToSelector:@selector(application:openURL:)])
1125  return [_delegate application:self openURL:aURL];
1126 
1127  return !![_documentController openDocumentWithContentsOfURL:aURL display:YES error:NULL];
1128 }
1129 
1130 - (void)_willBecomeActive
1131 {
1132  [[CPNotificationCenter defaultCenter] postNotificationName:CPApplicationWillBecomeActiveNotification
1133  object:self
1134  userInfo:nil];
1135 }
1136 
1137 - (void)_didBecomeActive
1138 {
1139  if (![self keyWindow] && _previousKeyWindow &&
1140  [[self windows] indexOfObjectIdenticalTo:_previousKeyWindow] !== CPNotFound)
1141  [_previousKeyWindow makeKeyWindow];
1142 
1143  if (![self mainWindow] && _previousMainWindow &&
1144  [[self windows] indexOfObjectIdenticalTo:_previousMainWindow] !== CPNotFound)
1145  [_previousMainWindow makeMainWindow];
1146 
1147  if ([self keyWindow])
1148  [[self keyWindow] orderFront:self];
1149  else if ([self mainWindow])
1150  [[self mainWindow] makeKeyAndOrderFront:self];
1151  else
1152  [[self mainMenu]._menuWindow makeKeyWindow]; //FIXME this may not actually work
1153 
1154  _previousKeyWindow = nil;
1155  _previousMainWindow = nil;
1156 
1157  [[CPNotificationCenter defaultCenter] postNotificationName:CPApplicationDidBecomeActiveNotification
1158  object:self
1159  userInfo:nil];
1160 }
1161 
1162 - (void)_willResignActive
1163 {
1164  [[CPNotificationCenter defaultCenter] postNotificationName:CPApplicationWillResignActiveNotification
1165  object:self
1166  userInfo:nil];
1167 }
1168 
1169 - (void)_didResignActive
1170 {
1171  if (self._activeMenu)
1172  [self._activeMenu cancelTracking];
1173 
1174  if ([self keyWindow])
1175  {
1176  _previousKeyWindow = [self keyWindow];
1177  [_previousKeyWindow resignKeyWindow];
1178  }
1179 
1180  if ([self mainWindow])
1181  {
1182  _previousMainWindow = [self mainWindow];
1183  [_previousMainWindow resignMainWindow];
1184  }
1185 
1186  [[CPNotificationCenter defaultCenter] postNotificationName:CPApplicationDidResignActiveNotification
1187  object:self
1188  userInfo:nil];
1189 }
1190 
1191 + (CPString)defaultThemeName
1192 {
1193  return ([[CPBundle mainBundle] objectForInfoDictionaryKey:"CPDefaultTheme"] || @"Aristo");
1194 }
1195 
1196 @end
1197 
1198 var _CPModalSessionMake = function(aWindow, aStopCode)
1199 {
1200  return { _window:aWindow, _state:CPRunContinuesResponse , _previous:nil };
1201 };
1202 
1203 var _CPEventListenerMake = function(anEventMask, aCallback)
1204 {
1205  return { _mask:anEventMask, _callback:aCallback };
1206 };
1207 
1208 // Make this a global for use in CPPlatformWindow+DOM.j.
1209 _CPRunModalLoop = function(anEvent)
1210 {
1211  [CPApp setCallback:_CPRunModalLoop forNextEventMatchingMask:CPAnyEventMask untilDate:nil inMode:0 dequeue:NO];
1212 
1213  var theWindow = [anEvent window],
1214  modalSession = CPApp._currentSession;
1215 
1216  // The special case for popovers here is not clear. In Cocoa the popover window does not respond YES to worksWhenModal,
1217  // yet it works when there is a modal window. Maybe it starts its own modal session, but interaction with the original
1218  // modal window seems to continue working as well. Regardless of correctness, this solution beats popovers not working
1219  // at all from sheets.
1220  if (theWindow == modalSession._window ||
1221  [theWindow worksWhenModal] ||
1222  [theWindow attachedSheet] == modalSession._window || // -dw- allow modal parent of sheet to be repositioned
1223  ([theWindow isKindOfClass:_CPAttachedWindow] && [[theWindow targetView] window] === modalSession._window))
1224  [theWindow sendEvent:anEvent];
1225 };
1226 
1234 function CPApplicationMain(args, namedArgs)
1235 {
1236 
1237 #if PLATFORM(DOM)
1238  // hook to allow recorder, etc to manipulate things before starting AppKit
1239  if (window.parent !== window && typeof window.parent._childAppIsStarting === "function")
1240  {
1241  try
1242  {
1243  window.parent._childAppIsStarting(window);
1244  }
1245  catch(err)
1246  {
1247  // This could happen if we're in an iframe without access to the parent frame.
1248  CPLog.warn("Failed to call parent frame's _childAppIsStarting().");
1249  }
1250  }
1251 #endif
1252 
1253  var mainBundle = [CPBundle mainBundle],
1254  principalClass = [mainBundle principalClass];
1255 
1256  if (!principalClass)
1257  principalClass = [CPApplication class];
1258 
1259  [principalClass sharedApplication];
1260 
1261  if ([args containsObject:"debug"])
1262  CPLogRegister(CPLogPopup);
1263 
1264  CPApp._args = args;
1265  CPApp._namedArgs = namedArgs;
1266 
1267  [_CPAppBootstrapper performActions];
1268 }
1269 
1270 var _CPAppBootstrapperActions = nil;
1271 @implementation _CPAppBootstrapper : CPObject
1272 {
1273  id __doxygen__;
1274 }
1275 
1276 + (CPArray)actions
1277 {
1278  return [@selector(bootstrapPlatform), @selector(loadDefaultTheme), @selector(loadMainCibFile)];
1279 }
1280 
1281 + (void)performActions
1282 {
1283  if (!_CPAppBootstrapperActions)
1284  _CPAppBootstrapperActions = [self actions];
1285 
1286  while (_CPAppBootstrapperActions.length)
1287  {
1288  var action = _CPAppBootstrapperActions.shift();
1289 
1290  if (objj_msgSend(self, action))
1291  return;
1292  }
1293 
1294  [CPApp run];
1295 }
1296 
1297 + (BOOL)bootstrapPlatform
1298 {
1299  return [CPPlatform bootstrap];
1300 }
1301 
1302 + (BOOL)loadDefaultTheme
1303 {
1304  var defaultThemeName = [CPApplication defaultThemeName],
1305  themeURL = nil;
1306 
1307  if (defaultThemeName === @"Aristo")
1308  themeURL = [[CPBundle bundleForClass:[CPApplication class]] pathForResource:defaultThemeName + @".blend"];
1309  else
1310  themeURL = [[CPBundle mainBundle] pathForResource:defaultThemeName + @".blend"];
1311 
1312  var blend = [[CPThemeBlend alloc] initWithContentsOfURL:themeURL];
1313  [blend loadWithDelegate:self];
1314 
1315  return YES;
1316 }
1317 
1318 + (void)blendDidFinishLoading:(CPThemeBlend)aThemeBlend
1319 {
1322 
1323  [self performActions];
1324 }
1325 
1326 + (BOOL)loadMainCibFile
1327 {
1328  var mainBundle = [CPBundle mainBundle],
1329  mainCibFile = [mainBundle objectForInfoDictionaryKey:CPMainCibFile] || [mainBundle objectForInfoDictionaryKey:CPMainCibFileHumanFriendly];
1330 
1331  if (mainCibFile)
1332  {
1333  [mainBundle loadCibFile:mainCibFile
1334  externalNameTable:[CPDictionary dictionaryWithObject:CPApp forKey:CPCibOwner]
1335  loadDelegate:self];
1336 
1337  return YES;
1338  }
1339  else
1340  [self loadCiblessBrowserMainMenu];
1341 
1342  return NO;
1343 }
1344 
1345 + (void)loadCiblessBrowserMainMenu
1346 {
1347  var mainMenu = [[CPMenu alloc] initWithTitle:@"MainMenu"];
1348 
1349  // FIXME: We should implement autoenabling.
1350  [mainMenu setAutoenablesItems:NO];
1351 
1352  var bundle = [CPBundle bundleForClass:[CPApplication class]],
1353  newMenuItem = [[CPMenuItem alloc] initWithTitle:@"New" action:@selector(newDocument:) keyEquivalent:@"n"];
1354 
1355  [newMenuItem setImage:[[CPImage alloc] initWithContentsOfFile:[bundle pathForResource:@"CPApplication/New.png"] size:CGSizeMake(16.0, 16.0)]];
1356  [newMenuItem setAlternateImage:[[CPImage alloc] initWithContentsOfFile:[bundle pathForResource:@"CPApplication/NewHighlighted.png"] size:CGSizeMake(16.0, 16.0)]];
1357 
1358  [mainMenu addItem:newMenuItem];
1359 
1360  var openMenuItem = [[CPMenuItem alloc] initWithTitle:@"Open" action:@selector(openDocument:) keyEquivalent:@"o"];
1361 
1362  [openMenuItem setImage:[[CPImage alloc] initWithContentsOfFile:[bundle pathForResource:@"CPApplication/Open.png"] size:CGSizeMake(16.0, 16.0)]];
1363  [openMenuItem setAlternateImage:[[CPImage alloc] initWithContentsOfFile:[bundle pathForResource:@"CPApplication/OpenHighlighted.png"] size:CGSizeMake(16.0, 16.0)]];
1364 
1365  [mainMenu addItem:openMenuItem];
1366 
1367  var saveMenu = [[CPMenu alloc] initWithTitle:@"Save"],
1368  saveMenuItem = [[CPMenuItem alloc] initWithTitle:@"Save" action:@selector(saveDocument:) keyEquivalent:nil];
1369 
1370  [saveMenuItem setImage:[[CPImage alloc] initWithContentsOfFile:[bundle pathForResource:@"CPApplication/Save.png"] size:CGSizeMake(16.0, 16.0)]];
1371  [saveMenuItem setAlternateImage:[[CPImage alloc] initWithContentsOfFile:[bundle pathForResource:@"CPApplication/SaveHighlighted.png"] size:CGSizeMake(16.0, 16.0)]];
1372 
1373  [saveMenu addItem:[[CPMenuItem alloc] initWithTitle:@"Save" action:@selector(saveDocument:) keyEquivalent:@"s"]];
1374  [saveMenu addItem:[[CPMenuItem alloc] initWithTitle:@"Save As" action:@selector(saveDocumentAs:) keyEquivalent:nil]];
1375 
1376  [saveMenuItem setSubmenu:saveMenu];
1377 
1378  [mainMenu addItem:saveMenuItem];
1379 
1380  var editMenuItem = [[CPMenuItem alloc] initWithTitle:@"Edit" action:nil keyEquivalent:nil],
1381  editMenu = [[CPMenu alloc] initWithTitle:@"Edit"],
1382 
1383  undoMenuItem = [[CPMenuItem alloc] initWithTitle:@"Undo" action:@selector(undo:) keyEquivalent:CPUndoKeyEquivalent],
1384  redoMenuItem = [[CPMenuItem alloc] initWithTitle:@"Redo" action:@selector(redo:) keyEquivalent:CPRedoKeyEquivalent];
1385 
1386  [undoMenuItem setKeyEquivalentModifierMask:CPUndoKeyEquivalentModifierMask];
1387  [redoMenuItem setKeyEquivalentModifierMask:CPRedoKeyEquivalentModifierMask];
1388 
1389  [editMenu addItem:undoMenuItem];
1390  [editMenu addItem:redoMenuItem];
1391 
1392  [editMenu addItem:[[CPMenuItem alloc] initWithTitle:@"Cut" action:@selector(cut:) keyEquivalent:@"x"]];
1393  [editMenu addItem:[[CPMenuItem alloc] initWithTitle:@"Copy" action:@selector(copy:) keyEquivalent:@"c"]];
1394  [editMenu addItem:[[CPMenuItem alloc] initWithTitle:@"Paste" action:@selector(paste:) keyEquivalent:@"v"]];
1395 
1396  [editMenuItem setSubmenu:editMenu];
1397  [editMenuItem setHidden:YES];
1398 
1399  [mainMenu addItem:editMenuItem];
1400 
1401  [mainMenu addItem:[CPMenuItem separatorItem]];
1402 
1403  [CPApp setMainMenu:mainMenu];
1404 }
1405 
1406 + (void)cibDidFinishLoading:(CPCib)aCib
1407 {
1408  [self performActions];
1409 }
1410 
1411 + (void)cibDidFailToLoad:(CPCib)aCib
1412 {
1413  throw new Error("Could not load main cib file (Did you forget to nib2cib it?).");
1414 }
1415 
1416 + (void)reset
1417 {
1418  _CPAppBootstrapperActions = nil;
1419 }
1420 
1421 @end
1422 
1423 
1425 
1429 + (unsigned)modifierFlags
1430 {
1431  return CPEventModifierFlags;
1432 }
1433 
1434 @end
1435 
1437 
1441 - (CPThemeBlend)themeBlend
1442 {
1443  return _themeBlend;
1444 }
1445 
1449 - (void)setThemeBlend:(CPThemeBlend)aValue
1450 {
1451  _themeBlend = aValue;
1452 }
1453 
1454 @end