1/** <title>NSApplication</title>
2
3   <abstract>The one and only application class.</abstract>
4
5   Copyright (C) 1996-2015 Free Software Foundation, Inc.
6
7   Author: Scott Christley <scottc@net-community.com>
8   Date: 1996
9   Author: Felipe A. Rodriguez <far@ix.netcom.com>
10   Date: August 1998
11   Author: Richard Frith-Macdonald <richard@brainstorm.co.uk>
12   Date: December 1998
13
14   This file is part of the GNUstep GUI Library.
15
16   This library is free software; you can redistribute it and/or
17   modify it under the terms of the GNU Lesser General Public
18   License as published by the Free Software Foundation; either
19   version 2 of the License, or (at your option) any later version.
20
21   This library is distributed in the hope that it will be useful,
22   but WITHOUT ANY WARRANTY; without even the implied warranty of
23   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.	 See the GNU
24   Lesser General Public License for more details.
25
26   You should have received a copy of the GNU Lesser General Public
27   License along with this library; see the file COPYING.LIB.
28   If not, see <http://www.gnu.org/licenses/> or write to the
29   Free Software Foundation, 51 Franklin Street, Fifth Floor,
30   Boston, MA 02110-1301, USA.
31*/
32
33#import "config.h"
34#include <stdio.h>
35#include <stdlib.h>
36
37#import <Foundation/NSArray.h>
38#import <Foundation/NSAutoreleasePool.h>
39#import <Foundation/NSBundle.h>
40#import <Foundation/NSDebug.h>
41#import <Foundation/NSDictionary.h>
42#import <Foundation/NSError.h>
43#import <Foundation/NSErrorRecoveryAttempting.h>
44#import <Foundation/NSException.h>
45#import <Foundation/NSFileManager.h>
46#import <Foundation/NSInvocation.h>
47#import <Foundation/NSNotification.h>
48#import <Foundation/NSObject.h>
49#import <Foundation/NSPathUtilities.h>
50#import <Foundation/NSProcessInfo.h>
51#import <Foundation/NSRunLoop.h>
52#import <Foundation/NSSet.h>
53#import <Foundation/NSString.h>
54#import <Foundation/NSTimer.h>
55#import <Foundation/NSThread.h>
56#import <Foundation/NSUserDefaults.h>
57#import <Foundation/NSValue.h>
58
59#ifndef LIB_FOUNDATION_LIBRARY
60#import <Foundation/NSConnection.h>
61#endif
62
63#import "AppKit/AppKitExceptions.h"
64#import "AppKit/NSAlert.h"
65#import "AppKit/NSApplication.h"
66#import "AppKit/NSCell.h"
67#import "AppKit/NSCursor.h"
68#import "AppKit/NSDocumentController.h"
69#import "AppKit/NSEvent.h"
70#import "AppKit/NSFontManager.h"
71#import "AppKit/NSImage.h"
72#import "AppKit/NSMenu.h"
73#import "AppKit/NSMenuItem.h"
74#import "AppKit/NSNibLoading.h"
75#import "AppKit/NSPageLayout.h"
76#import "AppKit/NSPanel.h"
77#import "AppKit/NSPasteboard.h"
78#import "AppKit/NSToolbarItem.h"
79#import "AppKit/NSWorkspace.h"
80#import "AppKit/NSScreen.h"
81#import "AppKit/PSOperators.h"
82
83#import "GSIconManager.h"
84#import "GNUstepGUI/GSDisplayServer.h"
85#import "GNUstepGUI/GSServicesManager.h"
86#import "GSGuiPrivate.h"
87#import "GNUstepGUI/GSInfoPanel.h"
88#import "GNUstepGUI/GSVersion.h"
89#import "NSDocumentFrameworkPrivate.h"
90#import "NSToolbarFrameworkPrivate.h"
91
92// minimize icon when suppressed?
93#define	MINI_ICON	0
94
95/* The -gui thread. See the comment in initialize_gnustep_backend. */
96NSThread *GSAppKitThread;
97
98/* Notifications used to implement hide and unhide functionality. */
99static NSString	*GSHideOtherApplicationsNotification
100  = @"GSHideOtherApplicationsNotification";
101static NSString	*GSUnhideAllApplicationsNotification
102  = @"GSUnhideAllApplicationsNotification";
103
104/*
105 * Base library exception handler
106 */
107static NSUncaughtExceptionHandler *defaultUncaughtExceptionHandler;
108
109/*
110 * Gui library user friendly exception handler
111 */
112static void
113_NSAppKitUncaughtExceptionHandler (NSException *exception)
114{
115  NSInteger retVal;
116
117  /* Reset the exception handler to the Base library's one, to prevent
118     recursive calls to the gui one. */
119  NSSetUncaughtExceptionHandler(defaultUncaughtExceptionHandler);
120
121  /*
122   * If there is no graphics context to run the alert panel in or
123   * its a severe error, use a non-graphical exception handler
124   */
125  if (GSCurrentContext() == nil
126    || [[exception name] isEqual: NSWindowServerCommunicationException]
127    || [[exception name] isEqual: GSWindowServerInternalException])
128    {
129      /* The following will raise again the exception using the base
130	 library exception handler */
131      [exception raise];
132    }
133
134  retVal = GSRunExceptionPanel
135    ([NSString stringWithFormat: _(@"Critical Error in %@"),
136	       [[NSProcessInfo processInfo] processName]],
137     exception,
138     _(@"Abort"),
139     nil,
140#ifdef DEBUG
141     _(@"Debug"));
142#else
143     nil);
144#endif
145
146  /* The user wants to abort */
147  if (retVal == NSAlertDefault)
148    {
149      /* The following will raise again the exception using the base
150	 library exception handler */
151      [exception raise];
152    }
153  else
154    {
155      /* Debug button: abort so we can trace the error in gdb */
156      abort();
157    }
158}
159
160/* Get the bundle.  */
161NSBundle *
162GSGuiBundle(void)
163{
164  /* This is the bundle from where we load localization of messages.  */
165  static NSBundle *guiBundle = nil;
166
167  if (!guiBundle)
168    {
169      /* Create the gui bundle we use to localize messages.  */
170      guiBundle = [NSBundle bundleForLibrary: @"gnustep-gui"
171			    version: OBJC_STRINGIFY(GNUSTEP_GUI_MAJOR_VERSION.GNUSTEP_GUI_MINOR_VERSION)];
172      RETAIN(guiBundle);
173    }
174  return guiBundle;
175}
176
177@interface GSBackend : NSObject
178{}
179+ (void) initializeBackend;
180@end
181
182static NSString *
183gnustep_backend_path(NSString *dir, NSString *name)
184{
185  NSString *path;
186  NSEnumerator *benum;
187
188  NSDebugFLLog(@"BackendBundle", @"Looking for %@", name);
189
190  /* Find the backend framework */
191  benum = [NSStandardLibraryPaths() objectEnumerator];
192  while ((path = [benum nextObject]))
193    {
194      path = [path stringByAppendingPathComponent: dir];
195      path = [path stringByAppendingPathComponent: name];
196      if ([[NSFileManager defaultManager] fileExistsAtPath: path])
197	{
198	  break;
199	}
200    }
201  return path;
202}
203
204/* Find and load the backend framework, if there is one. The name is
205   taken from a user default containing the name of the backend framework,
206   such as 'GNUstep-back', or simply 'back', or for historical reasons,
207   'libgnustep-back'.  */
208static NSString *
209gnustep_backend_framework(NSString *bundleName)
210{
211  if (bundleName == nil)
212    bundleName = @"GNUstep_back.framework";
213  else
214    {
215      if ([bundleName hasPrefix: @"GNUstep-"])
216	bundleName = [bundleName stringByAppendingString: @".framework"];
217      else
218	{
219	if  ([bundleName hasPrefix: @"libgnustep-"])
220	  {
221	    bundleName = [bundleName substringFromIndex: [@"libgnustep-" length]];
222	  }
223	bundleName = [NSString stringWithFormat: @"GNUstep-%@.framework",
224			       bundleName];
225	}
226    }
227
228  return gnustep_backend_path(@"Frameworks", bundleName);
229}
230
231/* Find and load the backend bundle, if there is one. The name is
232   taken from a user default containing the name of the backend bundle,
233   such as 'back', or for historical reasons, 'libgnustep-back'. New
234   versions may also have a version number associated with it.  */
235static NSString *
236gnustep_backend_bundle(NSString *bundleName)
237{
238  NSString *path, *bundleWithVersion;
239  int version = GNUSTEP_GUI_MAJOR_VERSION * 100 + GNUSTEP_GUI_MINOR_VERSION;
240
241  if (bundleName == nil)
242    {
243      bundleName = @"libgnustep-back";
244    }
245  else
246    {
247      if ([bundleName hasPrefix: @"libgnustep-"] == NO)
248	{
249	  bundleName = [NSString stringWithFormat: @"libgnustep-%@",
250				 bundleName];
251	}
252    }
253  bundleWithVersion = [NSString stringWithFormat: @"%@-%03d.bundle",
254				bundleName, version];
255  bundleName = [bundleName stringByAppendingString: @".bundle"];
256  path = gnustep_backend_path(@"Bundles", bundleWithVersion);
257  if (path == nil)
258    {
259      NSLog(@"Did not find correct version of backend (%@), "
260	@"falling back to std (%@).", bundleWithVersion, bundleName);
261      path = gnustep_backend_path(@"Bundles", bundleName);
262    }
263  return path;
264}
265
266BOOL
267initialize_gnustep_backend(void)
268{
269  static int first = 1;
270
271  if (first)
272    {
273      Class backend;
274
275      /*
276      Remember which thread we are running in. This thread will be the
277      -gui thread, ie. the only thread that may do any rendering. With
278      the exception of a few methods explicitly marked as thread-safe,
279      other threads should not call any methods in -gui.
280      */
281      GSAppKitThread = [NSThread currentThread];
282
283      first = 0;
284#ifdef BACKEND_BUNDLE
285      {
286	NSBundle *theBundle;
287	NSString *path, *bundleName;
288	NSUserDefaults	*defs = [NSUserDefaults standardUserDefaults];
289
290	/* What backend ? */
291	bundleName = [defs stringForKey: @"GSBackend"];
292	path = gnustep_backend_framework (bundleName);
293	if (path == nil)
294	  {
295	    NSDebugLLog(@"BackendBundle", @"Did not find backend framework.");
296	    path = gnustep_backend_bundle (bundleName);
297	  }
298
299	/* FIXME/TODO - update localized error messages.  */
300
301	/* Backend found ? */
302	if (bundleName == nil)
303	  bundleName = @"back";
304	NSCAssert1(path != nil, _(@"Unable to find backend %@"), bundleName);
305	NSDebugLog(@"Loading Backend from %@", path);
306	NSDebugFLLog(@"BackendBundle", @"Loading Backend from %@", path);
307
308	/* Create a bundle object.  (Should normally succeed).  */
309	theBundle = [NSBundle bundleWithPath: path];
310	NSCAssert1(theBundle != nil,
311		   _(@"Can't create NSBundle object for backend at path %@"),
312		   path);
313
314	/* Now load the object file from the bundle.  */
315	NSCAssert1 ([theBundle load],
316		    _(@"Can't load object file from backend at path %@"),
317		    path);
318
319	/* Now get the GSBackend class, which should have just been loaded
320	 * from the bundle.  */
321	backend = NSClassFromString (@"GSBackend");
322	NSCAssert1 (backend != Nil,
323	  _(@"Backend at path %@ doesn't contain the GSBackend class"), path);
324	[backend initializeBackend];
325      }
326
327#else
328      /* GSBackend will be in a separate library linked in with the app.
329       This would be cleaner with ...classNamed: @"GSBackend", but that
330       doesn't work in some cases (Mac OS X for instance).  */
331      [GSBackend initializeBackend];
332#endif
333    }
334  return YES;
335}
336
337void
338gsapp_user_bundles(void)
339{
340  NSUserDefaults *defs=[NSUserDefaults standardUserDefaults];
341  NSArray *a=[defs arrayForKey: @"GSAppKitUserBundles"];
342  NSUInteger i, c;
343  c = [a count];
344  if (a == nil || c == 0)
345    return;
346  NSLog(@"Loading %d user defined AppKit bundles", (int)c);
347  for (i = 0; i < c; i++)
348    {
349      NSBundle *b = [NSBundle bundleWithPath: [a objectAtIndex: i]];
350      if (!b)
351	{
352	  NSLog(@"* Unable to load '%@'", [a objectAtIndex: i]);
353	  continue;
354	}
355      NSLog(@"Loaded '%@'\n", [a objectAtIndex: i]);
356      [[[b principalClass] alloc] init];
357    }
358}
359
360/*
361 * Types
362 */
363struct _NSModalSession {
364  NSInteger		runState;
365  NSInteger		entryLevel;
366  NSWindow		*window;
367  NSModalSession	previous;
368};
369
370@interface NSApplication (Private)
371- (void) _appIconInit;
372- (void) _loadAppIconImage;
373- (NSDictionary*) _notificationUserInfo;
374- (void) _openDocument: (NSString*)name;
375- (id) _targetForAction: (SEL)aSelector
376	      keyWindow: (NSWindow *)keyWindow
377	     mainWindow: (NSWindow *)mainWindow;
378- (void) _windowDidBecomeKey: (NSNotification*) notification;
379- (void) _windowDidBecomeMain: (NSNotification*) notification;
380- (void) _windowDidResignKey: (NSNotification*) notification;
381- (void) _windowWillClose: (NSNotification*) notification;
382- (void) _workspaceNotification: (NSNotification*) notification;
383- (NSArray *) _openFiles;
384- (NSMenu *) _dockMenu;
385@end
386
387@interface NSWindow (TitleWithRepresentedFilename)
388- (BOOL) _hasTitleWithRepresentedFilename;
389@end
390
391@interface NSWindow (ApplicationPrivate)
392- (void) setAttachedSheet: (id) sheet;
393@end
394
395@implementation NSWindow (ApplicationPrivate)
396/**
397 * Associate sheet with the window it's attached to.  The window is not retained.
398 */
399- (void) setAttachedSheet: (id) sheet
400{
401  _attachedSheet = sheet;
402}
403@end
404
405@interface NSIconWindow : NSWindow
406@end
407
408@interface NSAppIconView : NSView
409- (void) setImage: (NSImage *)anImage;
410@end
411
412@interface NSMenu (HorizontalPrivate)
413- (void) _organizeMenu;
414@end
415
416/*
417 * Class variables
418 */
419static NSEvent *null_event;
420static Class arpClass;
421static NSNotificationCenter *nc;
422
423NSApplication	*NSApp = nil;
424
425@implementation	NSIconWindow
426
427- (BOOL) canBecomeMainWindow
428{
429  return NO;
430}
431
432- (BOOL) canBecomeKeyWindow
433{
434  return NO;
435}
436
437- (BOOL) becomesKeyOnlyIfNeeded
438{
439  return YES;
440}
441
442- (BOOL) worksWhenModal
443{
444  return YES;
445}
446
447- (void) orderWindow: (NSWindowOrderingMode)place relativeTo: (NSInteger)otherWin
448{
449  if ([[NSUserDefaults standardUserDefaults]
450	boolForKey: @"GSSuppressAppIcon"] == NO)
451    {
452      [super orderWindow: place relativeTo: otherWin];
453    }
454}
455
456- (void) _initDefaults
457{
458  [super _initDefaults];
459  /* Set the title of the window to the process name. Even as the
460     window shows no title bar, the window manager may show it.  */
461  [self setTitle: [[NSProcessInfo processInfo] processName]];
462  [self setExcludedFromWindowsMenu: YES];
463  [self setReleasedWhenClosed: NO];
464
465#if	MINI_ICON
466  /* Hack ...
467   * At least one window manager won't miniaturize a window unless
468   * it's at the standard level.  If the app icon is suppressed, we
469   * may still want a miniaturised version while the app is hidden.
470   */
471  if (YES == [[NSUserDefaults standardUserDefaults]
472    boolForKey: @"GSSuppressAppIcon"])
473    {
474      return;
475    }
476#endif
477  /* App icons and mini windows are displayed at dock level by default. Yet,
478     with the current window level mapping in -back, some window managers
479     will order pop up and context menus behind app icons and mini windows.
480     Therefore, it is possible to have app icons and mini windows displayed
481     at normal window level under control of a user preference. */
482  // See also NSMiniWindow -_initDefaults in NSWindow.m
483  if ([[NSUserDefaults standardUserDefaults]
484	boolForKey: @"GSAllowWindowsOverIcons"] == YES)
485    _windowLevel = NSDockWindowLevel;
486}
487
488- (void) rightMouseDown: (NSEvent *)theEvent
489{
490  NSMenu *menu = nil;
491  NSInterfaceStyle style = NSInterfaceStyleForKey(@"NSMenuInterfaceStyle", nil);
492
493  if (style == NSMacintoshInterfaceStyle || style == NSWindows95InterfaceStyle)
494    {
495      menu = [NSApp _dockMenu];
496    }
497  if (menu)
498    {
499      [NSMenu popUpContextMenu: menu
500		     withEvent: theEvent
501		       forView: [self contentView]];
502    }
503  else
504    {
505      [super rightMouseDown: theEvent];
506    }
507}
508
509@end
510
511@implementation NSAppIconView
512
513// Class variables
514static NSCell* dragCell = nil;
515static NSCell* tileCell = nil;
516
517static NSSize scaledIconSizeForSize(NSSize imageSize)
518{
519  NSSize iconSize, retSize;
520
521  iconSize = GSGetIconSize();
522  retSize.width = imageSize.width * iconSize.width / 64;
523  retSize.height = imageSize.height * iconSize.height / 64;
524  return retSize;
525}
526
527+ (void) initialize
528{
529  NSImage	*tileImage;
530  NSSize	iconSize;
531
532  iconSize = GSGetIconSize();
533  /* _appIconInit will set our image */
534  dragCell = [[NSCell alloc] initImageCell: nil];
535  [dragCell setBordered: NO];
536
537  tileImage = [[GSCurrentServer() iconTileImage] copy];
538  [tileImage setScalesWhenResized: YES];
539  [tileImage setSize: iconSize];
540  tileCell = [[NSCell alloc] initImageCell: tileImage];
541  RELEASE(tileImage);
542  [tileCell setBordered: NO];
543}
544
545- (BOOL) acceptsFirstMouse: (NSEvent*)theEvent
546{
547  return YES;
548}
549
550- (void) concludeDragOperation: (id<NSDraggingInfo>)sender
551{
552}
553
554- (NSDragOperation) draggingEntered: (id<NSDraggingInfo>)sender
555{
556  return NSDragOperationGeneric;
557}
558
559- (void) draggingExited: (id<NSDraggingInfo>)sender
560{
561}
562
563- (NSDragOperation) draggingUpdated: (id<NSDraggingInfo>)sender
564{
565  return NSDragOperationGeneric;
566}
567
568- (void) drawRect: (NSRect)rect
569{
570  NSSize iconSize = GSGetIconSize();
571
572  [tileCell drawWithFrame: NSMakeRect(0, 0, iconSize.width, iconSize.height)
573  		   inView: self];
574  [dragCell drawWithFrame: NSMakeRect(0, 0, iconSize.width, iconSize.height)
575		   inView: self];
576
577  if ([NSApp isHidden])
578    {
579      NSRectEdge mySides[] = {NSMinXEdge, NSMinYEdge, NSMaxXEdge, NSMaxYEdge};
580      CGFloat myGrays[] = {NSBlack, NSWhite, NSWhite, NSBlack};
581      NSDrawTiledRects(NSMakeRect(4, 4, 3, 2), rect, mySides, myGrays, 4);
582    }
583}
584
585- (id) initWithFrame: (NSRect)frame
586{
587  self = [super initWithFrame: frame];
588  [self registerForDraggedTypes: [NSArray arrayWithObjects:
589    NSFilenamesPboardType, nil]];
590  return self;
591}
592
593- (void) mouseDown: (NSEvent*)theEvent
594{
595  if ([theEvent clickCount] >= 2)
596    {
597      /* if not hidden raise windows which are possibly obscured. */
598      if ([NSApp isHidden] == NO)
599        {
600          NSArray *windows = RETAIN(GSOrderedWindows());
601          NSWindow *aWin;
602          NSEnumerator *iter = [windows reverseObjectEnumerator];
603
604          while ((aWin = [iter nextObject]))
605            {
606              if ([aWin isVisible] == YES && [aWin isMiniaturized] == NO
607                && aWin != [NSApp keyWindow] && aWin != [NSApp mainWindow]
608                && aWin != [self window]
609                && ([aWin styleMask] & NSMiniWindowMask) == 0)
610                {
611                  [aWin orderFrontRegardless];
612                }
613            }
614
615          if ([NSApp isActive] == YES)
616            {
617              if ([NSApp keyWindow] != nil)
618                {
619                  [[NSApp keyWindow] orderFront: self];
620                }
621              else if ([NSApp mainWindow] != nil)
622                {
623                  [[NSApp mainWindow] makeKeyAndOrderFront: self];
624                }
625              else
626                {
627                  /* We need give input focus to some window otherwise we'll
628                     never get keyboard events. FIXME: doesn't work. */
629                    NSWindow *menu_window= [[NSApp mainMenu] window];
630                    NSDebugLLog(@"Focus",
631		      @"No key on activation - make menu key");
632                    [GSServerForWindow(menu_window) setinputfocus:
633		      [menu_window windowNumber]];
634                }
635            }
636
637          RELEASE(windows);
638        }
639      [NSApp unhide: self]; // or activate or do nothing.
640    }
641  else
642    {
643      NSPoint	lastLocation;
644      NSPoint	location;
645      NSUInteger eventMask = NSLeftMouseDownMask | NSLeftMouseUpMask
646	| NSPeriodicMask | NSOtherMouseUpMask | NSRightMouseUpMask;
647      NSDate	*theDistantFuture = [NSDate distantFuture];
648      BOOL	done = NO;
649
650      lastLocation = [theEvent locationInWindow];
651      [NSEvent startPeriodicEventsAfterDelay: 0.02 withPeriod: 0.02];
652
653      while (!done)
654	{
655	  theEvent = [NSApp nextEventMatchingMask: eventMask
656					untilDate: theDistantFuture
657					   inMode: NSEventTrackingRunLoopMode
658					  dequeue: YES];
659
660	  switch ([theEvent type])
661	    {
662	      case NSRightMouseUp:
663	      case NSOtherMouseUp:
664	      case NSLeftMouseUp:
665	      /* any mouse up means we're done */
666		done = YES;
667		break;
668	      case NSPeriodic:
669		location = [_window mouseLocationOutsideOfEventStream];
670		if (NSEqualPoints(location, lastLocation) == NO)
671		  {
672		    NSPoint	origin = [_window frame].origin;
673
674		    origin.x += (location.x - lastLocation.x);
675		    origin.y += (location.y - lastLocation.y);
676		    [_window setFrameOrigin: origin];
677		  }
678		break;
679
680	      default:
681		break;
682	    }
683	}
684      [NSEvent stopPeriodicEvents];
685    }
686}
687
688- (BOOL) prepareForDragOperation: (id<NSDraggingInfo>)sender
689{
690  return YES;
691}
692
693- (BOOL) performDragOperation: (id<NSDraggingInfo>)sender
694{
695  NSArray	*types;
696  NSPasteboard	*dragPb;
697
698  dragPb = [sender draggingPasteboard];
699  types = [dragPb types];
700  if ([types containsObject: NSFilenamesPboardType] == YES)
701    {
702      NSArray	*names = [dragPb propertyListForType: NSFilenamesPboardType];
703      NSUInteger index;
704
705      [NSApp activateIgnoringOtherApps: YES];
706      for (index = 0; index < [names count]; index++)
707	{
708	  [NSApp _openDocument: [names objectAtIndex: index]];
709	}
710      return YES;
711    }
712  return NO;
713}
714
715- (void) setImage: (NSImage *)anImage
716{
717  NSImage *imgCopy = [anImage copy];
718
719  if (imgCopy)
720    {
721      NSSize imageSize = [imgCopy size];
722
723      [imgCopy setScalesWhenResized: YES];
724      [imgCopy setSize: scaledIconSizeForSize(imageSize)];
725    }
726  [dragCell setImage: imgCopy];
727  RELEASE(imgCopy);
728  [self setNeedsDisplay: YES];
729}
730
731@end
732
733/**
734 * <p>Every graphical GNUstep application has exactly one instance of
735 * <code>NSApplication</code> (or a subclass) instantiated.  Usually this is
736 * created through the +sharedApplication method.  Once created, this instance
737 * is always accessible through the global variable '<code>NSApp</code>'.</p>
738 *
739 * <p>The NSApplication instance manages the main run loop, dispatches
740 * events, and manages resources.  It sets up the connection to the window
741 * server and provides special methods for putting up "modal" (always on top)
742 * windows.</p>
743 *
744 * <p>Typically, -run is called by an application's <code>main</code> method
745 * after the NSApplication instance is created, which never returns.  However,
746 * applications needing to integrate other event loops may strategically call
747 * the -stop: method, followed by -run later on.</p>
748 *
749 * <p>To avoid most common needs for subclassing, NSApplication allows you to
750 * specify a <em>delegate</em> that is messaged in particular situations.
751 * See -delegate , -setDelegate: , and [(NSApplicationDelegate)].</p>
752 *
753 * <p><strong>Subclassing</strong> should be a last resort, and delegate
754 * methods should be used in most cases.  However, subclassing is most
755 * frequently done to implement custom event loop management by overriding
756 * -run when the method described above is not sufficient, or to intercept
757 * events by overriding -sendEvent: .</p>
758 */
759@implementation NSApplication
760
761static BOOL _isAutolaunchChecked = NO;
762
763/*
764 * Class methods
765 */
766+ (void) initialize
767{
768  if (self == [NSApplication class])
769    {
770      CREATE_AUTORELEASE_POOL(pool);
771      /*
772       * Dummy functions to fool linker into linking files that contain
773       * only catagories - static libraries seem to have problems here.
774       */
775      extern void	GSStringDrawingDummyFunction(void);
776
777      GSStringDrawingDummyFunction();
778
779      [self setVersion: 1];
780
781      /* Cache the NSAutoreleasePool class */
782      arpClass = [NSAutoreleasePool class];
783      nc = [NSNotificationCenter defaultCenter];
784      [pool drain];
785    }
786}
787
788// Helper method
789+ (void) _invokeWithAutoreleasePool: (NSInvocation*) inv
790{
791  CREATE_AUTORELEASE_POOL(pool);
792
793  [inv invoke];
794  [pool drain];
795}
796
797/**
798 * Calls [NSThread+detachNewThreadSelector:toTarget:withObject:] with the
799 * invocation wrapped by an autorelease pool.
800 */
801+ (void) detachDrawingThread: (SEL)selector
802		    toTarget: (id)target
803		  withObject: (id)argument
804{
805  NSInvocation *inv;
806
807  inv = [NSInvocation
808            invocationWithMethodSignature:
809                [target methodSignatureForSelector: selector]];
810  [inv setTarget: target];
811  [inv setSelector: selector];
812  [inv setArgument: argument atIndex: 2];
813  [NSThread detachNewThreadSelector: @selector(_invokeWithAutoreleasePool:)
814	    toTarget: self
815	    withObject: inv];
816}
817
818/**
819 * <p>Return the shared application instance, creating one (of the
820 * receiver class) if needed.  There is (and must always be) only a
821 * single shared application instance for each application.  After the
822 * shared application instance has been created, you can access it
823 * directly via the global variable <code>NSApp</code> (but not before!). When
824 * the shared application instance is created, it is also automatically
825 * initialized (that is, its <code>init</code> method is called), which
826 * connects to the window server and prepares the gui library for actual
827 * operation.  For this reason, you must always call <code>[NSApplication
828 * sharedApplication]</code> before using any functionality of the gui
829 * library - so, normally, this should be one of the first commands in
830 * your program (if you use <code>NSApplicationMain()</code>, this is
831 * automatically done).</p>
832 *
833 * <p>The shared application instance is normally an instance of
834 * NSApplication; but you can subclass NSApplication, and have an
835 * instance of your own subclass be created and used as the shared
836 * application instance.  If you want to get this result, you need to
837 * make sure the first time you call +sharedApplication is on your
838 * custom NSApplication subclass (rather than on NSApplication).
839 * Putting <code>[MyApplicationClass sharedApplication]</code>; as the first
840 * command in your program is the recommended way. :-) If you use
841 * <code>NSApplicationMain()</code>, it automatically creates the appropriate
842 * instance (which you can control by editing the info dictionary of
843 * the application).</p>
844 *
845 * <p>It is not safe to call this method from multiple threads - it would
846 * be useless anyway since the whole library is not thread safe: there
847 * must always be at most one thread using the gui library at a time.
848 * (If you absolutely need to have multiple threads in your
849 * application, make sure only one of them uses the gui [the 'drawing'
850 * thread], and the other ones do not).</p>
851 */
852+ (NSApplication *) sharedApplication
853{
854  /* If the global application does not yet exist then create it.  */
855  if (NSApp == nil)
856    {
857      /* -init sets NSApp.  */
858      [[self alloc] init];
859    }
860  return NSApp;
861}
862
863/*
864 * Instance methods
865 */
866
867/**
868 * The real gui initialisation ... called from -init
869 */
870- (void) _init
871{
872  GSDisplayServer *srv;
873  NSDictionary *attributes;
874  /* Initialization must be enclosed in an autorelease pool.  */
875  CREATE_AUTORELEASE_POOL(_app_init_pool);
876
877  /*
878   * Set NSApp as soon as possible, since other gui classes (which
879   * we refer or use in this method) might be calling [NSApplication
880   * sharedApplication] during their initialization, and we want
881   * those calls to succeed.
882   */
883  NSApp = self;
884
885  /* Initialize the backend here.  */
886  initialize_gnustep_backend();
887
888  /* Load user-defined bundles */
889  gsapp_user_bundles();
890
891  /* Connect to our window server.  */
892  srv = [GSDisplayServer serverWithAttributes: nil];
893  RETAIN(srv);
894  [GSDisplayServer setCurrentServer: srv];
895
896  /* Create a default context with the attributes of the main screen.  */
897  attributes = [[NSScreen mainScreen] deviceDescription];
898  _default_context = [NSGraphicsContext graphicsContextWithAttributes: attributes];
899  RETAIN(_default_context);
900  [NSGraphicsContext setCurrentContext: _default_context];
901
902  /* Initialize font manager.  */
903  [NSFontManager sharedFontManager];
904
905  _hidden = [[NSMutableArray alloc] init];
906  _inactive = [[NSMutableArray alloc] init];
907  _unhide_on_activation = YES;
908  _app_is_hidden = NO;
909  /* Ivar already automatically initialized to NO when the app is
910     created.  */
911  //_app_is_active = NO;
912  //_main_menu = nil;
913  _windows_need_update = YES;
914
915  /* Save the base library exception handler */
916  defaultUncaughtExceptionHandler = NSGetUncaughtExceptionHandler();
917  /* Set a new exception handler for the gui library.  */
918  NSSetUncaughtExceptionHandler(_NSAppKitUncaughtExceptionHandler);
919
920  _listener = [GSServicesManager newWithApplication: self];
921
922  /* NSEvent doesn't use -init so we use +alloc instead of +new.  */
923  _current_event = [NSEvent alloc]; // no current event
924  null_event = [NSEvent alloc];    // create dummy event
925
926  /* We are the end of responder chain.  */
927  [self setNextResponder: nil];
928
929  /* Create our app icon.
930     NB We are doing this here because WindowMaker will not map the app icon
931     window unless it is the very first window being mapped. */
932  [self _appIconInit];
933
934  [_app_init_pool drain];
935}
936
937
938/**
939 * This method initializes an NSApplication instance.  It sets the
940 * shared application instance to be the receiver, and then connects
941 * to the window server and performs the actual gui library
942 * initialization.
943 *
944 * If there is a already a shared application instance, calling this
945 * method results in an assertion (and normally program abortion/crash).
946 *
947 * It is recommended that you /never/ call this method directly from
948 * your code!  It's called automatically (and only once) by
949 * [NSApplication sharedApplication].  You might override this method
950 * in subclasses (make sure to call super's :-), then your overridden
951 * method will automatically be called (guaranteed once in the
952 * lifetime of the application) when you call [MyApplicationClass
953 * sharedApplication].
954 *
955 * If you call this method from your code (which we discourage you
956 * from doing), it is <em>your</em> responsibility to make sure it is called
957 * only once (this is according to the openstep specification).  Since
958 * +sharedApplication automatically calls this method, making also
959 * sure it calls it only once, you definitely want to use
960 * +sharedApplication instead of calling -init directly.
961 */
962- (id) init
963{
964  /*
965   * As per openstep specification, calling -init twice is a bug in
966   * the program.  +sharedApplication automatically makes sure it
967   * never calls -init more than once, and programmers should normally
968   * use +sharedApplication in programs.
969   *
970   * Please refrain from trying to have this method work with multiple
971   * calls (such as returning NSApp instead of raising an assertion).
972   * No matter what you do, you can't protect subclass -init custom
973   * code from multiple executions by changing the implementation here
974   * - so it's just simpler and cleaner that multiple -init executions
975   * are always forbidden, and subclasses inherit exactly the same
976   * kind of multiple execution protection as the superclass has, and
977   * initialization code behaves always in the same way for this class
978   * and for subclasses.
979   */
980  NSAssert (NSApp == nil, _(@"[NSApplication -init] called more than once"));
981
982  /*
983   * The appkit should run in the main thread ... so to be sure we perform
984   * all the initialisation there.
985   */
986  [self performSelectorOnMainThread: @selector(_init)
987			 withObject: self
988		      waitUntilDone: YES];
989  return NSApp;
990}
991
992/**
993 * <p>Activates the application, sets the application icon, loads the main
994 * Nib file if <code>NSMainNibFile</code> is set in the application
995 * property list, posts an
996 * <code>NSApplicationWillFinishLaunchingNotification</code>, and takes care
997 * of a few other startup tasks.
998 * If you override this method, be sure to call <em>super</em>.</p>
999 *
1000 * <p>The -run method calls this the first time it is called, before starting
1001 * the event loop for the first time.</p>
1002 */
1003- (void) finishLaunching
1004{
1005  NSDocumentController	*sdc;
1006  NSUserDefaults	*defs = [NSUserDefaults standardUserDefaults];
1007  NSString		*filePath;
1008  NSArray		*windows_list;
1009  NSUInteger		count;
1010  NSUInteger		i;
1011  BOOL			hadDuplicates = NO;
1012  BOOL			didAutoreopen = NO;
1013  NSArray               *files = nil;
1014
1015  /* post notification that launch will finish */
1016  [nc postNotificationName: NSApplicationWillFinishLaunchingNotification
1017      object: self];
1018
1019  /* Register our listener to incoming services requests etc. */
1020  [_listener registerAsServiceProvider];
1021
1022  /*
1023   * Establish the current key and main windows.  We need to do this in case
1024   * the windows were created and set to be key/main earlier - before the
1025   * app was active.
1026   */
1027  windows_list = [self windows];
1028  count = [windows_list count];
1029  for (i = 0; i < count; i++)
1030    {
1031      NSWindow	*win = [windows_list objectAtIndex: i];
1032
1033      if ([win isKeyWindow] == YES)
1034	{
1035	  if (_key_window == nil)
1036	    {
1037	      _key_window = win;
1038	    }
1039	  else
1040	    {
1041	      hadDuplicates = YES;
1042	      NSDebugLog(@"Duplicate keyWindow ignored");
1043	      [win resignKeyWindow];
1044	    }
1045	}
1046      if ([win isMainWindow] == YES)
1047	{
1048	  if (_main_window == nil)
1049	    {
1050	      _main_window = win;
1051	    }
1052	  else
1053	    {
1054	      hadDuplicates = YES;
1055	      NSDebugLog(@"Duplicate mainWindow ignored");
1056	      [win resignMainWindow];
1057	    }
1058	}
1059    }
1060
1061  /*
1062   * If there was more than one window set as key or main, we must make sure
1063   * that the one we have recorded is the real one by making it become key/main
1064   * again.
1065   */
1066  if (hadDuplicates)
1067    {
1068      [_main_window resignMainWindow];
1069      [_main_window becomeMainWindow];
1070      [_main_window orderFrontRegardless];
1071      [_key_window resignKeyWindow];
1072      [_key_window becomeKeyWindow];
1073      [_key_window orderFrontRegardless];
1074    }
1075
1076  // Make sure there is one designated main window
1077  if (_main_window == nil)
1078    {
1079      for (i = 0; i < count; i++)
1080        {
1081          NSWindow	*win = [windows_list objectAtIndex: i];
1082
1083          if ([win isVisible] && [win canBecomeMainWindow])
1084            {
1085              _main_window = win;
1086              break;
1087            }
1088        }
1089    }
1090
1091  /* Register self as observer to window events. */
1092  [nc addObserver: self selector: @selector(_windowWillClose:)
1093      name: NSWindowWillCloseNotification object: nil];
1094  [nc addObserver: self selector: @selector(_windowDidBecomeKey:)
1095      name: NSWindowDidBecomeKeyNotification object: nil];
1096  [nc addObserver: self selector: @selector(_windowDidBecomeMain:)
1097      name: NSWindowDidBecomeMainNotification object: nil];
1098  [nc addObserver: self selector: @selector(_windowDidResignKey:)
1099      name: NSWindowDidResignKeyNotification object: nil];
1100  [nc addObserver: self selector: @selector(_windowDidResignMain:)
1101      name: NSWindowDidResignMainNotification object: nil];
1102
1103  /* register as observer for hide/unhide notifications */
1104  [[[NSWorkspace sharedWorkspace] notificationCenter]
1105    addObserver: self selector: @selector(_workspaceNotification:)
1106      name: GSHideOtherApplicationsNotification object: nil];
1107  [[[NSWorkspace sharedWorkspace] notificationCenter]
1108    addObserver: self selector: @selector(_workspaceNotification:)
1109      name: GSUnhideAllApplicationsNotification object: nil];
1110
1111  // Don't activate the application, when the delegate hid it
1112  if (![self isHidden])
1113    {
1114      [self activateIgnoringOtherApps: YES];
1115    }
1116
1117  /*
1118   * Instantiate the NSDocumentController if we are a doc-based app
1119   * and eventually reopen all autosaved documents
1120   */
1121  sdc = [NSDocumentController sharedDocumentController];
1122  if ([[sdc documentClassNames] count] > 0)
1123    {
1124      didAutoreopen = [sdc _reopenAutosavedDocuments];
1125    }
1126
1127  /*
1128   *	Now check to see if we were launched with arguments asking to
1129   *	open a file.  We permit some variations on the default name.
1130   */
1131
1132  if ((files = [self _openFiles]) != nil)
1133    {
1134      [_listener application: self openFiles: files];
1135    }
1136  else if ((filePath = [defs stringForKey: @"GSFilePath"]) != nil
1137    || (filePath = [defs stringForKey: @"NSOpen"]) != nil)
1138    {
1139      [_listener application: self openFile: filePath];
1140    }
1141  else if ((filePath = [defs stringForKey: @"GSTempPath"]) != nil)
1142    {
1143      [_listener application: self openTempFile: filePath];
1144    }
1145  else if ((filePath = [defs stringForKey: @"NSPrint"]) != nil)
1146    {
1147      [_listener application: self printFile: filePath];
1148      [self terminate: self];
1149    }
1150  else if (!didAutoreopen && ![defs boolForKey: @"autolaunch"])
1151    {
1152      // For document based applications we automatically open a fresh document
1153      // unless denied by the delegate. For non-document based applications we
1154      // open a fresh document only when requested by the delegate.
1155      // Note: We consider an application document based if the shared document
1156      // controller reports at least one editable type.
1157      BOOL docBased =
1158	[[sdc documentClassNames] count] > 0 && [sdc defaultType] != nil;
1159      BOOL shouldOpen = docBased ? YES : NO;
1160
1161      if ([_delegate respondsToSelector:
1162                       @selector(applicationShouldOpenUntitledFile:)])
1163        {
1164	  shouldOpen = [_delegate applicationShouldOpenUntitledFile: self];
1165	}
1166      if (shouldOpen)
1167	{
1168	  if (docBased)
1169	    {
1170	      NSError *err = nil;
1171	      if ([sdc openUntitledDocumentAndDisplay: YES error: &err] == nil)
1172		{
1173		  [sdc presentError: err];
1174		}
1175	    }
1176	  else if ([_delegate respondsToSelector:
1177                                @selector(applicationOpenUntitledFile:)])
1178	    {
1179	      [_delegate applicationOpenUntitledFile: self];
1180	    }
1181        }
1182    }
1183}
1184
1185/*
1186 * Posts <code>NSApplicationDidFinishLaunchingNotification</code>.
1187 *
1188 * <p>The -run method calls this the first time it is called, before starting
1189 * the event loop for the first time and after calling finishLaunching.</p>
1190 */
1191- (void) _didFinishLaunching
1192{
1193  /* finish the launching post notification that launching has finished */
1194  [nc postNotificationName: NSApplicationDidFinishLaunchingNotification
1195		    object: self];
1196
1197  NS_DURING
1198    {
1199      [[[NSWorkspace sharedWorkspace] notificationCenter]
1200          postNotificationName: NSWorkspaceDidLaunchApplicationNotification
1201          object: [NSWorkspace sharedWorkspace]
1202          userInfo: [self _notificationUserInfo]];
1203    }
1204  NS_HANDLER
1205    {
1206      NSLog (_(@"Problem during launch app notification: %@"),
1207             [localException reason]);
1208      [localException raise];
1209    }
1210  NS_ENDHANDLER
1211}
1212
1213- (void) dealloc
1214{
1215  GSDisplayServer *srv = GSServerForWindow(_app_icon_window);
1216
1217  if (srv == nil)
1218    {
1219      srv = GSCurrentServer();
1220    }
1221  [[[NSWorkspace sharedWorkspace] notificationCenter]
1222    removeObserver: self];
1223  [nc removeObserver: self];
1224
1225  RELEASE(_hidden);
1226  RELEASE(_inactive);
1227  RELEASE(_listener);
1228  RELEASE(null_event);
1229  RELEASE(_current_event);
1230
1231  /* We may need to tidy up nested modal session structures. */
1232  while (_session != 0)
1233    {
1234      NSModalSession tmp = _session;
1235
1236      _session = tmp->previous;
1237      NSZoneFree(NSDefaultMallocZone(), tmp);
1238    }
1239
1240  /* Release the menus, then set them to nil so we don't try updating
1241     them after they have been deallocated.  */
1242  DESTROY(_main_menu);
1243  DESTROY(_windows_menu);
1244
1245  TEST_RELEASE(_app_icon);
1246  TEST_RELEASE(_app_icon_window);
1247  TEST_RELEASE(_infoPanel);
1248
1249  /* Destroy the default context */
1250  [NSGraphicsContext setCurrentContext: nil];
1251  DESTROY(_default_context);
1252
1253  /* Close the server */
1254  [srv closeServer];
1255  DESTROY(srv);
1256
1257  [super dealloc];
1258}
1259
1260/**
1261 * Activate app unconditionally if flag is YES, otherwise only if no other app
1262 * is active.  (<em><strong>Note:</strong> this is currently not implemented
1263 * under GNUstep.  The app is always activated unconditionally.</em>)  Usually
1264 * it is not necessary to manually call this method, except in some
1265 * circumstances of interapplication communication.
1266 */
1267- (void) activateIgnoringOtherApps: (BOOL)flag
1268{
1269  if (_isAutolaunchChecked == NO)
1270    {
1271      NSUserDefaults *defaults = [NSUserDefaults standardUserDefaults];
1272      NSString       *autolaunch = [defaults objectForKey: @"autolaunch"];
1273
1274      _isAutolaunchChecked = YES;
1275
1276      /* Application was executed with an argument '-autolaunch YES'.
1277         Do not activate application on first call. */
1278      if (autolaunch && [autolaunch isEqualToString: @"YES"])
1279        {
1280          return;
1281        }
1282    }
1283
1284  // TODO: Currently the flag is ignored
1285  if (_app_is_active == NO)
1286    {
1287      NSUInteger	count;
1288      NSUInteger	i;
1289      NSDictionary	*info;
1290
1291     /*
1292       * Menus should observe this notification in order to make themselves
1293       * visible when the application is active.
1294       */
1295      [nc postNotificationName: NSApplicationWillBecomeActiveNotification
1296          object: self];
1297
1298      _app_is_active = YES;
1299
1300      if ([[NSUserDefaults standardUserDefaults]
1301	boolForKey: @"GSSuppressAppIcon"])
1302	{
1303	  [_app_icon_window orderOut: self];
1304	}
1305
1306      /* Make sure to calculate count after the notification, since
1307         inactive status might be changed by a notifiee.  */
1308      count = [_inactive count];
1309      for (i = 0; i < count; i++)
1310        {
1311          [[_inactive objectAtIndex: i] orderFrontRegardless];
1312        }
1313      [_inactive removeAllObjects];
1314
1315      if (_unhide_on_activation)
1316        {
1317          [self unhide: nil];
1318        }
1319
1320      if ([self keyWindow] == nil && _hidden_key != nil
1321          && [[self windows] indexOfObjectIdenticalTo: _hidden_key] != NSNotFound)
1322        {
1323          [_hidden_key makeKeyWindow];
1324          _hidden_key = nil;
1325        }
1326
1327      if ([self mainWindow] == nil && _hidden_main != nil
1328          && [[self windows] indexOfObjectIdenticalTo: _hidden_main] != NSNotFound)
1329        {
1330          [_hidden_main makeMainWindow];
1331          _hidden_main = nil;
1332        }
1333
1334      if ([self keyWindow] != nil)
1335        {
1336          [[self keyWindow] orderFront: self];
1337        }
1338      else if ([self mainWindow] != nil)
1339        {
1340          [[self mainWindow] makeKeyAndOrderFront: self];
1341        }
1342      else
1343        {
1344          /* We need give input focus to some window otherwise we'll never get
1345             keyboard events. FIXME: doesn't work. */
1346          NSWindow *menu_window= [[self mainMenu] window];
1347          NSDebugLLog(@"Focus", @"No key on activation - make menu key");
1348          [GSServerForWindow(menu_window) setinputfocus:
1349                                [menu_window windowNumber]];
1350        }
1351
1352      info = [self _notificationUserInfo];
1353      [nc postNotificationName: NSApplicationDidBecomeActiveNotification
1354          object: self
1355		      userInfo: info];
1356      [[[NSWorkspace sharedWorkspace] notificationCenter]
1357          postNotificationName: NSApplicationDidBecomeActiveNotification
1358		      object: [NSWorkspace sharedWorkspace]
1359          userInfo: info];
1360    }
1361}
1362
1363/**
1364 * Forcefully deactivate the app, without activating another.  It is rarely
1365 * necessary to use this method.
1366 */
1367- (void) deactivate
1368{
1369  if (_app_is_active == YES)
1370    {
1371      NSArray		*windows_list;
1372      NSDictionary	*info;
1373      NSWindow		*win;
1374      NSEnumerator	*iter;
1375
1376      [nc postNotificationName: NSApplicationWillResignActiveNotification
1377          object: self];
1378
1379      _app_is_active = NO;
1380
1381      if ([self keyWindow] != nil)
1382        {
1383          _hidden_key = [self keyWindow];
1384          [_hidden_key resignKeyWindow];
1385        }
1386      // The main window is saved for when the app is activated again.
1387      // This is necessary for menu in window.
1388      if ([self mainWindow] != nil)
1389        {
1390          _hidden_main = [self mainWindow];
1391          [_hidden_main resignMainWindow];
1392        }
1393
1394      windows_list = GSOrderedWindows();
1395      iter = [windows_list reverseObjectEnumerator];
1396
1397      while ((win = [iter nextObject]))
1398        {
1399          NSModalSession theSession;
1400
1401          if ([win isVisible] == NO)
1402            {
1403              continue;		/* Already invisible	*/
1404            }
1405          if ([win canHide] == NO)
1406            {
1407              continue;		/* Can't be hidden	*/
1408            }
1409          if (win == _app_icon_window)
1410            {
1411              continue;		/* can't hide the app icon.	*/
1412            }
1413          /* Don't order out modal windows */
1414          theSession = _session;
1415          while (theSession != 0)
1416            {
1417              if (win == theSession->window)
1418                break;
1419              theSession = theSession->previous;
1420            }
1421          if (theSession)
1422            continue;
1423
1424          if ([win hidesOnDeactivate] == YES)
1425            {
1426	      /* NB Order is important here. When a hide on deactivate window
1427		 is ordered out while the application is inactive, it gets
1428		 removed from the _inactive list. Therefore, we must first
1429		 order out the window and then add it to the _inactive list. */
1430              [win orderOut: self];
1431              [_inactive addObject: win];
1432            }
1433        }
1434
1435      if (YES == [[NSUserDefaults standardUserDefaults]
1436	boolForKey: @"GSSuppressAppIcon"])
1437	{
1438#if	MINI_ICON
1439	  NSRect	f = [[[self mainMenu] window] frame];
1440	  NSPoint	p = f.origin;
1441
1442	  p.y += f.size.height;
1443          [_app_icon_window setFrameTopLeftPoint: p];
1444	  [_app_icon_window orderFrontRegardless];
1445          [_app_icon_window miniaturize: self];
1446#else
1447	  [_app_icon_window orderFrontRegardless];
1448#endif
1449	}
1450
1451      info = [self _notificationUserInfo];
1452      [nc postNotificationName: NSApplicationDidResignActiveNotification
1453          object: self
1454		      userInfo: info];
1455      [[[NSWorkspace sharedWorkspace] notificationCenter]
1456          postNotificationName: NSApplicationDidResignActiveNotification
1457		      object: [NSWorkspace sharedWorkspace]
1458          userInfo: info];
1459    }
1460}
1461
1462/**
1463 * Returns whether this app is the currently active GNUstep application.
1464 * Note that on a GNUstep system, unlike OS X, it is possible for NO GNUstep
1465 * app to be active.
1466 */
1467- (BOOL) isActive
1468{
1469  return _app_is_active;
1470}
1471
1472/**
1473 * Cause all other apps to hide themselves.
1474 */
1475- (void) hideOtherApplications: (id)sender
1476{
1477  [[[NSWorkspace sharedWorkspace] notificationCenter]
1478    postNotificationName: GSHideOtherApplicationsNotification
1479		  object: [NSWorkspace sharedWorkspace]
1480		userInfo: [self _notificationUserInfo]];
1481}
1482
1483/**
1484 * Cause all apps including this one to unhide themselves.
1485 */
1486- (void) unhideAllApplications: (id)sender
1487{
1488  [[[NSWorkspace sharedWorkspace] notificationCenter]
1489    postNotificationName: GSUnhideAllApplicationsNotification
1490		  object: [NSWorkspace sharedWorkspace]
1491		userInfo: [self _notificationUserInfo]];
1492}
1493
1494#define NSLogUncaughtExceptionMask 1
1495#define NSHandleUncaughtExceptionMask 2
1496#define NSLogUncaughtSystemExceptionMask 4
1497#define NSHandleUncaughtSystemExceptionMask 8
1498#define NSLogRuntimeErrorMask 16
1499#define NSLogUncaughtRuntimeErrorMask 32
1500
1501/**
1502 * Private method to handle an exception which occurs in the application runloop.
1503 */
1504- (void) _handleException: (NSException *)exception
1505{
1506  NSInteger mask = [[NSUserDefaults standardUserDefaults] integerForKey: @"NSExceptionHandlerMask"];
1507
1508  /**
1509   * If we are in debug mode, then rethrow the exception so that
1510   * the application can be stopped.
1511   **/
1512
1513  // log the exception.
1514  if (mask & NSLogUncaughtExceptionMask)
1515    {
1516      [self reportException: exception];
1517    }
1518
1519  // allow the default handler to handle the exception.
1520  if (mask & NSHandleUncaughtExceptionMask)
1521    {
1522      [exception raise];
1523    }
1524}
1525
1526/*
1527 * Running the main event loop
1528 */
1529
1530/**
1531 * <p>This method first calls -finishLaunching (if this is the first time -run)
1532 * has been called, then starts the main event loop of the application which
1533 * continues until -terminate: or -stop: is called.</p>
1534 *
1535 * <p>At each iteration, at most one event is dispatched, the main and services
1536 * menus are sent [NSMenu-update] messages, and -updateWindows is possibly
1537 * called if this has been flagged as necessary.</p>
1538 */
1539- (void) run
1540{
1541  NSEvent *e;
1542  id distantFuture = [NSDate distantFuture];     /* Cache this, safe */
1543
1544  if (_runLoopPool != nil)
1545    {
1546      [NSException raise: NSInternalInconsistencyException
1547		   format: @"NSApp's run called recursively"];
1548    }
1549
1550  /*
1551   *  Set this flag here in case the application is actually terminated
1552   *  inside -finishLaunching.
1553   */
1554  _app_is_running = YES;
1555
1556  if (_app_is_launched == NO)
1557    {
1558      _app_is_launched = YES;
1559      IF_NO_GC(_runLoopPool = [arpClass new]);
1560
1561      [self finishLaunching];
1562      [self _didFinishLaunching];
1563
1564      [_listener updateServicesMenu];
1565      [_main_menu update];
1566      DESTROY(_runLoopPool);
1567    }
1568
1569  while (_app_is_running)
1570    {
1571      IF_NO_GC(_runLoopPool = [arpClass new]);
1572
1573      // Catch and report any uncaught exceptions.
1574      NS_DURING
1575	{
1576	  e = [self nextEventMatchingMask: NSAnyEventMask
1577				untilDate: distantFuture
1578				   inMode: NSDefaultRunLoopMode
1579				  dequeue: YES];
1580
1581	  if (e != nil)
1582	    {
1583	      NSEventType	type = [e type];
1584
1585	      [self sendEvent: e];
1586
1587	      // update (en/disable) the services menu's items
1588	      if (type != NSPeriodic && type != NSMouseMoved)
1589		{
1590		  [_listener updateServicesMenu];
1591		  [_main_menu update];
1592		}
1593	    }
1594	}
1595      NS_HANDLER
1596	{
1597	  [self _handleException: localException];
1598	}
1599      NS_ENDHANDLER;
1600
1601      DESTROY (_runLoopPool);
1602    }
1603
1604  /* Every single non trivial line of code must be enclosed into an
1605     autorelease pool.  Create an autorelease pool here to wrap
1606     synchronize and the NSDebugLog.  */
1607  IF_NO_GC(_runLoopPool = [arpClass new]);
1608
1609  [[NSUserDefaults standardUserDefaults] synchronize];
1610  DESTROY (_runLoopPool);
1611}
1612
1613/**
1614 *  Returns whether the event loop managed by -run is currently active.
1615 */
1616- (BOOL) isRunning
1617{
1618  return _app_is_running;
1619}
1620
1621/*
1622 * Running modal event loops
1623 */
1624
1625/**
1626 * Halts a currently running modal event loop started by -runModalForWindow:
1627 * or -runModalSession: .  If you wish to halt the session in response to
1628 * user interaction with the modal window, use -stopModalWithCode: or
1629 * -stopModal instead; only use this to halt the loop from elsewhere, such as
1630 * another thread.
1631 */
1632- (void) abortModal
1633{
1634/* FIXME: The previous, now commented out, code here did only work from within the modal loop,
1635   which is contrary to the purpose of this method. Calling stopModalWithCode: works a bit better,
1636   but still relies on the modal loop to cooperate. Calling that method via performSelectorOnMainThread:...
1637   and moving the exception into stopModalWithCode:, looks like another option. Still this would
1638   rely on the loop getting executed.
1639
1640  if (_session == 0)
1641    {
1642      [NSException raise: NSAbortModalException
1643		  format: @"abortModal called while not in a modal session"];
1644    }
1645  [NSException raise: NSAbortModalException format: @"abortModal"];
1646*/
1647  [self stopModalWithCode: NSRunAbortedResponse];
1648}
1649
1650/**
1651 * Set up modal session for theWindow, and, if it is not visible already,
1652 * puts it up on screen, centering it if it is an NSPanel.  It is then
1653 * ordered front and made key or main window.
1654 */
1655- (NSModalSession) beginModalSessionForWindow: (NSWindow*)theWindow
1656{
1657  NSModalSession theSession;
1658
1659  theSession = (NSModalSession)NSZoneMalloc(NSDefaultMallocZone(),
1660		    sizeof(struct _NSModalSession));
1661  theSession->runState = NSRunContinuesResponse;
1662  theSession->entryLevel = [theWindow level];
1663  theSession->window = theWindow;
1664  theSession->previous = _session;
1665  _session = theSession;
1666
1667  /*
1668   * Displaying / raising window but centering panel only if not up
1669   * seems to match the behavior on OS X (Panther).
1670   */
1671  if ([theWindow isKindOfClass: [NSPanel class]])
1672    {
1673      if ([theWindow isVisible] == NO)
1674          [theWindow center];
1675      [theWindow setLevel: NSModalPanelWindowLevel];
1676    }
1677  [theWindow orderFrontRegardless];
1678  if ([self isActive] == YES)
1679    {
1680      if ([theWindow canBecomeKeyWindow] == YES)
1681	{
1682	  [theWindow makeKeyWindow];
1683	}
1684      else if ([theWindow canBecomeMainWindow] == YES)
1685	{
1686	  [theWindow makeMainWindow];
1687	}
1688    }
1689
1690  return theSession;
1691}
1692
1693/**
1694 * Clean up after a modal session has been run.  Called with theSession
1695 * returned from a previous call to beginModalSessionForWindow: .
1696 */
1697- (void) endModalSession: (NSModalSession)theSession
1698{
1699  NSModalSession	tmp = _session;
1700  NSArray		*windows = [self windows];
1701
1702  if (theSession == 0)
1703    {
1704      [NSException raise: NSInvalidArgumentException
1705		  format: @"null pointer passed to endModalSession:"];
1706    }
1707  /* Remove this session from linked list of sessions. */
1708  while (tmp != 0 && tmp != theSession)
1709    {
1710      tmp = tmp->previous;
1711    }
1712  if (tmp == 0)
1713    {
1714      [NSException raise: NSInvalidArgumentException
1715		  format: @"unknown session passed to endModalSession:"];
1716    }
1717  while (_session != theSession)
1718    {
1719      tmp = _session;
1720      _session = tmp->previous;
1721      if ([windows indexOfObjectIdenticalTo: tmp->window] != NSNotFound)
1722	{
1723	  [tmp->window setLevel: tmp->entryLevel];
1724	}
1725      NSZoneFree(NSDefaultMallocZone(), tmp);
1726    }
1727  _session = _session->previous;
1728  if ([windows indexOfObjectIdenticalTo: theSession->window] != NSNotFound)
1729    {
1730      [theSession->window setLevel: theSession->entryLevel];
1731    }
1732  NSZoneFree(NSDefaultMallocZone(), theSession);
1733
1734  // Bring the next modal window to front
1735  if ((_session != 0) &&
1736      ([windows indexOfObjectIdenticalTo: _session->window] != NSNotFound))
1737    {
1738      NSWindow *theWindow = _session->window;
1739
1740      // Same code as in beginModalSessionForWindow:
1741      [theWindow orderFrontRegardless];
1742      if ([self isActive] == YES)
1743        {
1744          if ([theWindow canBecomeKeyWindow] == YES)
1745            {
1746              [theWindow makeKeyWindow];
1747            }
1748          else if ([theWindow canBecomeMainWindow] == YES)
1749            {
1750              [theWindow makeMainWindow];
1751            }
1752        }
1753    }
1754  else
1755    {
1756      [_main_menu update];
1757    }
1758}
1759
1760/**
1761 * Starts modal event loop for given window, after calling
1762 * -beginModalSessionForWindow:.  Loop is broken only by -stopModal ,
1763 * -stopModalWithCode: , or -abortModal , at which time -endModalSession:
1764 * is called automatically.
1765 */
1766- (NSInteger) runModalForWindow: (NSWindow*)theWindow
1767{
1768  NSModalSession theSession = 0;
1769  NSInteger code = NSRunContinuesResponse;
1770
1771  NS_DURING
1772    {
1773      NSDate		*limit;
1774      GSDisplayServer	*srv;
1775
1776      theSession = [self beginModalSessionForWindow: theWindow];
1777      limit = [NSDate distantFuture];
1778      srv = GSCurrentServer();
1779
1780      while (code == NSRunContinuesResponse)
1781	{
1782	  /*
1783	   * Try to handle events for this session, discarding others.
1784	   */
1785	  code = [self runModalSession: theSession];
1786	  if (code == NSRunContinuesResponse)
1787	    {
1788	      /*
1789	       * Wait until there are more events to handle.
1790	       */
1791	      DPSPeekEvent(srv, NSAnyEventMask, limit, NSModalPanelRunLoopMode);
1792	    }
1793	}
1794
1795      [self endModalSession: theSession];
1796    }
1797  NS_HANDLER
1798    {
1799      if (theSession != 0)
1800	{
1801	  NSWindow *win_to_close = theSession->window;
1802
1803	  [self endModalSession: theSession];
1804	  [win_to_close close];
1805	}
1806      if ([[localException name] isEqual: NSAbortModalException] == NO)
1807	{
1808	  [localException raise];
1809     	}
1810      code = NSRunAbortedResponse;
1811    }
1812  NS_ENDHANDLER
1813
1814  return code;
1815}
1816
1817/**
1818<p>
1819Processes any events for a modal session described by the theSession
1820variable. When finished, it returns the state of the session (i.e.
1821whether it is still running or has been stopped, etc)
1822</p>
1823<p>If there are no pending events for the session, this method returns
1824immediately.
1825</p>
1826<p>
1827 Although Apple's docs state that, before processing the events, it makes the
1828 session window key and orders the window front, this method does not attempt
1829 to do this, because: 1) we don't want to interfere with use of other apps
1830 during modal session for this app; 2) occasionally other windows are active
1831 and should be usable during modal sessions (e.g., a popup dialog from a modal
1832 window); 3) most of the time -beginModalSessionForWindow: will have been
1833 called in advance.  If the latter is not the case, you may need to order the
1834 window front yourself in advance.
1835</p>
1836<p>
1837See Also: -runModalForWindow:
1838</p>
1839*/
1840- (NSInteger) runModalSession: (NSModalSession)theSession
1841{
1842  NSAutoreleasePool	*pool;
1843  GSDisplayServer	*srv;
1844  BOOL		done = NO;
1845  NSEvent	*event;
1846  NSDate	*limit;
1847
1848  if (theSession != _session)
1849    {
1850      [NSException raise: NSInvalidArgumentException
1851		  format: @"runModalSession: with wrong session"];
1852    }
1853
1854  // Use the default context for all events.
1855  srv = GSCurrentServer();
1856
1857  // Only handle input which is already available.
1858  limit = [NSDate distantPast];
1859
1860  /*
1861   *	Deal with the events in the queue.
1862   */
1863  while (done == NO && theSession->runState == NSRunContinuesResponse)
1864    {
1865      IF_NO_GC(pool = [arpClass new]);
1866
1867      event = DPSGetEvent(srv, NSAnyEventMask, limit, NSModalPanelRunLoopMode);
1868      if (event != nil)
1869	{
1870	  NSWindow	*eventWindow = [event window];
1871
1872	  /*
1873	   * We handle events for the session window, events for any
1874	   * window which works when modal, and any window management
1875	   * events.  All others are ignored/discarded.
1876	   */
1877	  if (eventWindow == theSession->window
1878	    || [eventWindow worksWhenModal] == YES
1879	    || [event type] == NSAppKitDefined)
1880	    {
1881	      ASSIGN(_current_event, event);
1882	    }
1883	  else
1884	    {
1885	      event = nil;	// Ignore/discard this event.
1886	    }
1887	}
1888      else
1889	{
1890	  done = YES;		// No more events pending.
1891	}
1892
1893      if (event != nil)
1894	{
1895	  NSEventType	type = [_current_event type];
1896
1897	  [self sendEvent: _current_event];
1898
1899	  // update (en/disable) the services menu's items
1900	  if (type != NSPeriodic && type != NSMouseMoved)
1901	    {
1902	      [_listener updateServicesMenu];
1903	      [_main_menu update];
1904	    }
1905
1906	  /*
1907	   *	Check to see if the window has gone away - if so, end session.
1908	   */
1909	  if ([[self windows] indexOfObjectIdenticalTo: _session->window]
1910	    == NSNotFound
1911            || ![_session->window isVisible])
1912	    {
1913	      [self stopModal];
1914	    }
1915	}
1916      [pool drain];
1917    }
1918
1919  NSAssert(_session == theSession, @"Session was changed while running");
1920
1921  return theSession->runState;
1922}
1923
1924/**
1925<p>
1926   Returns the window that is part of the current modal session, if any.
1927</p>
1928<p>
1929See -runModalForWindow:
1930</p>
1931*/
1932- (NSWindow *) modalWindow
1933{
1934  if (_session != 0)
1935    return (_session->window);
1936  else
1937    return nil;
1938}
1939
1940/**
1941 * Stops the main run loop, as well as a modal session if it is running.
1942 */
1943- (void) stop: (id)sender
1944{
1945  if (_session != 0)
1946    [self stopModal];
1947  else
1948    {
1949      _app_is_running = NO;
1950      /*
1951       * add dummy event to queue to assure loop cycles
1952       * at least one more time
1953       */
1954      DPSPostEvent(GSCurrentServer(), null_event, NO);
1955    }
1956}
1957
1958/**<p> Stops a running modal session causing -runModalForWindow: or
1959 * -runModalSession: to return <code>NSRunStoppedResponse</code>.  Use this
1960 * or -stopModalWithCode: to end a modal session in response to user input.</p>
1961 * <p>See Also: -stopModalWithCode:</p>
1962 */
1963- (void) stopModal
1964{
1965  [self stopModalWithCode: NSRunStoppedResponse];
1966}
1967
1968/**
1969 * Stops a running modal session causing -runModalForWindow: or
1970 * -runModalSession: to return the specified integer code.  Use this
1971 * or -stopModal to end a modal session in response to user input.
1972 */
1973- (void) stopModalWithCode: (NSInteger)returnCode
1974{
1975  /* According to the spec, there is no exception which is thrown
1976   * if we are not currently in a modal session.   While it is not
1977   * good practice to call this when we're not, we shouldn't throw
1978   * an exception.
1979   */
1980  if (_session != 0 && _session->runState == NSRunContinuesResponse)
1981    {
1982      if (returnCode == NSRunContinuesResponse)
1983	{
1984	  [NSException raise: NSInvalidArgumentException
1985	    format: @"stopModalWithCode: with NSRunContinuesResponse"];
1986	}
1987      _session->runState = returnCode;
1988    }
1989}
1990
1991/**
1992 * Put up a modal window centered relative to docWindow.  On OS X this is
1993 * deprecated in favor of
1994 * -beginSheet:modalForWindow:modalDelegate:didEndSelector:contextInfo: .
1995 */
1996- (NSInteger) runModalForWindow: (NSWindow *)theWindow
1997               relativeToWindow: (NSWindow *)docWindow
1998{
1999  if ((docWindow != nil) && (theWindow != nil))
2000    {
2001      NSRect  docFrame = [docWindow frame];
2002      NSPoint point = docFrame.origin;
2003      NSRect  theFrame = [theWindow frame];
2004      NSSize  size  = theFrame.size;
2005
2006      // Calculate window position...
2007      point.x += (docFrame.size.width - size.width) / 2;
2008      point.y += (docFrame.size.height - size.height) / 2;
2009
2010      NSDebugLLog(@"NSWindow", @"Positioning window %@ relative to %@ at %@",
2011            NSStringFromRect(theFrame), NSStringFromRect(docFrame), NSStringFromPoint(point));
2012      // Position window...
2013      [theWindow setFrameOrigin: point];
2014    }
2015  [theWindow orderWindow: NSWindowAbove
2016	     relativeTo: [docWindow windowNumber]];
2017  return [self runModalForWindow: theWindow];
2018}
2019
2020/**
2021 * Put up a modal sheet sliding down from top of docWindow.  If modalDelegate
2022 * is non-nil and responds to didEndSelector (this is optional), it is invoked
2023 * after the session ends.  The selector should take three arguments:
2024 * NSWindow *, int, void *.  It is passed the sheet window, the return code,
2025 * and the contextInfo passed in here.
2026
2027 * <em>Under GNUstep, the sheet aspect is not implemented (just centers
2028 * window on the screen), but modalDelegate didEndSelector is called if
2029 * both non-nil.</em>
2030 */
2031- (void) beginSheet: (NSWindow *)sheet
2032     modalForWindow: (NSWindow *)docWindow
2033      modalDelegate: (id)modalDelegate
2034     didEndSelector: (SEL)didEndSelector
2035	contextInfo: (void *)contextInfo
2036{
2037  // FIXME
2038  NSInteger ret;
2039
2040  [sheet setParentWindow: docWindow];
2041  [docWindow setAttachedSheet: sheet];
2042
2043  [[NSNotificationCenter defaultCenter]
2044          postNotificationName: NSWindowWillBeginSheetNotification
2045                        object: docWindow];
2046  ret = [self runModalForWindow: sheet
2047	      relativeToWindow: docWindow];
2048
2049  if (modalDelegate && [modalDelegate respondsToSelector: didEndSelector])
2050    {
2051      void (*didEnd)(id, SEL, id, NSInteger, void*);
2052
2053      didEnd = (void (*)(id, SEL, id, NSInteger, void*))[modalDelegate methodForSelector:
2054								 didEndSelector];
2055      didEnd(modalDelegate, didEndSelector, sheet, ret, contextInfo);
2056    }
2057
2058  [sheet close];
2059  [docWindow setAttachedSheet: nil];
2060  [sheet setParentWindow: nil];
2061  [[NSNotificationCenter defaultCenter]
2062          postNotificationName: NSWindowDidEndSheetNotification
2063                        object: docWindow];
2064}
2065
2066/**
2067 *  Analogous to -stopModal for sheets.
2068 */
2069- (void) endSheet: (NSWindow *)sheet
2070{
2071  // FIXME
2072  [self stopModal];
2073}
2074
2075/**
2076 *  Analogous to -stopModalWithCode: for sheets.
2077 */
2078- (void) endSheet: (NSWindow *)sheet
2079       returnCode: (NSInteger)returnCode
2080{
2081  // FIXME
2082  [self stopModalWithCode: returnCode];
2083}
2084
2085
2086/*
2087 * Getting, removing, and posting events
2088 */
2089
2090/* Private method used by GSDragView to dispatch drag events as Cocoa does. */
2091- (void) _postAndSendEvent: (NSEvent *)anEvent
2092{
2093  ASSIGN(_current_event, anEvent);
2094  [self sendEvent: anEvent];
2095}
2096
2097/**
2098 * Called by -run to dispatch events that are received according to AppKit's
2099 * forwarding conventions.  You rarely need to invoke this directly.  If you
2100 * want to synthesize an event for processing, call -postEvent:atStart: .
2101 */
2102- (void) sendEvent: (NSEvent *)theEvent
2103{
2104  NSEventType type;
2105
2106  type = [theEvent type];
2107  switch (type)
2108    {
2109      case NSPeriodic:	/* NSApplication traps the periodic events	*/
2110	break;
2111
2112      case NSKeyDown:
2113	{
2114	  NSDebugLLog(@"NSEvent", @"send key down event\n");
2115	  /* Key equivalents must be looked up explicitly in the Services menu
2116	     after checking the main menu, as NSMenu's -performKeyEquivalent:
2117	     ignores the Services menu. See the comment in that method for a
2118	     rationale. */
2119	  if ([[self keyWindow] performKeyEquivalent: theEvent] == NO
2120	    && [[self mainMenu] performKeyEquivalent: theEvent] == NO
2121	    && [[self servicesMenu] performKeyEquivalent: theEvent] == NO)
2122	    {
2123	      [[theEvent window] sendEvent: theEvent];
2124	    }
2125	  break;
2126	}
2127
2128      case NSKeyUp:
2129	{
2130	  NSDebugLLog(@"NSEvent", @"send key up event\n");
2131	  [[theEvent window] sendEvent: theEvent];
2132	  break;
2133	}
2134
2135      default:	/* pass all other events to the event's window	*/
2136	{
2137	  NSWindow *window = [theEvent window];
2138
2139	  if (!theEvent)
2140	    NSDebugLLog(@"NSEvent", @"NSEvent is nil!\n");
2141	  if (type == NSMouseMoved)
2142	    NSDebugLLog(@"NSMotionEvent", @"Send move (%d) to %@",
2143			(int)type, window);
2144	  else
2145	    NSDebugLLog(@"NSEvent", @"Send NSEvent type: %@ to %@",
2146			theEvent, window);
2147	  if (window)
2148	    [window sendEvent: theEvent];
2149	  else if (type == NSRightMouseDown)
2150	    [self rightMouseDown: theEvent];
2151	}
2152    }
2153}
2154
2155/**
2156 * Returns the most recent event -run pulled off the event queue.
2157 */
2158- (NSEvent*) currentEvent
2159{
2160  return _current_event;
2161}
2162
2163/* Utility for pen-device input.  See NSResponder. */
2164- (BOOL) shouldBeTreatedAsInkEvent: (NSEvent *)theEvent
2165{
2166  return [[theEvent window] shouldBeTreatedAsInkEvent: theEvent];
2167}
2168
2169/**
2170 * Drop events matching mask from the queue, before but not including
2171 * lastEvent.  The mask is a bitwise AND of event mask constants.
2172 * See (EventType) .  Use <code>NSAnyEventMask</code> to discard everything
2173 * up to lastEvent.
2174 */
2175- (void) discardEventsMatchingMask: (NSUInteger)mask
2176		       beforeEvent: (NSEvent *)lastEvent
2177{
2178  DPSDiscardEvents(GSCurrentServer(), mask, lastEvent);
2179}
2180
2181/**
2182 * Return the next event matching mask from the queue, dequeuing if flag is
2183 * YES.  Intervening events are NOT dequeued.  If no matching event is on the
2184 * queue, will wait for one until expiration, returning nil if none found.
2185 * See (EventType) for the list of masks.
2186 */
2187- (NSEvent*) nextEventMatchingMask: (NSUInteger)mask
2188			 untilDate: (NSDate*)expiration
2189			    inMode: (NSString*)mode
2190			   dequeue: (BOOL)flag
2191{
2192  NSEvent	*event;
2193
2194  if (_windows_need_update)
2195    {
2196      [self updateWindows];
2197    }
2198  if (!expiration)
2199    expiration = [NSDate distantPast];
2200
2201  if (flag)
2202    event = DPSGetEvent(GSCurrentServer(), mask, expiration, mode);
2203  else
2204    event = DPSPeekEvent(GSCurrentServer(), mask, expiration, mode);
2205
2206  if (event == null_event)
2207    {
2208      // Never return the null_event
2209      event = nil;
2210    }
2211
2212  if (event)
2213    {
2214IF_NO_GC(NSAssert([event retainCount] > 0, NSInternalInconsistencyException));
2215      /*
2216       * If we are not in a tracking loop, we may want to unhide a hidden
2217       * because the mouse has been moved.
2218       */
2219      if (mode != NSEventTrackingRunLoopMode)
2220	{
2221	  _windows_need_update = YES;
2222	  if ([NSCursor isHiddenUntilMouseMoves])
2223	    {
2224	      NSEventType type = [event type];
2225
2226	      if ((type == NSLeftMouseDown) || (type == NSLeftMouseUp)
2227		|| (type == NSOtherMouseDown) || (type == NSOtherMouseUp)
2228		|| (type == NSRightMouseDown) || (type == NSRightMouseUp)
2229		|| (type == NSMouseMoved))
2230		{
2231		  [NSCursor setHiddenUntilMouseMoves: NO];
2232		}
2233	    }
2234	}
2235
2236      if (flag)
2237        ASSIGN(_current_event, event);
2238    }
2239  return event;
2240}
2241
2242/**
2243 * Add an event to be processed by the app, either at end or at beginning
2244 * (next up) if flag is YES.  This provides a way to provide synthetic input
2245 * to an application, or, for whatever reason, to allow a real event to be
2246 * re-dispatched.
2247 */
2248- (void) postEvent: (NSEvent *)event atStart: (BOOL)flag
2249{
2250  DPSPostEvent(GSCurrentServer(), event, flag);
2251}
2252
2253/**
2254 * Sends the aSelector message to the receiver returned by the
2255 * -targetForAction:to:from: method (to which the aTarget and sender
2256 * arguments are passed).<br />
2257 * The method in the receiver must expect a single argument ...
2258 * the sender.<br />
2259 * Any value returned by the method in the receiver is ignored.<br />
2260 * This method returns YES on success, NO on failure (when no receiver
2261 * can be found for aSelector).
2262 */
2263- (BOOL) sendAction: (SEL)aSelector to: (id)aTarget from: (id)sender
2264{
2265  id resp = [self targetForAction: aSelector to: aTarget from: sender];
2266
2267  if (resp != nil)
2268    {
2269      IMP actionIMP = [resp methodForSelector: aSelector];
2270
2271      if (0 != actionIMP)
2272        {
2273          actionIMP(resp, aSelector, sender);
2274          return YES;
2275        }
2276    }
2277
2278  return NO;
2279}
2280
2281/**
2282 * If theTarget responds to theAction it is returned, otherwise
2283 * the application searches for an object which will handle
2284 * theAction and returns the first object found.<br />
2285 * Returns nil on failure.
2286 */
2287- (id) targetForAction: (SEL)theAction to: (id)theTarget from: (id)sender
2288{
2289  /*
2290   * If target responds to the selector then have it perform it.
2291   */
2292  if (theTarget)
2293    {
2294      if ([theTarget respondsToSelector: theAction])
2295        {
2296          return theTarget;
2297        }
2298      else
2299        {
2300          NSDebugLog(@"Target %@ does not respont to action %@", theTarget, NSStringFromSelector(theAction));
2301          return nil;
2302        }
2303    }
2304  else if ([sender isKindOfClass: [NSToolbarItem class]])
2305    {
2306      /* Special case for toolbar items which must look up the target in the
2307         responder chain of the window containing their toolbar not in the key
2308         or main window.
2309         Note: If (and only if) the toolbar's window is key window we must
2310         pass it as such to _targetForAction:... so that toolbar items in a
2311         modal dialog panel work.
2312       */
2313      NSWindow *toolbarWindow =
2314	[[[(NSToolbarItem *)sender toolbar] _toolbarView] window];
2315      NSWindow *keyWindow = [self keyWindow];
2316      if (keyWindow != toolbarWindow)
2317        keyWindow = nil;
2318      return [self _targetForAction: theAction
2319			  keyWindow: keyWindow
2320			 mainWindow: toolbarWindow];
2321    }
2322  else
2323    {
2324      return [self targetForAction: theAction];
2325    }
2326}
2327
2328/**
2329 * <p>
2330 *   Returns the target object that will respond to aSelector, if any. The
2331 *   method first checks if any of the key window's first responders, the
2332 *   key window or its delegate responds. Next it checks the main window in
2333 *   the same way. Finally it checks the receiver (NSApplication) and its
2334 *   delegate.
2335 * </p>
2336 */
2337- (id) targetForAction: (SEL)aSelector
2338{
2339  if (!aSelector)
2340    {
2341      return nil;
2342    }
2343
2344  /* During a modal session actions must not be sent to the main window of
2345   * the application, but rather to the dialog window of the modal session.
2346   * Note that the modal session window is not necessarily the key window,
2347   * as a panel with worksWhenModal = YES, e.g., the font panel, can still
2348   * become key window during a modal session.
2349   */
2350  NSWindow *mainWindow = [self mainWindow];
2351  if (_session != 0)
2352    mainWindow = _session->window;
2353  return [self _targetForAction: aSelector
2354		      keyWindow: [self keyWindow]
2355		     mainWindow: mainWindow];
2356}
2357
2358
2359/**
2360 * Attempts to perform aSelector using [NSResponder-tryToPerform:with:]
2361 * and if that is not possible, attempts to get the application
2362 * delegate to perform the aSelector.<br />
2363 * Returns YES if an object was found to perform aSelector, NO otherwise.
2364 */
2365- (BOOL) tryToPerform: (SEL)aSelector with: (id)anObject
2366{
2367  if ([super tryToPerform: aSelector with: anObject] == YES)
2368    {
2369      return YES;
2370    }
2371  if (_delegate != nil && [_delegate respondsToSelector: aSelector])
2372    {
2373      IMP actionIMP = [_delegate methodForSelector: aSelector];
2374      if (0 != actionIMP)
2375        {
2376          actionIMP(_delegate, aSelector, anObject);
2377          return YES;
2378        }
2379    }
2380  return NO;
2381}
2382
2383/**<p>Sets the application's icon. Any windows that use the old application
2384icon image as their mini window image will be updated to use the new
2385image.</p><p>See Also: -applicationIconImage</p>
2386*/
2387- (void) setApplicationIconImage: (NSImage*)anImage
2388{
2389  NSEnumerator *iterator;
2390  NSWindow *current;
2391  NSImage *old_app_icon = _app_icon;
2392  NSSize miniWindowSize;
2393  NSSize imageSize;
2394  GSDisplayServer *server;
2395
2396  // Ignore attempts to set nil as the icon image.
2397  if (nil == anImage)
2398    return;
2399
2400  RETAIN(old_app_icon);
2401
2402  // Use a copy as we change the name and size
2403  ASSIGNCOPY(_app_icon, anImage);
2404
2405  server = GSCurrentServer();
2406  miniWindowSize = server != 0 ? [server iconSize] : NSZeroSize;
2407  if (miniWindowSize.width <= 0 || miniWindowSize.height <= 0)
2408    {
2409      miniWindowSize = NSMakeSize(48, 48);
2410    }
2411
2412  // restrict size when the icon is larger than the mini window.
2413  imageSize = [_app_icon size];
2414  if (imageSize.width > miniWindowSize.width
2415    || imageSize.height > miniWindowSize.height)
2416    {
2417      [_app_icon setSize: miniWindowSize];
2418    }
2419
2420  // Let horizontal menu change icon
2421  [_main_menu _organizeMenu];
2422
2423  if (_app_icon_window != nil)
2424    {
2425      [(NSAppIconView *)[_app_icon_window contentView] setImage: _app_icon];
2426    }
2427
2428  // Swap the old image for the new one wherever it's used
2429  iterator = [[self windows] objectEnumerator];
2430  while ((current = [iterator nextObject]) != nil)
2431    {
2432      if ([current miniwindowImage] == old_app_icon)
2433        [current setMiniwindowImage: _app_icon];
2434    }
2435
2436  DESTROY(old_app_icon);
2437}
2438
2439/**<p>Returns the current icon be used for the application.</p>
2440   <p>See Also: -setApplicationIconImage:</p>
2441 */
2442- (NSImage*) applicationIconImage
2443{
2444  if (!_app_icon)
2445    {
2446      /* load the application icon */
2447      [self _loadAppIconImage];
2448    }
2449  return _app_icon;
2450}
2451
2452/**
2453 * Returns the actual window object being used to display the application
2454 * icon (usually in the dock).
2455 */
2456- (NSWindow*) iconWindow
2457{
2458  return _app_icon_window;
2459}
2460
2461/*
2462 * Hiding and arranging windows
2463 */
2464
2465/**<p> Request this application to "hide" (unmap all windows from the screen
2466 * except the icon window).  Posts
2467 * <code>NSApplicationWillHideNotification</code> and
2468 * <code>NSApplicationDidHideNotification</code>.  On OS X this activates
2469 * the next app that is running, however on GNUstep this is up to the window
2470 * manager.</p><p>See Also: -unhide: -isHidden</p>
2471 */
2472- (void) hide: (id)sender
2473{
2474#ifdef __MINGW32__
2475  [self miniaturizeAll: sender];
2476#else
2477  if (_app_is_hidden == NO)
2478    {
2479      if (![[NSUserDefaults standardUserDefaults]
2480	     boolForKey: @"GSSuppressAppIcon"])
2481	{
2482	  NSArray		*windows_list;
2483	  NSDictionary  	*info;
2484	  NSWindow		*win;
2485	  NSEnumerator  	*iter;
2486
2487	  [nc postNotificationName: NSApplicationWillHideNotification
2488	                    object: self];
2489
2490	  if ([self keyWindow] != nil)
2491	    {
2492	      _hidden_key = [self keyWindow];
2493	      [_hidden_key resignKeyWindow];
2494	    }
2495
2496	  // The main window is saved for when the app is activated again.
2497	  // This is necessary for menu in window.
2498	  if ([self mainWindow] != nil)
2499	    {
2500	      _hidden_main = [self mainWindow];
2501	      [_hidden_main resignMainWindow];
2502	    }
2503
2504          /** Ask the window manager to hide all the application windows for us.
2505              Return whether they have been hidden. */
2506          win = [[self mainMenu] window];
2507          if ([GSServerForWindow(win) hideApplication: [win windowNumber]] == NO)
2508            {
2509              windows_list = GSOrderedWindows();
2510              iter = [windows_list reverseObjectEnumerator];
2511
2512              while ((win = [iter nextObject]))
2513                {
2514                  if ([win isVisible] == NO && ![win isMiniaturized])
2515                    {
2516                      continue;		/* Already invisible	*/
2517                    }
2518                  if ([win canHide] == NO)
2519                    {
2520                      continue;		/* Not hideable	*/
2521                    }
2522                  if (win == _app_icon_window)
2523                    {
2524                      continue;		/* can't hide the app icon.	*/
2525                    }
2526                  if (_app_is_active == YES && [win hidesOnDeactivate] == YES)
2527                    {
2528                      continue;		/* Will be hidden by deactivation	*/
2529                    }
2530                  [_hidden addObject: win];
2531                  [win orderOut: self];
2532                }
2533            }
2534	  _app_is_hidden = YES;
2535
2536	  if (YES == [[NSUserDefaults standardUserDefaults]
2537		       boolForKey: @"GSSuppressAppIcon"])
2538	    {
2539#if	MINI_ICON
2540	      NSRect	f = [[[self mainMenu] window] frame];
2541	      NSPoint	p = f.origin;
2542
2543	      p.y += f.size.height;
2544	      [_app_icon_window setFrameTopLeftPoint: p];
2545	      [_app_icon_window orderFrontRegardless];
2546	      [_app_icon_window miniaturize: self];
2547#else
2548	      [_app_icon_window orderFrontRegardless];
2549#endif
2550	    }
2551	  else
2552	    {
2553	      [[_app_icon_window contentView] setNeedsDisplay: YES];
2554	    }
2555
2556	  /*
2557	   * On hiding we also deactivate the application which will make the menus
2558	   * go away too.
2559	   */
2560	  [self deactivate];
2561	  _unhide_on_activation = YES;
2562
2563	  info = [self _notificationUserInfo];
2564	  [nc postNotificationName: NSApplicationDidHideNotification
2565			    object: self
2566		          userInfo: info];
2567	  [[[NSWorkspace sharedWorkspace] notificationCenter]
2568	    postNotificationName: NSApplicationDidHideNotification
2569		          object: [NSWorkspace sharedWorkspace]
2570		        userInfo: info];
2571	}
2572      else
2573	{
2574	  /*Minimize all windows if there isn't an AppIcon. This isn't the
2575	    most elegant solution, but avoids to loss the app if the user
2576	    hide it. */
2577	  [self miniaturizeAll: sender];
2578	}
2579    }
2580#endif
2581}
2582
2583/**<p>Returns whether app is currently in hidden state.</p>
2584   <p>See Also: -hide: -unhide:</p>
2585 */
2586- (BOOL) isHidden
2587{
2588  return _app_is_hidden;
2589}
2590
2591/**<p>Unhides and activates this application.</p>
2592   <p>See Also: -unhideWithoutActivation -hide: -isHidden</p>
2593 */
2594- (void) unhide: (id)sender
2595{
2596  if (_app_is_hidden)
2597    {
2598      [self unhideWithoutActivation];
2599      _unhide_on_activation = NO;
2600    }
2601  if (_app_is_active == NO)
2602    {
2603      /*
2604       * Activation should make the applications menus visible.
2605       */
2606      [self activateIgnoringOtherApps: YES];
2607    }
2608}
2609
2610/**
2611 * Unhides this app (displays its windows) but does not activate it.
2612 */
2613- (void) unhideWithoutActivation
2614{
2615  if (_app_is_hidden == YES)
2616    {
2617      NSDictionary	*info;
2618      NSUInteger	count;
2619      NSUInteger	i;
2620
2621      [nc postNotificationName: NSApplicationWillUnhideNotification
2622			object: self];
2623
2624      /* Make sure we set this before ordering windows to avoid possible
2625	 recursive loops (some methods window/backend methods check if
2626	 the app is hidden before ordering a window).  */
2627      _app_is_hidden = NO;
2628
2629      count = [_hidden count];
2630      for (i = 0; i < count; i++)
2631        {
2632          NSWindow *win = [_hidden objectAtIndex: i];
2633          [win orderFrontRegardless];
2634          if ([win isMiniaturized])
2635            {
2636              [GSServerForWindow(win) miniwindow: [win windowNumber]];
2637            }
2638        }
2639      [_hidden removeAllObjects];
2640      [[_app_icon_window contentView] setNeedsDisplay: YES];
2641
2642      info = [self _notificationUserInfo];
2643      [nc postNotificationName: NSApplicationDidUnhideNotification
2644			object: self
2645		      userInfo: info];
2646      [[[NSWorkspace sharedWorkspace] notificationCenter]
2647        postNotificationName: NSApplicationDidUnhideNotification
2648		      object: [NSWorkspace sharedWorkspace]
2649		    userInfo: info];
2650    }
2651}
2652
2653/**
2654 * Arranges all non-miniaturized app's windows in front by successively calling
2655 * [NSWindow-orderFront:] on each window in the app's Windows menu.
2656 */
2657- (void) arrangeInFront: (id)sender
2658{
2659  NSMenu	*menu;
2660
2661  menu = [self windowsMenu];
2662  if (menu)
2663    {
2664      NSArray	*itemArray;
2665      NSUInteger count;
2666      NSUInteger i;
2667
2668      itemArray = [menu itemArray];
2669      count = [itemArray count];
2670      for (i = 0; i < count; i++)
2671	{
2672	  id win = [(NSMenuItem*)[itemArray objectAtIndex: i] target];
2673
2674	  if ([win isKindOfClass: [NSWindow class]] &&
2675	      [win isVisible] && ![win isMiniaturized])
2676	    {
2677	      [win orderFront: sender];
2678	    }
2679	}
2680    }
2681}
2682
2683/*
2684 * User interface validation
2685 */
2686- (BOOL) validateUserInterfaceItem: (id <NSValidatedUserInterfaceItem>)anItem
2687{
2688  // FIXME
2689  return YES;
2690}
2691
2692/*
2693 * Managing windows
2694 */
2695
2696/**
2697 * Returns current key window.  If this app is active, one window should be
2698 * key.  If this app is not active, nil should be returned.  The key window
2699 * is the one that will receive keyboard input.
2700 */
2701- (NSWindow*) keyWindow
2702{
2703  return _key_window;
2704}
2705
2706/**
2707 * Returns current main window of this application. There need not necessarily
2708 * be a main window, even if app is active, and this should return nil if the
2709 * app is inactive.
2710 */
2711- (NSWindow*) mainWindow
2712{
2713  return _main_window;
2714}
2715
2716/**
2717 * Sends aSelector to each window, either in the app's internal window list
2718 * (flag = NO) or their stacking order from front to back on the screen
2719 * (flag = YES, <em>currently not implemented under GNUstep</em>).
2720 */
2721- (NSWindow*) makeWindowsPerform: (SEL)aSelector inOrder: (BOOL)flag
2722{
2723  NSArray *window_list;
2724  NSUInteger i, c;
2725
2726  // so i suppose when flag is YES it only runs on visible windows
2727  if (flag)
2728    {
2729      window_list = GSOrderedWindows();
2730    }
2731  else
2732    {
2733      window_list = [self windows];
2734    }
2735
2736  for (i = 0, c = [window_list count]; i < c; i++)
2737    {
2738      NSWindow *window = [window_list objectAtIndex: i];
2739
2740      if ([window performSelector: aSelector] != nil)
2741        {
2742          return window;
2743        }
2744    }
2745  return nil;
2746}
2747
2748/**
2749 * Iconify all windows in the app.
2750 */
2751- (void) miniaturizeAll: sender
2752{
2753  NSArray *window_list = [self windows];
2754  NSUInteger i, count;
2755
2756  for (i = 0, count = [window_list count]; i < count; i++)
2757    [[window_list objectAtIndex: i] miniaturize: sender];
2758}
2759
2760/**
2761 * Prevent window reordering in response to most recent mouse down (useful to
2762 * prevent raise-on-mouse-click).  <em>Not implemented under GNUstep.</em>
2763 */
2764- (void) preventWindowOrdering
2765{
2766  //TODO
2767}
2768
2769/**
2770 * Set whether the main run loop will request all visible windows update
2771 * themselves after the current or next event is processed.  (Update occurs
2772 * after event dispatch in the loop.)
2773 * This is needed when in NSEventTrackingRunLoopMode.  When the application
2774 * is using NSDefaultRunLoopMode or NSModalPanelRunLoopMode windows are updated
2775 * after each loop iteration irrespective of this setting.
2776 */
2777- (void) setWindowsNeedUpdate: (BOOL)flag
2778{
2779  _windows_need_update = flag;
2780}
2781
2782/**
2783 * Sends each of the app's visible windows an [NSWindow-update] message.
2784 * This method is called automatically for every iteration of the run loop
2785 * in NSDefaultRunLoopMode or NSModalPanelRunLoopMode, but is only called during
2786 * NSEventTrackingRunLoopMode if -setWindowsNeedUpdate: is set to YES.
2787 */
2788- (void) updateWindows
2789{
2790  NSArray	*window_list = [self windows];
2791  NSUInteger	count = [window_list count];
2792  NSUInteger	i;
2793
2794  _windows_need_update = NO;
2795  [nc postNotificationName: NSApplicationWillUpdateNotification object: self];
2796
2797  for (i = 0; i < count; i++)
2798    {
2799      NSWindow *win = [window_list objectAtIndex: i];
2800      if ([win isVisible])
2801	[win update];
2802    }
2803  [nc postNotificationName: NSApplicationDidUpdateNotification object: self];
2804}
2805
2806/**
2807 *  Returns array of app's visible and invisible windows.
2808 */
2809- (NSArray*) windows
2810{
2811  return GSAllWindows();
2812}
2813
2814/**
2815 * Returns window for windowNum.  Note the window number can be obtained for
2816 * a window from [NSWindow-windowNumber].
2817 */
2818- (NSWindow *) windowWithWindowNumber: (NSInteger)windowNum
2819{
2820  return GSWindowWithNumber(windowNum);
2821}
2822
2823/*
2824 * Showing Standard Panels
2825 */
2826
2827/** Calls -orderFrontStandardAboutPanelWithOptions: with nil passed as
2828    the options dictionary.
2829*/
2830- (void) orderFrontStandardAboutPanel: sender
2831{
2832  [self orderFrontStandardAboutPanelWithOptions: nil];
2833}
2834
2835/** OS X compatibility: Calls -orderFrontStandardInfoPanelWithOptions: .
2836*/
2837- (void) orderFrontStandardAboutPanelWithOptions: (NSDictionary *)dictionary
2838{
2839  [self orderFrontStandardInfoPanelWithOptions: dictionary];
2840}
2841
2842/* infoPanel, GNUstep API */
2843/** Calls -orderFrontStandardInfoPanelWithOptions: with nil passed as
2844    the options dictionary.
2845*/
2846- (void) orderFrontStandardInfoPanel: sender
2847{
2848  [self orderFrontStandardInfoPanelWithOptions: nil];
2849}
2850
2851/**
2852   <p>
2853   Orders front the standard info panel for the application,
2854   taking the needed information from the <code>dictionary</code>
2855   argument.  There is a single standard info panel per application;
2856   it is created the first time that this method is invoked, and then
2857   reused in all subsequent calls.  The application standard info
2858   panel is immutable and can not be changed after creation.  Useful
2859   keys for the <code>dictionary</code> are:
2860   </p>
2861
2862   <deflist>
2863   <term>ApplicationName</term>
2864   <desc>A string with the name of the
2865   application (eg, <var>"Gorm"</var>).  If not available, the
2866   <file>Info-gnustep.plist</file> file is searched for the value of
2867   <var>ApplicationName</var> followed by
2868   <var>NSHumanReadableShortName</var>. If this also fails, the
2869   string returned by [NSProcessInfo-processName] is used.
2870   </desc>
2871
2872   <term>ApplicationDescription</term>
2873   <desc> A string with a very short
2874   description of the application (eg, <var>"GNUstep Graphics
2875   Objects Relationship Modeller"</var>).  If not available,
2876   <file>Info-gnustep.plist</file> is searched for that key; if this
2877   fails, no application description is shown.
2878   </desc>
2879
2880   <term>ApplicationIcon</term>
2881   <desc> An image to be shown near the title.
2882   If not available, <file>Info-gnustep.plist</file> is searched for
2883   <var>ApplicationIcon</var>; if this fails, NSApp's -applicationIconImage
2884   method is used instead.
2885   </desc>
2886
2887   <term>ApplicationRelease</term>
2888   <desc> A string with the name of the
2889   application, release included (eg, <var>"Gorm 0.1"</var>).  If
2890   not available, the value for <var>ApplicationVersion</var> is
2891   used instead.  If this fails, <file>Info-gnustep.plist</file> is
2892   searched for <var>ApplicationRelease</var> or
2893   <var>NSAppVersion</var>, otherwise, <var>"Unknown"</var> is
2894   used.
2895   </desc>
2896
2897   <term>FullVersionID</term>
2898   <desc> A string with the full version of the
2899   application (eg, <var>"0.1.2b"</var> or
2900   <var>"snap011100"</var>).  If not available,
2901   <var>Version</var> is used instead.  If this fails,
2902   <file>Info-gnustep.plist</file> is looked for
2903   <var>NSBuildVersion</var>.  If all fails, no full version is
2904   shown.
2905   </desc>
2906
2907   <term>Authors</term>
2908   <desc> An array of strings, each one with the name
2909   of an author (eg, <var>[NSArray arrayWithObject: "Nicola Pero
2910   &lt;n.pero\@mi.flashnet.it&gt;"]</var>).  If not found,
2911   <file>Info-gnustep.plist</file> is searched for <var>Authors</var>,
2912   if this fails, <var>"Unknown"</var> is displayed.
2913   </desc>
2914
2915   <term>URL</term>
2916   <desc> [This field is still under work, so it might be
2917   changed] A string with an URL (eg, <var>"See
2918   http://www.gnustep.org"</var>).
2919   </desc>
2920
2921   <term>Copyright</term>
2922   <desc> A string with copyright owners (eg,
2923   <var>"Copyright (C) 2000 The Free Software Foundation,
2924   Inc."</var>).  Support for multiple line strings is planned but
2925   not yet available.  If not found, <file>Info-gnustep.plist</file>
2926   is searched for <var>Copyright</var> and then (failing this) for
2927   <var>NSHumanReadableCopyright</var>.  If all fails,
2928   <var>"Copyright Information Not Available"</var> is used.
2929   </desc>
2930
2931   <term>CopyrightDescription</term>
2932   <desc>A string describing the kind of
2933   copyright (eg, <var>"Released under the GNU General Public
2934   License 2.0"</var>).  If not available,
2935   <file>Info-gnustep.plist</file> is searched for
2936   <var>CopyrightDescription</var>.  If this fails, no copyright
2937   description is shown.
2938   </desc>
2939   </deflist>
2940 */
2941- (void) orderFrontStandardInfoPanelWithOptions: (NSDictionary *)dictionary
2942{
2943  if (_infoPanel == nil)
2944    _infoPanel = [[GSInfoPanel alloc] initWithDictionary: dictionary];
2945
2946  [_infoPanel setTitle: NSLocalizedString (@"Info",
2947					   @"Title of the Info Panel")];
2948  [_infoPanel orderFront: self];
2949}
2950
2951/*
2952 * Getting the main menu
2953 */
2954
2955/**
2956 * Returns the main menu of the receiver.
2957 */
2958- (NSMenu*) mainMenu
2959{
2960  return _main_menu;
2961}
2962
2963/**
2964 * Sets the main menu of the receiver.  This is sent update messages by the
2965 * main event loop.
2966 */
2967- (void) setMainMenu: (NSMenu*)aMenu
2968{
2969  if (_main_menu == aMenu)
2970    {
2971      return;
2972    }
2973
2974  if (_main_menu != nil)
2975    {
2976      [_main_menu setMain: NO];
2977    }
2978
2979  ASSIGN(_main_menu, aMenu);
2980
2981  if (_main_menu != nil)
2982    {
2983      [_main_menu setMain: YES];
2984    }
2985}
2986
2987/*
2988   Overrides to show transient version of main menu as in NeXTstep.
2989*/
2990- (void) rightMouseDown: (NSEvent*)theEvent
2991{
2992  // On right mouse down display the main menu transient
2993  if (_main_menu != nil)
2994    [NSMenu popUpContextMenu: _main_menu
2995	    withEvent: theEvent
2996	    forView: nil];
2997  else
2998    [super rightMouseDown: theEvent];
2999}
3000
3001/**
3002 *  Here for compatibility with OS X, but currently a no-op.
3003 */
3004- (void) setAppleMenu: (NSMenu*)aMenu
3005{
3006    //TODO: Unclear, what this should do.
3007}
3008
3009/*
3010 * Managing the Windows menu
3011 */
3012
3013/**
3014 * Adds an item to the app's Windows menu.  This is usually done automatically
3015 * so you don't need to call this method.
3016 */
3017- (void) addWindowsItem: (NSWindow*)aWindow
3018		  title: (NSString*)aString
3019	       filename: (BOOL)isFilename
3020{
3021  [self changeWindowsItem: aWindow  title: aString  filename: isFilename];
3022}
3023
3024/**
3025 * Removes an item from the app's Windows menu.  This is usually done
3026 * automatically so you don't need to call this method.
3027 */
3028- (void) removeWindowsItem: (NSWindow*)aWindow
3029{
3030  if (_windows_menu)
3031    {
3032      NSArray	*itemArray;
3033      NSUInteger count;
3034
3035      itemArray = [_windows_menu itemArray];
3036      count = [itemArray count];
3037      while (count-- > 0)
3038	{
3039	  NSMenuItem *item = [itemArray objectAtIndex: count];
3040
3041	  if ([item target] == aWindow)
3042	    {
3043	      [_windows_menu removeItemAtIndex: count];
3044	      return;
3045	    }
3046	}
3047    }
3048}
3049
3050// this is an internal helper method for changeWindowsItem:title:filename
3051// when window represents an editable document
3052- (void) setImageForWindowsItem: (NSMenuItem *)item
3053{
3054  NSImage *oldImage = [item image];
3055  NSImage *newImage;
3056
3057  if (!([[item target] styleMask] & NSClosableWindowMask))
3058    return;
3059
3060  if ([[item target] isDocumentEdited])
3061    {
3062      newImage = [NSImage imageNamed: @"common_CloseBroken"];
3063    }
3064  else
3065    {
3066      newImage = [NSImage imageNamed: @"common_Close"];
3067    }
3068
3069  if (newImage != oldImage)
3070    {
3071      [item setImage: newImage];
3072    }
3073}
3074
3075/** Changes the Window menu item associated with aWindow to aString. If no
3076    associated window item exists, one is created. If isFilename is YES, then
3077    aString is assumed to be a filename representation the way
3078    [NSWindow-setTitleWithRepresentedFilename:] would format it, otherwise
3079    the string is displayed literally in the menu item.
3080 */
3081- (void) changeWindowsItem: (NSWindow*)aWindow
3082		     title: (NSString*)aString
3083		  filename: (BOOL)isFilename
3084{
3085  NSArray	*itemArray;
3086  NSUInteger	count;
3087  NSUInteger	i;
3088  id		item;
3089
3090  if (![aWindow isKindOfClass: [NSWindow class]])
3091    [NSException raise: NSInvalidArgumentException
3092		 format: @"Object of bad type passed as window"];
3093
3094  if (isFilename)
3095    {
3096      NSRange	r = [aString rangeOfString: @"  --  "];
3097
3098      if (r.length > 0)
3099	{
3100	  aString = [aString substringToIndex: r.location];
3101	}
3102    }
3103
3104  /*
3105   * If there is no menu and nowhere to put one, we can't do anything.
3106   */
3107  if (_windows_menu == nil)
3108    return;
3109
3110  /*
3111   * Check if the window is already in the menu.
3112   */
3113  itemArray = [_windows_menu itemArray];
3114  count = [itemArray count];
3115  for (i = 0; i < count; i++)
3116    {
3117      NSMenuItem *item = [itemArray objectAtIndex: i];
3118
3119      if ([item target] == aWindow)
3120	{
3121	  /*
3122	   * If our menu item already exists and with the correct
3123	   * title, we need not continue.
3124	   */
3125	  if ([[item title] isEqualToString: aString])
3126	    {
3127	      return;
3128	    }
3129	  else
3130	    {
3131	      /*
3132	       * Else, we need to remove the old item and add it again
3133	       * with the new title.  Then new item might be located
3134	       * somewhere else in the menu than the old one (because
3135	       * items in the menu are sorted by title) ... this is
3136	       * why we remove the old one and then insert it again.
3137	       */
3138	      [_windows_menu removeItem: item];
3139	      break;
3140	    }
3141	}
3142    }
3143
3144  /*
3145   * Can't permit an untitled window in the window menu ... so if the
3146   * window has not title, we don't add it to the menu.
3147   */
3148  if (aString == nil || [aString isEqualToString: @""])
3149    return;
3150
3151  /*
3152   * Now we insert a menu item for the window in the correct order.
3153   * Window menu items are inserted in alphabetic order at the bottom
3154   * of the windows menu except for two special items with actions
3155   * 'performMiniaturize: ' and 'performClose: '.  If these exist the
3156   * they are kept at the below all window entries in the menu.
3157   */
3158  itemArray = [_windows_menu itemArray];
3159  count = [itemArray count];
3160
3161  if (count > 0 && sel_isEqual([[itemArray objectAtIndex: count-1] action],
3162		@selector(performClose:)))
3163    count--;
3164  if (count > 0 && sel_isEqual([[itemArray objectAtIndex: count-1] action],
3165		@selector(performMiniaturize:)))
3166    count--;
3167  for (i = 0; i < count; i++)
3168    {
3169      NSMenuItem *item = [itemArray objectAtIndex: i];
3170      if ([[item target] isKindOfClass: [NSWindow class]] &&
3171	  sel_isEqual([item action], @selector(makeKeyAndOrderFront:)))
3172	break;
3173    }
3174
3175  while (i < count)
3176    {
3177      item = [itemArray objectAtIndex: i];
3178
3179      if ([[item title] compare: aString] == NSOrderedDescending)
3180	break;
3181      i++;
3182    }
3183
3184  item = [_windows_menu insertItemWithTitle: aString
3185			action: @selector(makeKeyAndOrderFront:)
3186			keyEquivalent: @""
3187			atIndex: i];
3188  [item setTarget: aWindow];
3189
3190  // When changing for a window with a file, we should also set the image.
3191  [self setImageForWindowsItem: item];
3192}
3193
3194/**
3195 * Update Windows menu item for aWindow, to reflect its edited status.  This
3196 * is usually done automatically so you don't need to call this.
3197 */
3198- (void) updateWindowsItem: (NSWindow*)aWindow
3199{
3200  NSMenu *menu;
3201
3202  menu = [self windowsMenu];
3203  if (menu != nil)
3204    {
3205      NSArray	*itemArray;
3206      NSUInteger count;
3207      NSUInteger i;
3208      BOOL	found = NO;
3209
3210      itemArray = [menu itemArray];
3211      count = [itemArray count];
3212      for (i = 0; i < count; i++)
3213	{
3214	  NSMenuItem *item = [itemArray objectAtIndex: i];
3215
3216	  if ([item target] == aWindow)
3217	    {
3218	      [self setImageForWindowsItem: item];
3219	      break;
3220	    }
3221	}
3222
3223      if (found == NO)
3224	{
3225	  [self changeWindowsItem: aWindow
3226			    title: [aWindow title]
3227			 filename: [aWindow _hasTitleWithRepresentedFilename]];
3228	}
3229    }
3230}
3231
3232/**
3233 * Sets the windows menu of the receiver.  The windows menu keeps track of all
3234 * windows open in the application.
3235 */
3236- (void) setWindowsMenu: (NSMenu*)aMenu
3237{
3238  if (_windows_menu == aMenu)
3239    {
3240      return;
3241    }
3242
3243  /*
3244   * Remove all the windows from the old windows menu.
3245   */
3246  if (_windows_menu != nil)
3247    {
3248      NSArray *itemArray = [_windows_menu itemArray];
3249      NSUInteger i, count = [itemArray count];
3250
3251      for (i = 0; i < count; i++)
3252	{
3253	  NSMenuItem *anItem = [itemArray objectAtIndex: i];
3254	  id win = [anItem target];
3255
3256	  if ([win isKindOfClass: [NSWindow class]])
3257	    {
3258	      [_windows_menu removeItem: anItem];
3259	    }
3260	}
3261    }
3262
3263  /* Set the new _windows_menu.  */
3264  ASSIGN (_windows_menu, aMenu);
3265
3266  {
3267    /*
3268     * Now use [-changeWindowsItem:title:filename:] to build the new menu.
3269     */
3270    NSArray * windows = [self windows];
3271    NSUInteger i, count = [windows count];
3272    for (i = 0; i < count; i++)
3273      {
3274	NSWindow	*win = [windows objectAtIndex: i];
3275
3276	if (([win isExcludedFromWindowsMenu] == NO)
3277	    && ([win isVisible] || [win isMiniaturized]))
3278	  {
3279	    [self changeWindowsItem: win
3280			      title: [win title]
3281			   filename: [win _hasTitleWithRepresentedFilename]];
3282	  }
3283      }
3284  }
3285}
3286
3287/**
3288 * Returns current Windows menu for the application (whose window contents are
3289 * managed automatically by this class and NSWindow).
3290 */
3291- (NSMenu*) windowsMenu
3292{
3293  return _windows_menu;
3294}
3295
3296/*
3297 * Managing the Service menu
3298 */
3299
3300/**
3301 * Registers the types this application can send and receive over Services.
3302 * sendTypes represents the pasteboard types this app can receive in Service
3303 * invocation.  This is used to set up the services menu of this app -- an
3304 * item is added for each registered service that can accept one of sendTypes
3305 * or return one of returnTypes
3306 */
3307- (void) registerServicesMenuSendTypes: (NSArray *)sendTypes
3308			   returnTypes: (NSArray *)returnTypes
3309{
3310  [_listener registerSendTypes: sendTypes
3311		  returnTypes: returnTypes];
3312}
3313
3314/** <p>Returns the services menu of the receiver.</p>
3315    <p>See Also: -setServicesMenu:</p>
3316 */
3317- (NSMenu *) servicesMenu
3318{
3319  return [_listener servicesMenu];
3320}
3321
3322/**<p> Returns the services provided previously registered using the
3323 * -setServicesProvider: method.</p><p>See Also: -setServicesProvider:</p>
3324 */
3325- (id) servicesProvider
3326{
3327  return [_listener servicesProvider];
3328}
3329
3330/**<p>ets the services menu for the receiver.  This should be called, otherwise
3331 * warnings will be generated by the main event loop, which is in charge of
3332 * updating all menus, including the Services menu.</p>
3333 * <p>See Also: -servicesMenu</p>
3334 */
3335- (void) setServicesMenu: (NSMenu *)aMenu
3336{
3337  [_listener setServicesMenu: aMenu];
3338}
3339
3340/**
3341 * Sets the object which provides services to other applications.<br />
3342 * Passing a nil value for anObject will result in the provision of
3343 * services to other applications by this application being disabled.<br />
3344 * See [NSPasteboard] for information about providing services.
3345 */
3346- (void) setServicesProvider: (id)anObject
3347{
3348  [_listener setServicesProvider: anObject];
3349}
3350
3351/**
3352 * Return an object capable of sending and receiving the specified sendType
3353 * and returnType in a services interaction.  NSApp is generally the last
3354 * responder to get this request, and the implementation passes it on to the
3355 * delegate if it handles it and is not itself an [NSResponder], or returns
3356 * nil otherwise.
3357 */
3358- (id) validRequestorForSendType: (NSString *)sendType
3359		      returnType: (NSString *)returnType
3360{
3361  if (_delegate != nil && ![_delegate isKindOfClass: [NSResponder class]]
3362    && [_delegate respondsToSelector:
3363                    @selector(validRequestorForSendType:returnType:)])
3364    return [_delegate validRequestorForSendType: sendType
3365				     returnType: returnType];
3366
3367  return nil;
3368}
3369
3370/**
3371 *  Returns the default drawing context for the app.
3372 */
3373- (NSGraphicsContext *) context
3374{
3375  return _default_context;
3376}
3377
3378/**
3379 *  NSLogs an exception without raising it.
3380 */
3381- (void) reportException: (NSException *)anException
3382{
3383  if (anException)
3384    NSLog (_(@"reported exception - %@"), anException);
3385}
3386
3387- (BOOL) presentError: (NSError *)error
3388{
3389  NSAlert *alert;
3390  NSInteger result;
3391
3392  error = [self willPresentError: error];
3393  alert = [NSAlert alertWithError: error];
3394  result = [alert runModal];
3395
3396  if (result != NSAlertErrorReturn)
3397    {
3398      // Convert result (1, 0, -1) into index (0, 1, 2)
3399      result = 1 - result;
3400      return [[error recoveryAttempter] attemptRecoveryFromError: error
3401                 optionIndex: result];
3402    }
3403  else
3404    {
3405      return NO;
3406    }
3407}
3408
3409struct _DelegateWrapper
3410{
3411  id delegate;
3412  SEL selector;
3413  NSError *error;
3414  void *context;
3415};
3416
3417- (void) presentError: (NSError*)error
3418       modalForWindow: (NSWindow*)window
3419             delegate: (id)delegate
3420   didPresentSelector: (SEL)sel
3421          contextInfo: (void*)context
3422{
3423  NSAlert *alert;
3424  struct _DelegateWrapper *wrapper;
3425
3426  error = [self willPresentError: error];
3427  alert = [NSAlert alertWithError: error];
3428  // FIXME: Who is trying to recover the error?
3429  wrapper = malloc(sizeof(struct _DelegateWrapper));
3430  wrapper->delegate = delegate;
3431  wrapper->selector = sel;
3432  wrapper->error = error;
3433  wrapper->context = context;
3434
3435  [alert beginSheetModalForWindow: window
3436         modalDelegate: self
3437         didEndSelector: @selector(_didPresentError:returnCode:contextInfo:)
3438         contextInfo: wrapper];
3439}
3440
3441- (void) _didPresentError: (NSWindow*)sheet
3442               returnCode: (NSInteger)result
3443              contextInfo: (void*)context
3444{
3445  struct _DelegateWrapper *wrapper;
3446  id delegate;
3447  SEL sel;
3448  NSError *error;
3449  void *orgContext;
3450  BOOL recover;
3451
3452  wrapper = (struct _DelegateWrapper*)context;
3453  delegate = wrapper->delegate;
3454  sel = wrapper->selector;
3455  error = wrapper->error;
3456  orgContext = wrapper->context;
3457  free(wrapper);
3458
3459  if (result != NSAlertErrorReturn)
3460    {
3461      // Convert result (1, 0, -1) into index (0, 1, 2)
3462      result = 1 - result;
3463      recover = [[error recoveryAttempter] attemptRecoveryFromError: error
3464                 optionIndex: result];
3465    }
3466  else
3467    {
3468      recover = NO;
3469    }
3470
3471  if ([delegate respondsToSelector: sel])
3472    {
3473      void (*didEnd)(id, SEL, BOOL, void*);
3474
3475      didEnd = (void (*)(id, SEL, BOOL, void*))[delegate methodForSelector: sel];
3476      didEnd(delegate, sel, recover, orgContext);
3477    }
3478}
3479
3480- (NSError*) willPresentError: (NSError*)error
3481{
3482  if ([_delegate respondsToSelector: @selector(application:willPresentError:)])
3483    {
3484      return [_delegate application: self willPresentError: error];
3485    }
3486  else
3487    {
3488      return error;
3489    }
3490}
3491
3492/**
3493 * Requests the application terminates the application.  First an
3494 * -applicationShouldTerminate: message is sent to the delegate, and only if
3495 * it returns <code>NSTerminateNow</code> will termination be
3496 * carried out.<br />
3497 * The old version of -applicationShouldTerminate: returned a BOOL, and this
3498 * should still work as YES is
3499 * equivalent to <code>NSTerminateNow</code> and NO is
3500 * equivalent to <code>NSTerminateCancel</code>.
3501 */
3502- (void) terminate: (id)sender
3503{
3504  NSDocumentController		*sdc;
3505  NSApplicationTerminateReply	termination;
3506
3507  /* First ask the shared document controller to save any unsaved changes */
3508  sdc = [NSDocumentController sharedDocumentController];
3509  if ([[sdc documentClassNames] count] > 0)
3510    {
3511      if ([sdc reviewUnsavedDocumentsWithAlertTitle: _(@"Quit")
3512					cancellable: YES] == NO)
3513	{
3514	  return;
3515	}
3516    }
3517
3518  /* Now ask the application delegate whether its okay to terminate */
3519  termination = NSTerminateNow;
3520  if ([_delegate respondsToSelector: @selector(applicationShouldTerminate:)])
3521    {
3522      /* The old API has applicationShouldTerminate: return a BOOL,
3523       * so if we are linked in to an application which used that
3524       * API, the delegate might return a BOOL rather than an
3525       * NSTerminateNow.  That's fine as both NSTerminateNow
3526       * and BOOL are integral types (though potentially of different sizes),
3527       * and NSTerminateNow is defined as YES and NSTerminateCancel as NO.
3528       * So all we need to do is mask the low byte of the return value in
3529       * case there is uninitialised random data in the higher bytes.
3530       */
3531      termination = ([_delegate applicationShouldTerminate: self] & 0xff);
3532    }
3533
3534  if (termination == NSTerminateNow)
3535    {
3536      [self replyToApplicationShouldTerminate: YES];
3537    }
3538  /*
3539     Don't need to do anything on NSTerminateLater, as long as user code
3540     follows the contract (to call replyTo... later).
3541  */
3542}
3543
3544/**
3545 * Terminates the app if shouldTerminate is YES, otherwise does nothing.
3546 * This should be called by user code if a delegate has returned
3547 * <code>NSTerminateLater</code> to an -applicationShouldTerminate: message.
3548 */
3549- (void) replyToApplicationShouldTerminate: (BOOL)shouldTerminate
3550{
3551  // Prevent cycles in terminate: call.
3552  if (shouldTerminate && _app_is_running)
3553    {
3554      [nc postNotificationName: NSApplicationWillTerminateNotification
3555	  object: self];
3556
3557      _app_is_running = NO;
3558
3559      GSRemoveIcon(_app_icon_window);
3560      [[self windows] makeObjectsPerformSelector: @selector(close)];
3561      [NSCursor setHiddenUntilMouseMoves: NO];
3562
3563      /* Store our user information.  */
3564      [[NSUserDefaults standardUserDefaults] synchronize];
3565
3566      /* Tell the Workspace that we really did terminate.  */
3567      [[[NSWorkspace sharedWorkspace] notificationCenter]
3568        postNotificationName: NSWorkspaceDidTerminateApplicationNotification
3569		      object: [NSWorkspace sharedWorkspace]
3570		    userInfo: [self _notificationUserInfo]];
3571
3572      /* Destroy the main run loop pool (this also destroys any nested
3573         pools which might have been created inside this one).  */
3574      DESTROY (_runLoopPool);
3575
3576      /* Now free the NSApplication object.  Enclose the operation
3577         into an autorelease pool, in case some -dealloc method needs
3578         to use any temporary object.  */
3579      {
3580        NSAutoreleasePool *pool;
3581
3582        IF_NO_GC(pool = [arpClass new]);
3583
3584        DESTROY(NSApp);
3585
3586        DESTROY(pool);
3587      }
3588
3589      /* And finally, stop the program.  */
3590      exit(0);
3591    }
3592}
3593
3594- (void) replyToOpenOrPrint: (NSApplicationDelegateReply)reply
3595{
3596  // FIXME
3597}
3598
3599/**
3600 * Returns the application's delegate, as set by the -setDelegate: method.<br/>
3601 * <p>The application delegate will automatically be sent various
3602 * notifications (as long as it implements the appropriate methods)
3603 * when application events occur.  The method to handle each of these
3604 * notifications has name mirroring the notification name, so for instance
3605 * an <em>NSApplicationDidBecomeActiveNotification</em> is handled by an
3606 * <code>applicationDidBecomeActive:</code> method.
3607 * </p>
3608 * <list>
3609 *   <item>NSApplicationDidBecomeActiveNotification</item>
3610 *   <item>NSApplicationDidFinishLaunchingNotification</item>
3611 *   <item>NSApplicationDidHideNotification</item>
3612 *   <item>NSApplicationDidResignActiveNotification</item>
3613 *   <item>NSApplicationDidUnhideNotification</item>
3614 *   <item>NSApplicationDidUpdateNotification</item>
3615 *   <item>NSApplicationWillBecomeActiveNotification</item>
3616 *   <item>NSApplicationWillFinishLaunchingNotification</item>
3617 *   <item>NSApplicationWillHideNotification</item>
3618 *   <item>NSApplicationWillResignActiveNotification</item>
3619 *   <item>NSApplicationWillTerminateNotification</item>
3620 *   <item>NSApplicationWillUnhideNotification</item>
3621 *   <item>NSApplicationWillUpdateNotification</item>
3622 * </list>
3623 * <p>The delegate is also sent various messages to ask for authorisation
3624 * to perform actions, or to ask it to perform actions (again, as long
3625 * as it implements the appropriate methods).
3626 * </p>
3627 * <list>
3628 *   <item>application:shouldTerminateAfterLastWindowClosed:</item>
3629 *   <item>application:shouldOpenUntitledFile:</item>
3630 *   <item>application:openFile:</item>
3631 *   <item>application:openFiles:</item>
3632 *   <item>application:openFileWithoutUI:</item>
3633 *   <item>application:openTempFile:</item>
3634 *   <item>application:openUntitledFile:</item>
3635 *   <item>application:shouldOpenUntitledFile:</item>
3636 *   <item>application:printFile:</item>
3637 *   <item>application:shouldTerminate:</item>
3638 *   <item>application:shouldTerminateAfterLastWindowClosed:</item>
3639 * </list>
3640 * <p>The delegate is also called upon to respond to any actions which
3641 *   are not handled by a window, a window delgate, or by the application
3642 *   object itself.  This is controlled by the -targetForAction: method.
3643 * </p>
3644 * <p>Finally, the application delegate is responsible for handling
3645 *   messages sent to the application from remote processes (see the
3646 *   section documenting distributed objects for [NSPasteboard]).
3647 * </p>
3648 * <p>See -setDelegate: and [(NSApplicationDelegate)] for more information.</p>
3649 */
3650- (id) delegate
3651{
3652  return _delegate;
3653}
3654
3655/**
3656 * Sets the delegate of the application to anObject.<br />
3657 * <p><em>Beware</em>, this does not retain anObject, so you must be sure
3658 * that, in the event of anObject being deallocated, you
3659 * stop it being the application delagate by calling this
3660 * method again with another object (or nil) as the argument.
3661 * </p>
3662 * <p>See -delegate and [(NSApplicationDelegate)] for more information.</p>
3663 */
3664- (void) setDelegate: (id)anObject
3665{
3666  if (_delegate)
3667    [nc removeObserver: _delegate name: nil object: self];
3668  _delegate = anObject;
3669
3670#define SET_DELEGATE_NOTIFICATION(notif_name) \
3671  if ([_delegate respondsToSelector: @selector(application##notif_name:)]) \
3672    [nc addObserver: _delegate \
3673      selector: @selector(application##notif_name:) \
3674      name: NSApplication##notif_name##Notification object: self]
3675
3676  SET_DELEGATE_NOTIFICATION(DidBecomeActive);
3677  SET_DELEGATE_NOTIFICATION(DidFinishLaunching);
3678  SET_DELEGATE_NOTIFICATION(DidHide);
3679  SET_DELEGATE_NOTIFICATION(DidResignActive);
3680  SET_DELEGATE_NOTIFICATION(DidUnhide);
3681  SET_DELEGATE_NOTIFICATION(DidUpdate);
3682  SET_DELEGATE_NOTIFICATION(WillBecomeActive);
3683  SET_DELEGATE_NOTIFICATION(WillFinishLaunching);
3684  SET_DELEGATE_NOTIFICATION(WillHide);
3685  SET_DELEGATE_NOTIFICATION(WillResignActive);
3686  SET_DELEGATE_NOTIFICATION(WillTerminate);
3687  SET_DELEGATE_NOTIFICATION(WillUnhide);
3688  SET_DELEGATE_NOTIFICATION(WillUpdate);
3689}
3690
3691/*
3692 * Methods for scripting
3693 */
3694
3695/**
3696 * OS X scripting method to return document objects being handled by
3697 * application.  <em>Not implemented yet under GNUstep.</em>
3698 */
3699- (NSArray *) orderedDocuments
3700{
3701  // FIXME
3702  return nil;
3703}
3704
3705/**
3706 * OS X scripting method to return windows in front-to-back on-screen order
3707 * for scriptable windows.
3708 * <em>The GNUstep implementation returns all the windows excluding NSPanels.
3709 * some backends may return an array in an unspecified order.</em>
3710 */
3711- (NSArray *) orderedWindows
3712{
3713  NSArray *arr = GSOrderedWindows();
3714  NSMutableArray *ret = [[NSMutableArray alloc] initWithCapacity:[arr count]];
3715  NSEnumerator *iter = [arr objectEnumerator];
3716  id win;
3717  while ((win = [iter nextObject]))
3718    {
3719      if (![win isKindOfClass:[NSPanel class]])
3720        [ret addObject:win];
3721    }
3722
3723  return AUTORELEASE(ret);
3724}
3725
3726/*
3727 * Methods for user attention requests
3728 */
3729
3730/**
3731 * Cancels a request previously made through calling -requestUserAttention: .
3732 * Note that request is cancelled automatically if user activates the app.
3733 */
3734- (void) cancelUserAttentionRequest: (NSInteger)request
3735{
3736  // FIXME
3737}
3738
3739/**
3740 * Method that on OS X makes the icon jump in the doc.  Mercifully, this is
3741 * unimplemented under GNUstep.  If it <em>were</em> implemented, requestType
3742 * could be either <code>NSCriticalRequest</code> (bounce endlessly) or
3743 * <code>NSInformationalRequest</code> (bounce for one second).
3744 */
3745- (NSInteger) requestUserAttention: (NSRequestUserAttentionType)requestType
3746{
3747  // FIXME
3748  return 0;
3749}
3750
3751- (NSApplicationPresentationOptions) currentPresentationOptions
3752{
3753  return _presentationOptions;
3754}
3755
3756- (NSApplicationPresentationOptions) presentationOptions
3757{
3758  return _presentationOptions;
3759}
3760
3761/**
3762 * Currently unimplemented and unused in GNUstep, it could be extended to handle
3763 * special GNUstep needs too
3764 */
3765- (void)setPresentationOptions: (NSApplicationPresentationOptions)options
3766{
3767  _presentationOptions = options;
3768}
3769
3770/*
3771 * NSCoding protocol
3772 */
3773- (void) encodeWithCoder: (NSCoder*)aCoder
3774{
3775  [super encodeWithCoder: aCoder];
3776  if ([aCoder allowsKeyedCoding])
3777    {
3778      /*
3779      if (_delegate != nil)
3780        {
3781	  [aCoder encodeObject: _delegate forKey: @"NSDelegate"];
3782	}
3783      [aCoder encodeObject: _main_menu forKey: @"NSMainMenu"]; // ???
3784      [aCoder encodeObject: _windows_menu forKey: @"NSWindowsMenu"]; // ???
3785      */
3786    }
3787  else
3788    {
3789      [aCoder encodeConditionalObject: _delegate];
3790      [aCoder encodeObject: _main_menu];
3791      [aCoder encodeConditionalObject: _windows_menu];
3792    }
3793}
3794
3795- (id) initWithCoder: (NSCoder*)aDecoder
3796{
3797  id	obj;
3798
3799  [super initWithCoder: aDecoder];
3800  if ([aDecoder allowsKeyedCoding])
3801    {
3802      /*
3803      if ([aDecoder containsValueForKey: @"NSDelegate"])
3804	{
3805	  obj = [aDecoder decodeObjectForKey: @"NSDelegate"];
3806	  [self setDelegate: obj];
3807	}
3808      obj = [aDecoder decodeObjectForKey: @"NSMainMenu"];
3809      [self setMainMenu: obj];
3810      obj = [aDecoder decodeObjectForKey: @"NSWindowsMenu"];
3811      [self setWindowsMenu: obj];
3812      */
3813    }
3814  else
3815    {
3816      obj = [aDecoder decodeObject];
3817      [self setDelegate: obj];
3818      obj = [aDecoder decodeObject];
3819      [self setMainMenu: obj];
3820      obj = [aDecoder decodeObject];
3821      [self setWindowsMenu: obj];
3822    }
3823  return self;
3824}
3825
3826@end /* NSApplication */
3827
3828
3829@implementation	NSApplication (Private)
3830
3831- (void) _loadAppIconImage
3832{
3833  NSDictionary	*infoDict;
3834  NSString	*appIconFile;
3835  NSImage	*image = nil;
3836
3837  infoDict = [[NSBundle mainBundle] infoDictionary];
3838  appIconFile = [infoDict objectForKey: @"NSIcon"];
3839  if (appIconFile && ![appIconFile isEqual: @""])
3840    {
3841      image = [NSImage imageNamed: appIconFile];
3842    }
3843
3844  // Try to look up the icon file.
3845  appIconFile = [infoDict objectForKey: @"CFBundleIconFile"];
3846  if (appIconFile && ![appIconFile isEqual: @""])
3847    {
3848      image = [NSImage imageNamed: appIconFile];
3849    }
3850
3851  if (image == nil)
3852    {
3853      image = [NSImage imageNamed: @"GNUstep"];
3854    }
3855  else
3856    {
3857      /* Set the new image to be named 'NSApplicationIcon' ... to do that we
3858       * must first check that any existing image of the same name has its
3859       * name removed.
3860       */
3861      [(NSImage*)[NSImage imageNamed: @"NSApplicationIcon"] setName: nil];
3862      // We need to copy the image as we may have a proxy here
3863      image = AUTORELEASE([image copy]);
3864      [image setName: @"NSApplicationIcon"];
3865    }
3866  [self setApplicationIconImage: image];
3867}
3868
3869- (void) _appIconInit
3870{
3871  NSAppIconView	*iv;
3872  NSUInteger	mask = NSIconWindowMask;
3873  BOOL  	suppress;
3874
3875  suppress = [[NSUserDefaults standardUserDefaults]
3876    boolForKey: @"GSSuppressAppIcon"];
3877#if	MINI_ICON
3878  if (suppress)
3879    {
3880      mask = NSMiniaturizableWindowMask;
3881    }
3882#endif
3883
3884  _app_icon_window = [[NSIconWindow alloc] initWithContentRect: NSZeroRect
3885				styleMask: mask
3886				  backing: NSBackingStoreRetained
3887				    defer: NO
3888				   screen: nil];
3889
3890
3891
3892  {
3893    NSRect iconContentRect;
3894    NSRect iconFrame;
3895    NSRect iconViewFrame;
3896
3897    iconContentRect = GSGetIconFrame(_app_icon_window);
3898    iconFrame = [_app_icon_window frameRectForContentRect: iconContentRect];
3899    iconFrame.origin = [[NSScreen mainScreen] frame].origin;
3900    iconViewFrame = NSMakeRect(0, 0,
3901      iconContentRect.size.width, iconContentRect.size.height);
3902    [_app_icon_window setFrame: iconFrame display: YES];
3903
3904    iv = [[NSAppIconView alloc] initWithFrame: iconViewFrame];
3905    [iv setImage: [self applicationIconImage]];
3906    [_app_icon_window setContentView: iv];
3907    RELEASE(iv);
3908  }
3909
3910  if (NO == suppress)
3911    {
3912      /* The icon window is not suppressed ... display it.
3913       */
3914      [_app_icon_window orderFrontRegardless];
3915    }
3916}
3917
3918- (NSDictionary*) _notificationUserInfo
3919{
3920  NSString	*path;
3921  NSString	*port;
3922  NSNumber	*processIdentifier;
3923  NSDictionary	*userInfo;
3924
3925  processIdentifier = [NSNumber numberWithInt:
3926    [[NSProcessInfo processInfo] processIdentifier]];
3927  port = [(GSServicesManager*)_listener port];
3928  path = [[NSBundle mainBundle] bundlePath];
3929  if (port == nil)
3930    {
3931      if (path == nil)
3932	{
3933	  userInfo = [NSDictionary dictionaryWithObjectsAndKeys:
3934	    processIdentifier, @"NSApplicationProcessIdentifier",
3935	    nil];
3936	}
3937      else
3938	{
3939	  userInfo = [NSDictionary dictionaryWithObjectsAndKeys:
3940	    path, @"NSApplicationPath",
3941	    processIdentifier, @"NSApplicationProcessIdentifier",
3942	    nil];
3943	}
3944    }
3945  else if (path == nil)
3946    {
3947      userInfo = [NSDictionary dictionaryWithObjectsAndKeys:
3948	port, @"NSApplicationName",
3949	processIdentifier, @"NSApplicationProcessIdentifier",
3950	nil];
3951    }
3952  else
3953    {
3954      userInfo = [NSDictionary dictionaryWithObjectsAndKeys:
3955	port, @"NSApplicationName",
3956	path, @"NSApplicationPath",
3957	processIdentifier, @"NSApplicationProcessIdentifier",
3958	nil];
3959    }
3960  return userInfo;
3961}
3962
3963- (void) _openDocument: (NSString*)filePath
3964{
3965  [_listener application: self openFile: filePath];
3966}
3967
3968- (id) _targetForAction: (SEL)aSelector window: (NSWindow *)window
3969{
3970  id resp, delegate;
3971  NSDocumentController *sdc;
3972
3973  if (window == nil)
3974    {
3975      return nil;
3976    }
3977
3978  /* traverse the responder chain including the window's delegate */
3979  resp = [window firstResponder];
3980  while (resp != nil && resp != self)
3981    {
3982      if ([resp respondsToSelector: aSelector])
3983	{
3984	  return resp;
3985	}
3986      if (resp == window)
3987	{
3988	  delegate = [window delegate];
3989	  if ([delegate respondsToSelector: aSelector])
3990	    {
3991	      return delegate;
3992	    }
3993	}
3994      resp = [resp nextResponder];
3995    }
3996
3997  /* in a document based app try the window's document */
3998  sdc = [NSDocumentController sharedDocumentController];
3999  if ([[sdc documentClassNames] count] > 0)
4000    {
4001      resp = [sdc documentForWindow: window];
4002
4003      if (resp != nil && [resp respondsToSelector: aSelector])
4004	{
4005	  return resp;
4006	}
4007    }
4008
4009  /* nothing found */
4010  return nil;
4011}
4012
4013- (id) _targetForAction: (SEL)aSelector
4014	      keyWindow: (NSWindow *)keyWindow
4015	     mainWindow: (NSWindow *)mainWindow
4016{
4017  NSDocumentController *sdc;
4018  id resp;
4019
4020  if (aSelector == NULL)
4021    return nil;
4022
4023  /* start looking in the key window's responder chain */
4024  resp = [self _targetForAction: aSelector window: keyWindow];
4025  if (resp != nil)
4026    {
4027      return resp;
4028    }
4029
4030  /* next check the main window's responder chain (provided it is not
4031   * the key window) */
4032  if (mainWindow != keyWindow)
4033    {
4034      resp = [self _targetForAction: aSelector window: mainWindow];
4035      if (resp != nil)
4036	{
4037	  return resp;
4038	}
4039    }
4040
4041  /* try the shared application imstance and its delegate */
4042  if ([self respondsToSelector: aSelector])
4043    {
4044      return self;
4045    }
4046
4047  if (_delegate != nil && [_delegate respondsToSelector: aSelector])
4048    {
4049      return _delegate;
4050    }
4051
4052  /* Try the NSApplication's responder list to determine if any of them
4053   * respond to the selector.
4054   */
4055  resp = [self nextResponder];
4056  while (resp != nil)
4057    {
4058      if ([resp respondsToSelector: aSelector])
4059	{
4060	  return resp;
4061	}
4062      resp = [resp nextResponder];
4063    }
4064
4065  /* as last resort in a document based app, try the document controller */
4066  sdc = [NSDocumentController sharedDocumentController];
4067  if ([[sdc documentClassNames] count] > 0
4068    && [sdc respondsToSelector: aSelector])
4069    {
4070      return sdc;
4071    }
4072
4073  /* give up */
4074  return nil;
4075}
4076
4077- (void) _windowDidBecomeKey: (NSNotification*) notification
4078{
4079  id	obj = [notification object];
4080
4081  if (_key_window == nil && [obj isKindOfClass: [NSWindow class]])
4082    {
4083      _key_window = obj;
4084      [_main_menu update];
4085    }
4086  else if (_key_window != obj)
4087    {
4088      NSLog(@"Bogus attempt to set key window");
4089    }
4090}
4091
4092- (void) _windowDidBecomeMain: (NSNotification*) notification
4093{
4094  id	obj = [notification object];
4095
4096  if (_main_window == nil && [obj isKindOfClass: [NSWindow class]])
4097    {
4098      _main_window = obj;
4099      [_main_menu update];
4100    }
4101  else if (_main_window != obj)
4102    {
4103      NSLog(@"Bogus attempt to set main window");
4104    }
4105}
4106
4107- (void) _windowDidResignKey: (NSNotification*) notification
4108{
4109  id	obj = [notification object];
4110
4111  if (_key_window == obj)
4112    {
4113      _key_window = nil;
4114      [NSCursor setHiddenUntilMouseMoves: NO];
4115    }
4116  else
4117    {
4118      NSLog(@"Bogus attempt to resign key window");
4119    }
4120}
4121
4122- (void) _windowDidResignMain: (NSNotification*) notification
4123{
4124  id	obj = [notification object];
4125
4126  if (_main_window == obj)
4127    {
4128      _main_window = nil;
4129    }
4130  else
4131    {
4132      NSLog(@"Bogus attempt to resign key window");
4133    }
4134}
4135
4136- (void) _lastWindowClosed
4137{
4138  if ([_delegate respondsToSelector:
4139    @selector(applicationShouldTerminateAfterLastWindowClosed:)])
4140    {
4141      if ([_delegate
4142	applicationShouldTerminateAfterLastWindowClosed: self])
4143        {
4144          [self terminate: self];
4145        }
4146    }
4147  /* wlux 2009-10-17: If we use MS Windows style menus, terminate
4148     the application by default when the last window is closed. */
4149  else if (NSInterfaceStyleForKey(@"NSMenuInterfaceStyle", nil) ==
4150	   NSWindows95InterfaceStyle)
4151    {
4152      [self terminate: self];
4153    }
4154}
4155
4156- (void) _windowWillClose: (NSNotification*) notification
4157{
4158  NSWindow *win = [notification object];
4159
4160  if (_app_is_running)
4161    {
4162      /* FIXME Don't use GSOrderedWindows(), since it may not return all
4163	 windows when the application is hidden. E.g., given a hidden
4164	 application with a single window with canHide == NO, if the user
4165	 closes this window it would appear as if the last window was closed. */
4166      NSArray *windows_list = GSOrderedWindows();
4167      NSEnumerator *iter = [windows_list objectEnumerator];
4168      BOOL wasLast = YES;
4169      NSWindow *tmp;
4170
4171      while ((tmp = [iter nextObject]))
4172        {
4173          if (tmp != win && [tmp canBecomeMainWindow] == YES)
4174            {
4175              wasLast = NO;
4176	      break;
4177            }
4178        }
4179
4180      /* Perform _lastWindowDidClose at the end of the event loop cycle so
4181	 that all interested objects are notified before we terminate the
4182	 application. In particular, this means that a modified document
4183	 associated with the closed window has been closed before -terminate:
4184	 is called and therefore the user isn't asked twice whether she wants
4185	 to save that document's unsaved changes. */
4186      if (wasLast)
4187	{
4188	  [self performSelector: @selector(_lastWindowClosed)
4189		     withObject: nil
4190		     afterDelay: 0.1];
4191	}
4192    }
4193}
4194
4195- (void) _workspaceNotification: (NSNotification*) notification
4196{
4197  NSString	*name = [notification name];
4198  NSDictionary	*info = [notification userInfo];
4199
4200  /*
4201   * Handle hiding and unhiding of this app if necessary.
4202   */
4203  if ([name isEqualToString: GSUnhideAllApplicationsNotification] == YES)
4204    {
4205      [self unhideWithoutActivation];
4206    }
4207  else if ([name isEqualToString: GSHideOtherApplicationsNotification] == YES)
4208    {
4209      NSString	*port = [info objectForKey: @"NSApplicationName"];
4210
4211      if ([port isEqual: [[GSServicesManager manager] port]] == NO)
4212	{
4213	  [self hide: self];
4214	}
4215    }
4216}
4217
4218- (NSArray *) _openFiles
4219{
4220  NSMutableArray *files = nil;
4221  NSArray *args = [[NSProcessInfo processInfo] arguments];
4222  NSEnumerator *en = [args objectEnumerator];
4223  NSString *file = nil;
4224
4225  [en nextObject]; // skip the first element, which is always empty...
4226  while ((file = [en nextObject]) != nil)
4227    {
4228      if ([file length] == 0)
4229        {
4230          continue;
4231        }
4232
4233      if ([file characterAtIndex: 0] != '-')
4234	{
4235	  if (files == nil)
4236	    {
4237	      files = [NSMutableArray array];
4238	    }
4239	  [files addObject: file];
4240	}
4241      else
4242	{
4243	  break;
4244	}
4245    }
4246
4247  return files;
4248}
4249
4250- (NSMenu *) _dockMenu
4251{
4252  NSUInteger i, j, n;
4253  NSMenu *dockMenu, *windowsMenu;
4254
4255  // ask delegate for a dock menu, if none create a new one
4256  dockMenu = nil;
4257  if ([_delegate respondsToSelector: @selector(applicationDockMenu:)])
4258    {
4259      // NB we make a copy of the menu since we are going to modify it
4260      dockMenu = [[_delegate applicationDockMenu: self] copy];
4261    }
4262  if (dockMenu == nil)
4263    {
4264      dockMenu = [[NSMenu alloc] initWithTitle:@""];
4265    }
4266
4267  // copy window menu entries to the top of the menu
4268  windowsMenu = [NSApp windowsMenu];
4269  for (i = j = 0, n = [windowsMenu numberOfItems]; i < n; i++)
4270    {
4271      NSMenuItem *item = [windowsMenu itemAtIndex: i];
4272      if ([[item target] isKindOfClass:[NSWindow class]] &&
4273          sel_isEqual([item action], @selector(makeKeyAndOrderFront:)))
4274        {
4275          [[dockMenu insertItemWithTitle: [item title]
4276                                  action: @selector(makeKeyAndOrderFront:)
4277                           keyEquivalent: @""
4278                                 atIndex: j++]
4279                    setTarget: [item target]];
4280        }
4281    }
4282  if (j > 0)
4283    {
4284      [dockMenu insertItem: [NSMenuItem separatorItem] atIndex: j++];
4285    }
4286
4287  // insert standard entries to show or hide and to quit the application at
4288  // the bottom
4289  if (j < [dockMenu numberOfItems])
4290    {
4291      [dockMenu addItem: [NSMenuItem separatorItem]];
4292    }
4293  if ([self isHidden])
4294    {
4295      [dockMenu addItemWithTitle:_(@"Show")
4296                          action:@selector(unhide:)
4297                   keyEquivalent:@""];
4298    }
4299  else
4300    {
4301      [dockMenu addItemWithTitle:_(@"Hide")
4302                          action:@selector(hide:)
4303                   keyEquivalent:@""];
4304    }
4305  [dockMenu addItemWithTitle:_(@"Quit")
4306                      action:@selector(terminate:)
4307               keyEquivalent:@""];
4308
4309  // return the menu
4310  return dockMenu;
4311}
4312
4313@end // NSApplication (Private)
4314
4315
4316@implementation NSApplication (GSGUIInternal)
4317
4318- (void) _windowWillDealloc: (NSWindow *)window
4319{
4320  if (window == _key_window)
4321    _key_window = nil;
4322  if (window == _main_window)
4323    _main_window = nil;
4324}
4325
4326@end
4327
4328