API  1.0.0
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 
24 
25 @typedef CPModalSession
26 
27 var CPMainCibFile = @"CPMainCibFile",
28  CPMainCibFileHumanFriendly = @"Main cib file base name",
30 
31 
32 @protocol CPApplicationDelegate <CPObject>
33 
34 @optional
35 - (CPApplicationTerminateReply)applicationShouldTerminate:(CPApplication)sender;
36 - (CPString)applicationShouldTerminateMessage:(CPApplication)sender;
37 - (void)applicationDidBecomeActive:(CPNotification)aNotification;
38 - (void)applicationDidChangeScreenParameters:(CPNotification)aNotification;
39 - (void)applicationDidFinishLaunching:(CPNotification)aNotification;
40 - (void)applicationDidResignActive:(CPNotification)aNotification;
41 - (void)applicationWillBecomeActive:(CPNotification)aNotification;
42 - (void)applicationWillFinishLaunching:(CPNotification)aNotification;
43 - (void)applicationWillResignActive:(CPNotification)aNotification;
44 - (void)applicationWillTerminate:(CPNotification)aNotification;
45 
46 @end
47 
50 
75 @implementation CPApplication : CPResponder
76 {
77  CPArray _eventListeners;
78  int _eventListenerInsertionIndex;
79 
80  CPEvent _currentEvent;
81  CPWindow _lastMouseMoveWindow;
82 
83  CPArray _windows;
84  CPWindow _keyWindow;
85  CPWindow _mainWindow;
86  CPWindow _previousKeyWindow;
87  CPWindow _previousMainWindow;
88 
89  CPDocumentController _documentController;
90 
91  CPModalSession _currentSession;
92 
93  //
94  id <CPApplicationDelegate> _delegate;
95  CPInteger _implementedDelegateMethods;
96 
97  BOOL _finishedLaunching;
98  BOOL _isActive;
99 
100  CPDictionary _namedArgs;
101  CPArray _args;
102  CPString _fullArgsString;
103 
104  CPImage _applicationIconImage;
105 
106  CPPanel _aboutPanel;
107 
108  CPThemeBlend _themeBlend;
109 }
110 
116 + (CPApplication)sharedApplication
117 {
118  if (!CPApp)
119  CPApp = [[CPApplication alloc] init];
120 
121  return CPApp;
122 }
123 
129 - (id)init
130 {
131  self = [super init];
132 
133  CPApp = self;
134 
135  if (self)
136  {
137  _eventListeners = [];
138  _eventListenerInsertionIndex = 0;
139 
140  _windows = [[CPNull null]];
141  }
142 
143  return self;
144 }
145 
146 // Configuring Applications
147 
154 - (void)setDelegate:(id <CPApplicationDelegate>)aDelegate
155 {
156  if (_delegate == aDelegate)
157  return;
158 
159  _implementedDelegateMethods = 0;
160 
161  var defaultCenter = [CPNotificationCenter defaultCenter],
162  delegateNotifications =
163  [
164  CPApplicationWillFinishLaunchingNotification, @selector(applicationWillFinishLaunching:),
165  CPApplicationDidFinishLaunchingNotification, @selector(applicationDidFinishLaunching:),
166  CPApplicationWillBecomeActiveNotification, @selector(applicationWillBecomeActive:),
167  CPApplicationDidBecomeActiveNotification, @selector(applicationDidBecomeActive:),
168  CPApplicationWillResignActiveNotification, @selector(applicationWillResignActive:),
169  CPApplicationDidResignActiveNotification, @selector(applicationDidResignActive:),
170  CPApplicationWillTerminateNotification, @selector(applicationWillTerminate:),
171  CPApplicationDidChangeScreenParametersNotification, @selector(applicationDidChangeScreenParameters:)
172  ],
173  count = [delegateNotifications count];
174 
175  if (_delegate)
176  {
177  var index = 0;
178 
179  for (; index < count; index += 2)
180  {
181  var notificationName = delegateNotifications[index],
182  selector = delegateNotifications[index + 1];
183 
184  if ([_delegate respondsToSelector:selector])
185  [defaultCenter removeObserver:_delegate name:notificationName object:self];
186  }
187  }
188 
189  _delegate = aDelegate;
190 
191  var index = 0;
192 
193  for (; index < count; index += 2)
194  {
195  var notificationName = delegateNotifications[index],
196  selector = delegateNotifications[index + 1];
197 
198  if ([_delegate respondsToSelector:selector])
199  [defaultCenter addObserver:_delegate selector:selector name:notificationName object:self];
200  }
201 
202  if ([_delegate respondsToSelector:@selector(applicationShouldTerminate:)])
203  _implementedDelegateMethods |= CPApplicationDelegate_applicationShouldTerminate_;
204 
205  if ([_delegate respondsToSelector:@selector(applicationShouldTerminateMessage:)])
206  _implementedDelegateMethods |= CPApplicationDelegate_applicationShouldTerminateMessage_
207 }
208 
212 - (id)delegate
213 {
214  return _delegate;
215 }
216 
223 - (void)finishLaunching
224 {
225  // At this point we clear the window.status to eliminate Safari's "Cancelled" error message
226  // The message shouldn't be displayed, because only an XHR is cancelled, but it is a usability issue.
227  // We do it here so that applications can change it in willFinish or didFinishLaunching
228 #if PLATFORM(DOM)
229  window.status = " ";
230 #endif
231 
232  // We also want to set the default cursor on the body, so that buttons and things don't have an iBeam
234 
235  var bundle = [CPBundle mainBundle],
236  delegateClassName = [bundle objectForInfoDictionaryKey:@"CPApplicationDelegateClass"];
237 
238  if (delegateClassName)
239  {
240  var delegateClass = objj_getClass(delegateClassName);
241 
242  if (delegateClass)
243  [self setDelegate:[[delegateClass alloc] init]];
244  }
245 
246  var defaultCenter = [CPNotificationCenter defaultCenter];
247 
248  [defaultCenter
249  postNotificationName:CPApplicationWillFinishLaunchingNotification
250  object:self];
251 
252  var types = [bundle objectForInfoDictionaryKey:@"CPBundleDocumentTypes"];
253 
254  if ([types count] > 0)
255  _documentController = [CPDocumentController sharedDocumentController];
256 
257  var needsUntitled = !!_documentController,
258  URLStrings = nil;
259 
260 #if PLATFORM(DOM)
261  URLStrings = window.cpOpeningURLStrings && window.cpOpeningURLStrings();
262 #endif
263 
264  var index = 0,
265  count = [URLStrings count];
266 
267  for (; index < count; ++index)
268  needsUntitled = ![self _openURL:[CPURL URLWithString:URLStrings[index]]] && needsUntitled;
269 
270  if (needsUntitled && [_delegate respondsToSelector:@selector(applicationShouldOpenUntitledFile:)])
271  needsUntitled = [_delegate applicationShouldOpenUntitledFile:self];
272 
273  if (needsUntitled)
274  [_documentController newDocument:self];
275 
276  [_documentController _updateRecentDocumentsMenu];
277 
278  [defaultCenter
279  postNotificationName:CPApplicationDidFinishLaunchingNotification
280  object:self];
281 
282  [[CPRunLoop currentRunLoop] limitDateForMode:CPDefaultRunLoopMode];
283 
284  _finishedLaunching = YES;
285 }
286 
287 - (void)terminate:(id)aSender
288 {
290  postNotificationName:CPApplicationWillTerminateNotification
291  object:self];
292 
293  if (![CPPlatform isBrowser])
294  {
295  [[CPDocumentController sharedDocumentController] closeAllDocumentsWithDelegate:self
296  didCloseAllSelector:@selector(_documentController:didCloseAll:context:)
297  contextInfo:nil];
298  }
299  else
300  {
301  [[[self keyWindow] platformWindow] _propagateCurrentDOMEvent:YES];
302  }
303 }
304 
311 - (void)setApplicationIconImage:(CPImage)anImage
312 {
313  _applicationIconImage = anImage;
314 }
315 
320 - (CPImage)applicationIconImage
321 {
322  if (_applicationIconImage)
323  return _applicationIconImage;
324 
325  var imagePath = [[CPBundle mainBundle] objectForInfoDictionaryKey:@"CPApplicationIcon"];
326  if (imagePath)
327  _applicationIconImage = [[CPImage alloc] initWithContentsOfFile:imagePath];
328 
329  return _applicationIconImage;
330 }
331 
335 - (void)orderFrontStandardAboutPanel:(id)sender
336 {
338 }
339 
364 - (void)orderFrontStandardAboutPanelWithOptions:(CPDictionary)options
365 {
366  if (!_aboutPanel)
367  {
368  var mainInfo = [[CPBundle mainBundle] infoDictionary],
369  applicationTitle = [options objectForKey:"ApplicationName"] || [mainInfo objectForKey:@"CPBundleName"],
370  applicationIcon = [options objectForKey:@"ApplicationIcon"] || [self applicationIconImage],
371  version = [options objectForKey:@"Version"] || [mainInfo objectForKey:@"CPBundleVersion"],
372  applicationVersion = [options objectForKey:@"ApplicationVersion"] || [mainInfo objectForKey:@"CPBundleShortVersionString"],
373  copyright = [options objectForKey:@"Copyright"] || [mainInfo objectForKey:@"CPHumanReadableCopyright"];
374 
375  var windowWidth = 275,
376  windowHeight = 223,
377  imgWidth = 100,
378  imgHeight = 100,
379  interField = 8,
380  aboutPanel = [[CPWindow alloc] initWithContentRect:CGRectMake(0, 0, windowWidth, windowHeight) styleMask:CPClosableWindowMask],
381  imageView = [[CPImageView alloc] initWithFrame:CGRectMake((windowWidth / 2) - (imgWidth / 2), interField, imgWidth, imgHeight)],
382  applicationLabel = [[CPTextField alloc] initWithFrame:CGRectMake(17, imgHeight + 16, windowWidth - 34, 24)],
383  versionLabel = [[CPTextField alloc] initWithFrame:CGRectMake(17, imgHeight + 48, windowWidth - 34, 16)],
384  copyrightLabel = [[CPTextField alloc] initWithFrame:CGRectMake(17, imgHeight + 72, windowWidth - 34, 32)],
385  contentView = [aboutPanel contentView];
386 
387  [applicationLabel setFont:[CPFont boldSystemFontOfSize:[CPFont systemFontSize] + 2]];
388  [applicationLabel setAlignment:CPCenterTextAlignment];
389  [versionLabel setFont:[CPFont systemFontOfSize:[CPFont systemFontSize] - 1]];
390  [versionLabel setAlignment:CPCenterTextAlignment];
391  [copyrightLabel setFont:[CPFont systemFontOfSize:[CPFont systemFontSize] - 1]];
392  [copyrightLabel setAlignment:CPCenterTextAlignment];
393  [copyrightLabel setLineBreakMode:CPLineBreakByWordWrapping];
394 
395  [contentView addSubview:imageView];
396  [contentView addSubview:applicationLabel];
397  [contentView addSubview:versionLabel];
398  [contentView addSubview:copyrightLabel];
399 
400  var standardPath = [[CPBundle bundleForClass:[self class]] pathForResource:@"standardApplicationIcon.png"];
401 
402  [imageView setImage:applicationIcon || [[CPImage alloc] initWithContentsOfFile:standardPath
403  size:CGSizeMake(256, 256)]];
404 
405  [applicationLabel setStringValue:applicationTitle || ""];
406 
407  if (applicationVersion && version)
408  [versionLabel setStringValue:@"Version " + applicationVersion + " (" + version + ")"];
409  else if (applicationVersion || version)
410  [versionLabel setStringValue:@"Version " + (applicationVersion || version)];
411  else
412  [versionLabel setStringValue:@""];
413 
414  [copyrightLabel setStringValue:copyright || @""];
415  [aboutPanel center];
416 
417  _aboutPanel = aboutPanel;
418  }
419 
420  [_aboutPanel orderFront:self];
421 }
422 
423 
424 - (void)_documentController:(CPDocumentController)docController didCloseAll:(BOOL)didCloseAll context:(Object)info
425 {
426  // callback method for terminate:
427  if (didCloseAll)
428  [self replyToApplicationShouldTerminate:[self _sendDelegateApplicationShouldTerminate]];
429 }
430 
431 - (void)replyToApplicationShouldTerminate:(BOOL)terminate
432 {
433  if (terminate == CPTerminateNow)
434  {
435  [[CPNotificationCenter defaultCenter] postNotificationName:CPApplicationWillTerminateNotification object:self];
437  }
438 }
439 
440 - (void)activateIgnoringOtherApps:(BOOL)shouldIgnoreOtherApps
441 {
442  if (_isActive)
443  return;
444 
445  [self _willBecomeActive];
446 
447  [CPPlatform activateIgnoringOtherApps:shouldIgnoreOtherApps];
448  _isActive = YES;
449 
450  [self _didBecomeActive];
451 }
452 
453 - (void)deactivate
454 {
455  if (!_isActive)
456  return;
457 
458  [self _willResignActive];
459 
461  _isActive = NO;
462 
463  [self _didResignActive];
464 }
465 
466 - (void)isActive
467 {
468  return _isActive;
469 }
470 
471 - (void)hideOtherApplications:(id)aSender
472 {
474 }
475 
480 - (void)run
481 {
482  [self finishLaunching];
483  [self sendEvent:[CPEvent otherEventWithType:CPAppKitDefined
484  location:CGPointMakeZero()
485  modifierFlags:0
487  windowNumber:[_keyWindow windowNumber]
488  context:nil
489  subtype:nil
490  data1:nil
491  data2:nil]];
492 }
493 
494 // Managing the Event Loop
499 - (void)runModalForWindow:(CPWindow)aWindow
500 {
501  [self runModalSession:[self beginModalSessionForWindow:aWindow]];
502 }
503 
509 - (void)stopModalWithCode:(int)aCode
510 {
511  if (!_currentSession)
512  {
513  return;
514  // raise exception;
515  }
516 
517  _currentSession._state = aCode;
518  _currentSession = _currentSession._previous;
519 
520 // if (aCode == CPRunAbortedResponse)
521  [self _removeRunModalLoop];
522 }
523 
524 /* @ignore */
525 - (void)_removeRunModalLoop
526 {
527  var count = _eventListeners.length;
528 
529  while (count--)
530  if (_eventListeners[count]._callback === _CPRunModalLoop)
531  {
532  _eventListeners.splice(count, 1);
533  if (count <= _eventListenerInsertionIndex)
534  _eventListenerInsertionIndex--;
535 
536  return;
537  }
538 }
539 
543 - (void)stopModal
544 {
545  [self stopModalWithCode:CPRunStoppedResponse]
546 }
547 
551 - (void)abortModal
552 {
553  [self stopModalWithCode:CPRunAbortedResponse];
554 }
555 
560 - (CPModalSession)beginModalSessionForWindow:(CPWindow)aWindow
561 {
562  return _CPModalSessionMake(aWindow, 0);
563 }
564 
569 - (void)runModalSession:(CPModalSession)aModalSession
570 {
571  aModalSession._previous = _currentSession;
572  _currentSession = aModalSession;
573 
574  var theWindow = aModalSession._window;
575 
576  [theWindow center];
577  [theWindow makeKeyWindow];
578  [theWindow orderFront:self];
579 
580 // [theWindow._bridge _obscureWindowsBelowModalWindow];
581 
582  [CPApp setCallback:_CPRunModalLoop forNextEventMatchingMask:CPAnyEventMask untilDate:nil inMode:0 dequeue:YES];
583 }
584 
589 - (CPWindow)modalWindow
590 {
591  if (!_currentSession)
592  return nil;
593 
594  return _currentSession._window;
595 }
596 
597 /* @ignore */
598 - (BOOL)_handleKeyEquivalent:(CPEvent)anEvent
599 {
600  return [[self keyWindow] performKeyEquivalent:anEvent] ||
601  [[self mainMenu] performKeyEquivalent:anEvent];
602 }
603 
608 - (void)sendEvent:(CPEvent)anEvent
609 {
610  _currentEvent = anEvent;
612 
613  var theWindow = [anEvent window];
614 
615  if ([anEvent type] == CPMouseMoved)
616  {
617  if (theWindow !== _lastMouseMoveWindow)
618  [_lastMouseMoveWindow _mouseExitedResizeRect];
619 
620  _lastMouseMoveWindow = theWindow;
621  }
622 
623  /*
624  Event listeners are processed from back to front so that newer event listeners normally take
625  precedence. If during the execution of a callback a new event listener is added, it should
626  be inserted after the current callback but before any higher priority callbacks. This makes
627  repeating event listeners (those that reinsert themselves) stable relative to each other.
628  */
629  for (var i = _eventListeners.length - 1; i >= 0; i--)
630  {
631  var listener = _eventListeners[i];
632 
633  if (listener._mask & (1 << [anEvent type]))
634  {
635  _eventListeners.splice(i, 1);
636  // In case the callback wants to add more listeners.
637  _eventListenerInsertionIndex = i;
638  listener._callback(anEvent);
639 
640  if (listener._dequeue)
641  {
642  // Don't process the event normally and don't send it to any other listener.
643  _eventListenerInsertionIndex = _eventListeners.length;
644  return;
645  }
646  }
647  }
648 
649  _eventListenerInsertionIndex = _eventListeners.length;
650 
651  // Check if this is a candidate for key equivalent...
652  if ([anEvent _couldBeKeyEquivalent] && [self _handleKeyEquivalent:anEvent])
653  // The key equivalent was handled.
654  return;
655 
656  if (theWindow)
657  [theWindow sendEvent:anEvent];
658 }
659 
664 - (void)doCommandBySelector:(SEL)aSelector
665 {
666  if ([_delegate respondsToSelector:aSelector])
667  [_delegate performSelector:aSelector];
668  else
669  [super doCommandBySelector:aSelector];
670 }
671 
675 - (CPWindow)keyWindow
676 {
677  return _keyWindow;
678 }
679 
683 - (CPWindow)mainWindow
684 {
685  return _mainWindow;
686 }
687 
691 - (CPWindow)windowWithWindowNumber:(int)aWindowNumber
692 {
693  // Never allow _windows[0] to be returned - it's an internal CPNull placeholder.
694  if (!aWindowNumber)
695  return nil;
696 
697  return _windows[aWindowNumber];
698 }
699 
703 - (CPArray)windows
704 {
705  // Return all windows, but not the CPNull placeholder in _windows[0].
706  return [_windows subarrayWithRange:CPMakeRange(1, [_windows count] - 1)];
707 }
708 
712 - (CPArray)orderedWindows
713 {
714 #if PLATFORM(DOM)
715  return CPWindowObjectList();
716 #else
717  return [];
718 #endif
719 }
720 
721 - (void)hide:(id)aSender
722 {
723  [CPPlatform hide:self];
724 }
725 
726 // Accessing the Main Menu
730 - (CPMenu)mainMenu
731 {
732  return [self menu];
733 }
734 
739 - (void)setMainMenu:(CPMenu)aMenu
740 {
741  [self setMenu:aMenu];
742 }
743 
744 - (void)setMenu:(CPMenu)aMenu
745 {
746  if ([aMenu _menuName] === "CPMainMenu")
747  {
748  if ([self menu] === aMenu)
749  return;
750 
751  [super setMenu:aMenu];
752 
753  if ([CPPlatform supportsNativeMainMenu])
754  window.cpSetMainMenu([self menu]);
755  }
756  else
757  [aMenu _setMenuName:@"CPMainMenu"];
758 }
759 
764 - (void)orderFrontColorPanel:(id)aSender
765 {
767 }
768 
769 // Posting Actions
779 - (BOOL)tryToPerform:(SEL)anAction with:(id)anObject
780 {
781  if (!anAction)
782  return NO;
783 
784  if ([super tryToPerform:anAction with:anObject])
785  return YES;
786 
787  if ([_delegate respondsToSelector:anAction])
788  {
789  [_delegate performSelector:anAction withObject:anObject];
790 
791  return YES;
792  }
793 
794  return NO;
795 }
796 
804 - (BOOL)sendAction:(SEL)anAction to:(id)aTarget from:(id)aSender
805 {
806  var target = [self targetForAction:anAction to:aTarget from:aSender];
807 
808  if (!target)
809  return NO;
810 
811  [target performSelector:anAction withObject:aSender];
812 
813  return YES;
814 }
815 
827 - (id)targetForAction:(SEL)anAction to:(id)aTarget from:(id)aSender
828 {
829  if (!anAction)
830  return nil;
831 
832  if (aTarget)
833  return aTarget;
834 
835  return [self targetForAction:anAction];
836 }
837 
855 - (id)_targetForWindow:(CPWindow)aWindow action:(SEL)anAction
856 {
857  var responder = [aWindow firstResponder],
858  checkWindow = YES;
859 
860  while (responder)
861  {
862  if ([responder respondsToSelector:anAction])
863  return responder;
864 
865  if (responder == aWindow)
866  checkWindow = NO;
867 
868  responder = [responder nextResponder];
869  }
870 
871  if (checkWindow && [aWindow respondsToSelector:anAction])
872  return aWindow;
873 
874  var delegate = [aWindow delegate];
875 
876  if ([delegate respondsToSelector:anAction])
877  return delegate;
878 
879  var windowController = [aWindow windowController];
880 
881  if ([windowController respondsToSelector:anAction])
882  return windowController;
883 
884  var theDocument = [windowController document];
885  if (theDocument !== delegate && [theDocument respondsToSelector:anAction])
886  return theDocument;
887 
888  return nil;
889 }
890 
905 - (id)targetForAction:(SEL)anAction
906 {
907  if (!anAction)
908  return nil;
909 
910  var target = [self _targetForWindow:[self keyWindow] action:anAction];
911 
912  if (target)
913  return target;
914 
915  target = [self _targetForWindow:[self mainWindow] action:anAction];
916 
917  if (target)
918  return target;
919 
920  if ([self respondsToSelector:anAction])
921  return self;
922 
923  if ([_delegate respondsToSelector:anAction])
924  return _delegate;
925 
926  if ([_documentController respondsToSelector:anAction])
927  return _documentController;
928 
929  return nil;
930 }
931 
947 - (void)setCallback:(Function)aCallback forNextEventMatchingMask:(unsigned int)aMask untilDate:(CPDate)anExpiration inMode:(CPString)aMode dequeue:(BOOL)shouldDequeue
948 {
949  _eventListeners.splice(_eventListenerInsertionIndex++, 0, _CPEventListenerMake(aMask, aCallback, shouldDequeue));
950 }
951 
969 - (void)setTarget:(id)aTarget selector:(SEL)aSelector forNextEventMatchingMask:(unsigned int)aMask untilDate:(CPDate)anExpiration inMode:(CPString)aMode dequeue:(BOOL)shouldDequeue
970 {
971  _eventListeners.splice(_eventListenerInsertionIndex++, 0, _CPEventListenerMake(aMask, function (anEvent) { if (aTarget != null) aTarget.isa.objj_msgSend1(aTarget, aSelector, anEvent); }, shouldDequeue));
972 }
973 
977 - (CPEvent)currentEvent
978 {
979  return _currentEvent;
980 }
981 
982 // Managing Sheets
983 
992 - (void)beginSheet:(CPWindow)aSheet modalForWindow:(CPWindow)aWindow modalDelegate:(id)aModalDelegate didEndSelector:(SEL)didEndSelector contextInfo:(id)contextInfo
993 {
994  if ([aWindow isSheet])
995  {
996  [CPException raise:CPInternalInconsistencyException reason:@"The target window of beginSheet: cannot be a sheet"];
997  return;
998  }
999 
1000  if (![aWindow attachedSheet])
1001  [aSheet._windowView _enableSheet:YES inWindow:aWindow];
1002 
1003  [aWindow _attachSheet:aSheet modalDelegate:aModalDelegate didEndSelector:didEndSelector contextInfo:contextInfo];
1004 }
1005 
1019 - (void)endSheet:(CPWindow)sheet returnCode:(int)returnCode
1020 {
1021  var count = [_windows count];
1022 
1023  while (--count >= 0)
1024  {
1025  var aWindow = [_windows objectAtIndex:count],
1026  context = aWindow._sheetContext;
1027 
1028  if (context && context["sheet"] === sheet)
1029  {
1030  context["returnCode"] = returnCode;
1031  [aWindow _endSheet];
1032  return;
1033  }
1034  }
1035 }
1036 
1041 - (void)endSheet:(CPWindow)sheet
1042 {
1043  // FIX ME: this is wrong: by Cocoa this should be: CPRunStoppedResponse.
1044  [self endSheet:sheet returnCode:0];
1045 }
1046 
1062 - (CPArray)arguments
1063 {
1064  // FIXME This should probably not access the window object #if !PLATFORM(DOM), but the unit tests rely on it.
1065  if (window && window.location && _fullArgsString !== window.location.hash)
1066  [self _reloadArguments];
1067 
1068  return _args;
1069 }
1070 
1087 - (void)setArguments:(CPArray)args
1088 {
1089  if (!args || args.length == 0)
1090  {
1091  _args = [];
1092  // Don't use if PLATFORM(DOM) here - the unit test fakes window.location so we should play along.
1093  if (window && window.location)
1094  window.location.hash = @"#";
1095  return;
1096  }
1097 
1098  if (![args isKindOfClass:CPArray])
1099  args = [CPArray arrayWithObject:args];
1100 
1101  _args = args;
1102 
1103  var toEncode = [_args copy];
1104  for (var i = 0, count = toEncode.length; i < count; i++)
1105  toEncode[i] = encodeURIComponent(toEncode[i]);
1106 
1107  var hash = [toEncode componentsJoinedByString:@"/"];
1108 
1109  // Don't use if PLATFORM(DOM) here - the unit test fakes window.location so we should play along.
1110  if (window && window.location)
1111  window.location.hash = @"#" + hash;
1112 }
1113 
1114 - (void)_reloadArguments
1115 {
1116  // FIXME This should probably not access the window object #if !PLATFORM(DOM), but the unit tests rely on it.
1117  _fullArgsString = (window && window.location) ? window.location.hash : "";
1118 
1119  if (_fullArgsString.length)
1120  {
1121  var args = _fullArgsString.substring(1).split("/");
1122 
1123  for (var i = 0, count = args.length; i < count; i++)
1124  args[i] = decodeURIComponent(args[i]);
1125 
1126  _args = args;
1127  }
1128  else
1129  _args = [];
1130 }
1131 
1149 - (CPDictionary)namedArguments
1150 {
1151  return _namedArgs;
1152 }
1153 
1154 - (BOOL)_openURL:(CPURL)aURL
1155 {
1156  if (_delegate && [_delegate respondsToSelector:@selector(application:openFile:)])
1157  {
1158  CPLog.warn("application:openFile: is deprecated, use application:openURL: instead.");
1159  return [_delegate application:self openFile:[aURL absoluteString]];
1160  }
1161 
1162  if (_delegate && [_delegate respondsToSelector:@selector(application:openURL:)])
1163  return [_delegate application:self openURL:aURL];
1164 
1165  return !![_documentController openDocumentWithContentsOfURL:aURL display:YES error:NULL];
1166 }
1167 
1168 - (void)_willBecomeActive
1169 {
1170  [[CPNotificationCenter defaultCenter] postNotificationName:CPApplicationWillBecomeActiveNotification
1171  object:self
1172  userInfo:nil];
1173 }
1174 
1175 - (void)_didBecomeActive
1176 {
1177  if (![self keyWindow] && _previousKeyWindow &&
1178  [[self windows] indexOfObjectIdenticalTo:_previousKeyWindow] !== CPNotFound)
1179  [_previousKeyWindow makeKeyWindow];
1180 
1181  if (![self mainWindow] && _previousMainWindow &&
1182  [[self windows] indexOfObjectIdenticalTo:_previousMainWindow] !== CPNotFound)
1183  [_previousMainWindow makeMainWindow];
1184 
1185  if ([self keyWindow])
1186  [[self keyWindow] orderFront:self];
1187  else if ([self mainWindow])
1188  [[self mainWindow] makeKeyAndOrderFront:self];
1189  else if ([self mainMenu])
1190  [[self mainMenu]._menuWindow makeKeyWindow]; //FIXME this may not actually work
1191 
1192  _previousKeyWindow = nil;
1193  _previousMainWindow = nil;
1194 
1195  [[CPNotificationCenter defaultCenter] postNotificationName:CPApplicationDidBecomeActiveNotification
1196  object:self
1197  userInfo:nil];
1198 }
1199 
1200 - (void)_willResignActive
1201 {
1202  [[CPNotificationCenter defaultCenter] postNotificationName:CPApplicationWillResignActiveNotification
1203  object:self
1204  userInfo:nil];
1205 }
1206 
1207 - (BOOL)_sendDelegateApplicationShouldTerminate
1208 {
1209  if (!(_implementedDelegateMethods & CPApplicationDelegate_applicationShouldTerminate_))
1210  return YES;
1211 
1212  return [_delegate applicationShouldTerminate:self];
1213 }
1214 
1215 - (CPString)_sendDelegateApplicationShouldTerminateMessage
1216 {
1217  if (!(_implementedDelegateMethods & CPApplicationDelegate_applicationShouldTerminateMessage_))
1218  return @"You have attempted to leave this page. Are you sure you want to exit this page?";
1219 
1220  return [_delegate applicationShouldTerminateMessage:self];
1221 }
1222 
1223 - (void)_didResignActive
1224 {
1225  if (self._activeMenu)
1226  [self._activeMenu cancelTracking];
1227 
1228  if ([self keyWindow])
1229  {
1230  _previousKeyWindow = [self keyWindow];
1231  [_previousKeyWindow resignKeyWindow];
1232  }
1233 
1234  if ([self mainWindow])
1235  {
1236  _previousMainWindow = [self mainWindow];
1237  [_previousMainWindow resignMainWindow];
1238  }
1239 
1240  [[CPNotificationCenter defaultCenter] postNotificationName:CPApplicationDidResignActiveNotification
1241  object:self
1242  userInfo:nil];
1243 }
1244 
1245 + (CPString)defaultThemeName
1246 {
1247  return ([[CPBundle mainBundle] objectForInfoDictionaryKey:"CPDefaultTheme"] || @"Aristo2");
1248 }
1249 
1250 @end
1251 
1252 var _CPModalSessionMake = function(aWindow, aStopCode)
1253 {
1254  return { _window:aWindow, _state:CPRunContinuesResponse , _previous:nil };
1255 };
1256 
1257 var _CPEventListenerMake = function(anEventMask, aCallback, shouldDequeue)
1258 {
1259  return { _mask:anEventMask, _callback:aCallback, _dequeue:shouldDequeue };
1260 };
1261 
1262 // Make this a global for use in CPPlatformWindow+DOM.j.
1263 _CPRunModalLoop = function(anEvent)
1264 {
1265  [CPApp setCallback:_CPRunModalLoop forNextEventMatchingMask:CPAnyEventMask untilDate:nil inMode:0 dequeue:YES];
1266 
1267  var theWindow = [anEvent window],
1268  modalSession = CPApp._currentSession;
1269 
1270  /*
1271  The special case for popovers here is not clear. In Cocoa the popover window does not respond YES to worksWhenModal, yet it works when there is a modal window. Maybe it starts its own modal session, but interaction with the original modal window seems to continue working as well. Regardless of correctness, this solution beats popovers not working at all from sheets.
1272  */
1273  if (theWindow == modalSession._window ||
1274  [theWindow worksWhenModal] ||
1275  [theWindow attachedSheet] == modalSession._window || // -dw- allow modal parent of sheet to be repositioned
1276  ([theWindow isKindOfClass:_CPPopoverWindow] && [[theWindow targetView] window] === modalSession._window))
1277  {
1278  [theWindow sendEvent:anEvent];
1279  }
1280 };
1281 
1288 function CPApplicationMain(args, namedArgs)
1289 {
1290 
1291 #if PLATFORM(DOM)
1292  // hook to allow recorder, etc to manipulate things before starting AppKit
1293  if (window.parent !== window && typeof window.parent._childAppIsStarting === "function")
1294  {
1295  try
1296  {
1297  window.parent._childAppIsStarting(window);
1298  }
1299  catch(err)
1300  {
1301  // This could happen if we're in an iframe without access to the parent frame.
1302  CPLog.warn("Failed to call parent frame's _childAppIsStarting().");
1303  }
1304  }
1305 #endif
1306 
1307  var mainBundle = [CPBundle mainBundle],
1308  principalClass = [mainBundle principalClass];
1309 
1310  if (!principalClass)
1311  principalClass = [CPApplication class];
1312 
1313  [principalClass sharedApplication];
1314 
1315  if ([args containsObject:"debug"])
1316  CPLogRegister(CPLogPopup);
1317 
1318  CPApp._args = args;
1319  CPApp._namedArgs = namedArgs;
1320 
1321  [_CPAppBootstrapper performActions];
1322 }
1323 
1324 var _CPAppBootstrapperActions = nil;
1325 @implementation _CPAppBootstrapper : CPObject
1326 {
1327  id __doxygen__;
1328 }
1329 
1330 + (CPArray)actions
1331 {
1332  return [@selector(bootstrapPlatform), @selector(loadDefaultTheme), @selector(loadMainCibFile)];
1333 }
1334 
1335 + (void)performActions
1336 {
1337  if (!_CPAppBootstrapperActions)
1338  _CPAppBootstrapperActions = [self actions];
1339 
1340  while (_CPAppBootstrapperActions.length)
1341  {
1342  var action = _CPAppBootstrapperActions.shift();
1343 
1344  if (self.isa.objj_msgSend0(self, action))
1345  return;
1346  }
1347 
1348  [CPApp run];
1349 }
1350 
1351 + (BOOL)bootstrapPlatform
1352 {
1353  return [CPPlatform bootstrap];
1354 }
1355 
1356 + (BOOL)loadDefaultTheme
1357 {
1358  var defaultThemeName = [CPApplication defaultThemeName],
1359  themeURL = nil;
1360 
1361  if (defaultThemeName === @"Aristo" || defaultThemeName === @"Aristo2")
1362  themeURL = [[CPBundle bundleForClass:[CPApplication class]] pathForResource:defaultThemeName + @".blend"];
1363  else
1364  themeURL = [[CPBundle mainBundle] pathForResource:defaultThemeName + @".blend"];
1365 
1366  var blend = [[CPThemeBlend alloc] initWithContentsOfURL:themeURL];
1367  [blend loadWithDelegate:self];
1368 
1369  return YES;
1370 }
1371 
1372 + (void)blendDidFinishLoading:(CPThemeBlend)aThemeBlend
1373 {
1376 
1377  [self performActions];
1378 }
1379 
1380 + (BOOL)loadMainCibFile
1381 {
1382  var mainBundle = [CPBundle mainBundle],
1383  mainCibFile = [mainBundle objectForInfoDictionaryKey:CPMainCibFile] || [mainBundle objectForInfoDictionaryKey:CPMainCibFileHumanFriendly];
1384 
1385  if (mainCibFile)
1386  {
1387  [mainBundle loadCibFile:mainCibFile
1388  externalNameTable:@{ CPCibOwner: CPApp }
1389  loadDelegate:self];
1390 
1391  return YES;
1392  }
1393  else
1394  [self loadCiblessBrowserMainMenu];
1395 
1396  return NO;
1397 }
1398 
1399 + (void)loadCiblessBrowserMainMenu
1400 {
1401  var mainMenu = [[CPMenu alloc] initWithTitle:@"MainMenu"];
1402 
1403  // FIXME: We should implement autoenabling.
1404  [mainMenu setAutoenablesItems:NO];
1405 
1406  var newMenuItem = [[CPMenuItem alloc] initWithTitle:@"New" action:@selector(newDocument:) keyEquivalent:@"n"];
1407 
1408  [newMenuItem setImage:[[CPTheme defaultTheme] valueForAttributeWithName:@"menu-general-icon-new" forClass:_CPMenuView]];
1409  [newMenuItem setAlternateImage:[[CPTheme defaultTheme] valueForAttributeWithName:@"menu-general-icon-new" inState:CPThemeStateHighlighted forClass:_CPMenuView]];
1410 
1411  [mainMenu addItem:newMenuItem];
1412 
1413  var openMenuItem = [[CPMenuItem alloc] initWithTitle:@"Open" action:@selector(openDocument:) keyEquivalent:@"o"];
1414 
1415  [openMenuItem setImage:[[CPTheme defaultTheme] valueForAttributeWithName:@"menu-general-icon-open" forClass:_CPMenuView]];
1416  [openMenuItem setAlternateImage:[[CPTheme defaultTheme] valueForAttributeWithName:@"menu-general-icon-open" inState:CPThemeStateHighlighted forClass:_CPMenuView]];
1417 
1418  [mainMenu addItem:openMenuItem];
1419 
1420  var saveMenu = [[CPMenu alloc] initWithTitle:@"Save"],
1421  saveMenuItem = [[CPMenuItem alloc] initWithTitle:@"Save" action:@selector(saveDocument:) keyEquivalent:nil];
1422 
1423  [saveMenuItem setImage:[[CPTheme defaultTheme] valueForAttributeWithName:@"menu-general-icon-save" forClass:_CPMenuView]];
1424  [saveMenuItem setAlternateImage:[[CPTheme defaultTheme] valueForAttributeWithName:@"menu-general-icon-save" inState:CPThemeStateHighlighted forClass:_CPMenuView]];
1425 
1426  [saveMenu addItem:[[CPMenuItem alloc] initWithTitle:@"Save" action:@selector(saveDocument:) keyEquivalent:@"s"]];
1427  [saveMenu addItem:[[CPMenuItem alloc] initWithTitle:@"Save As" action:@selector(saveDocumentAs:) keyEquivalent:nil]];
1428 
1429  [saveMenuItem setSubmenu:saveMenu];
1430 
1431  [mainMenu addItem:saveMenuItem];
1432 
1433  var editMenuItem = [[CPMenuItem alloc] initWithTitle:@"Edit" action:nil keyEquivalent:nil],
1434  editMenu = [[CPMenu alloc] initWithTitle:@"Edit"],
1435 
1436  undoMenuItem = [[CPMenuItem alloc] initWithTitle:@"Undo" action:@selector(undo:) keyEquivalent:CPUndoKeyEquivalent],
1437  redoMenuItem = [[CPMenuItem alloc] initWithTitle:@"Redo" action:@selector(redo:) keyEquivalent:CPRedoKeyEquivalent];
1438 
1439  [undoMenuItem setKeyEquivalentModifierMask:CPUndoKeyEquivalentModifierMask];
1440  [redoMenuItem setKeyEquivalentModifierMask:CPRedoKeyEquivalentModifierMask];
1441 
1442  [editMenu addItem:undoMenuItem];
1443  [editMenu addItem:redoMenuItem];
1444 
1445  [editMenu addItem:[[CPMenuItem alloc] initWithTitle:@"Cut" action:@selector(cut:) keyEquivalent:@"x"]];
1446  [editMenu addItem:[[CPMenuItem alloc] initWithTitle:@"Copy" action:@selector(copy:) keyEquivalent:@"c"]];
1447  [editMenu addItem:[[CPMenuItem alloc] initWithTitle:@"Paste" action:@selector(paste:) keyEquivalent:@"v"]];
1448 
1449  [editMenuItem setSubmenu:editMenu];
1450  [editMenuItem setHidden:YES];
1451 
1452  [mainMenu addItem:editMenuItem];
1453 
1454  [mainMenu addItem:[CPMenuItem separatorItem]];
1455 
1456  [CPApp setMainMenu:mainMenu];
1457 }
1458 
1459 + (void)cibDidFinishLoading:(CPCib)aCib
1460 {
1461  [self performActions];
1462 }
1463 
1464 + (void)cibDidFailToLoad:(CPCib)aCib
1465 {
1466  throw new Error("Could not load main cib file. Did you forget to nib2cib it?");
1467 }
1468 
1469 + (void)reset
1470 {
1471  _CPAppBootstrapperActions = nil;
1472 }
1473 
1474 @end
1475 
1476 
1478 
1482 + (unsigned)modifierFlags
1483 {
1484  return CPEventModifierFlags;
1485 }
1486 
1487 @end
1488 
1490 
1494 - (CPThemeBlend)themeBlend
1495 {
1496  return _themeBlend;
1497 }
1498 
1502 - (void)setThemeBlend:(CPThemeBlend)aValue
1503 {
1504  _themeBlend = aValue;
1505 }
1506 
1507 @end
Used to implement exception handling (creating & raising).
Definition: CPException.h:2
Definition: CPFont.h:2
CPModalSession var CPMainCibFileHumanFriendly
Definition: CPApplication.j:28
CPRunContinuesResponse
Definition: CPMenu.h:2
void endSheet:returnCode:(CPWindow sheet, [returnCode] int returnCode)
id targetForAction:(SEL anAction)
id init()
Definition: CALayer.j:126
CPColorPanel sharedColorPanel()
Definition: CPColorPanel.j:93
id delegate()
Definition: CPWindow.j:1552
A representation of a single point in time.
Definition: CPDate.h:2
void postNotificationName:object:userInfo:(CPString aNotificationName, [object] id anObject, [userInfo] CPDictionary aUserInfo)
An object representation of nil.
Definition: CPNull.h:2
CPWindowController windowController()
Definition: CPWindow.j:1569
CPFont systemFontOfSize:(CGSize aSize)
Definition: CPFont.j:282
id valueForAttributeWithName:inState:forClass:(CPString aName, [inState] ThemeState aState, [forClass] id aClass)
Definition: CPTheme.j:236
CPCursor arrowCursor()
Definition: CPCursor.j:246
CPModalSession var CPMainCibFile
Definition: CPApplication.j:27
The main run loop for the application.
Definition: CPRunLoop.h:2
CFData prototype isa
Definition: CPData.j:214
void orderFrontStandardAboutPanelWithOptions:(CPDictionary options)
CPResponder firstResponder()
Definition: CPWindow.j:1657
Definition: CPCib.h:2
CPMenuItem separatorItem()
Definition: CPMenuItem.j:536
id delegate()
Definition: CALayer.j:965
id initWithTitle:(CPString aTitle)
Definition: CPMenu.j:257
void postNotificationName:object:(CPString aNotificationName, [object] id anObject)
void raise:reason:(CPString aName, [reason] CPString aReason)
Definition: CPException.j:66
unsigned modifierFlags()
Definition: CPEvent.j:309
void hideOtherApplications:(id aSender)
Definition: CPPlatform.j:60
void setMenu:(CPMenu aMenu)
Definition: CPResponder.j:333
CPString defaultThemeName()
void setDefaultTheme:(CPTheme aTheme)
Definition: CPTheme.j:39
CPMenu menu()
Definition: CPResponder.j:338
CPNotificationCenter defaultCenter()
A mutable key-value pair collection.
Definition: CPDictionary.h:2
id targetForAction:to:from:(SEL anAction, [to] id aTarget, [from] id aSender)
CPRunLoop currentRunLoop()
Definition: CPRunLoop.j:232
void hide:(id aSender)
Definition: CPPlatform.j:64
CPMouseMoved
void runModalSession:(CPModalSession aModalSession)
id initWithContentRect:styleMask:(CGRect aContentRect, [styleMask] unsigned aStyleMask)
Definition: CPWindow.j:265
CPString pathForResource:(CPString aFilename)
Definition: CPBundle.j:158
var CPApplicationDelegate_applicationShouldTerminateMessage_
Definition: CPApplication.j:49
An immutable string (collection of characters).
Definition: CPString.h:2
CPNull null()
Definition: CPNull.j:51
CPString absoluteString()
Definition: CPURL.j:105
CPBundle mainBundle()
Definition: CPBundle.j:82
id objectForKey:(id aKey)
Definition: CPDictionary.j:515
if(CPFeatureIsCompatible(CPHTMLCanvasFeature))
Definition: CPImage.h:2
void setThemeBlend:(CPThemeBlend aValue)
void orderFront:(id aSender)
Definition: CPColorPanel.j:268
CPApplication sharedApplication()
void bootstrap()
Definition: CPPlatform.j:27
CPWindow keyWindow()
CPImage applicationIconImage()
id initWithContentsOfFile:size:(CPString aFilename, [size] CGSize aSize)
Definition: CPImage.j:176
void stopModalWithCode:(int aCode)
void doCommandBySelector:(SEL aSelector)
Definition: CPResponder.j:304
void deactivate()
Definition: CPPlatform.j:56
CPWindow window()
Definition: CPEvent.j:341
int length()
Definition: CPString.j:186
id initWithContentsOfFile:(CPString aFilename)
Definition: CPImage.j:192
A notification that can be posted to a CPNotificationCenter.
Definition: CPNotification.h:2
CPWindow mainWindow()
CPEvent otherEventWithType:location:modifierFlags:timestamp:windowNumber:context:subtype:data1:data2:(CPEventType anEventType, [location] CGPoint aLocation, [modifierFlags] unsigned modifierFlags, [timestamp] CPTimeInterval aTimestamp, [windowNumber] int aWindowNumber, [context] CPGraphicsContext aGraphicsContext, [subtype] short aSubtype, [data1] int aData1, [data2] int aData2)
Definition: CPEvent.j:174
CPDate limitDateForMode:(CPString aMode)
Definition: CPRunLoop.j:342
CPTheme defaultTheme()
Definition: CPTheme.j:44
CPModalSession beginModalSessionForWindow:(CPWindow aWindow)
CPFont boldSystemFontOfSize:(CGSize aSize)
Definition: CPFont.j:294
float systemFontSize()
Definition: CPFont.j:168
CPApplicationTerminateReply CPTerminateNow
function CPApplicationMain(args, namedArgs)
id objectForInfoDictionaryKey:(CPString aKey)
Definition: CPBundle.j:183
CPNotFound
Definition: CPObjJRuntime.j:62
void setMenu:(CPMenu aMenu)
var CPApplicationDelegate_applicationShouldTerminate_
Definition: CPApplication.j:48
id init()
Definition: CPObject.j:145
void sendEvent:(CPEvent anEvent)
Sends messages (CPNotification) between objects.
Definition: CPTheme.h:2
void activateIgnoringOtherApps:(BOOL shouldIgnoreOtherApps)
Definition: CPPlatform.j:52
CPTimeInterval currentTimestamp()
Definition: CPEvent.j:84
id initWithTitle:action:keyEquivalent:(CPString aTitle, [action] SEL anAction, [keyEquivalent] CPString aKeyEquivalent)
Definition: CPMenuItem.j:121
CPBundle bundleForClass:(Class aClass)
Definition: CPBundle.j:77
void terminateApplication()
Definition: CPPlatform.j:48
CPTheme themeNamed:(CPString aName)
Definition: CPTheme.j:70
void set()
Definition: CPCursor.j:128
CPModalSession var CPEventModifierFlags
Definition: CPApplication.j:29
Definition: CPEvent.h:2
Class class()
Definition: CPObject.j:179
Definition: CPPanel.h:2
CPPlatformWindow platformWindow()
Definition: CPWindow.j:389
id valueForAttributeWithName:forClass:(CPString aName, [forClass] id aClass)
Definition: CPTheme.j:218
unsigned hash()
Definition: CPObject.j:547
CPDictionary infoDictionary()
Definition: CPBundle.j:178
Definition: CPURL.h:2
void finishLaunching()
CPMenu menu
id alloc()
Definition: CPObject.j:130
void setDelegate:(id< CPApplicationDelegate > aDelegate)