1 /*
   2  * Copyright (c) 2011, Oracle and/or its affiliates. All rights reserved.
   3  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
   4  *
   5  * This code is free software; you can redistribute it and/or modify it
   6  * under the terms of the GNU General Public License version 2 only, as
   7  * published by the Free Software Foundation.  Oracle designates this
   8  * particular file as subject to the "Classpath" exception as provided
   9  * by Oracle in the LICENSE file that accompanied this code.
  10  *
  11  * This code is distributed in the hope that it will be useful, but WITHOUT
  12  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
  13  * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
  14  * version 2 for more details (a copy is included in the LICENSE file that
  15  * accompanied this code).
  16  *
  17  * You should have received a copy of the GNU General Public License version
  18  * 2 along with this work; if not, write to the Free Software Foundation,
  19  * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
  20  *
  21  * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
  22  * or visit www.oracle.com if you need additional information or have any
  23  * questions.
  24  */
  25 
  26 #import <Cocoa/Cocoa.h>
  27 
  28 #import "QueuingApplicationDelegate.h"
  29 
  30 static id <NSApplicationDelegate> realDelegate = nil;
  31 
  32 @interface NSBundle (EAWTOverrides)
  33 - (BOOL)_hasEAWTOverride:(NSString *)key;
  34 @end
  35 
  36 
  37 @implementation NSBundle (EAWTOverrides)
  38 
  39 - (BOOL)_hasEAWTOverride:(NSString *)key {
  40     return [[[[self objectForInfoDictionaryKey:@"Java"] objectForKey:@"EAWTOverride"] objectForKey:key] boolValue];
  41 }
  42 
  43 @end
  44 
  45 @implementation QueuingApplicationDelegate
  46 
  47 + (QueuingApplicationDelegate*) sharedDelegate
  48 {
  49     static QueuingApplicationDelegate * qad = nil;
  50 
  51     if (!qad) {
  52         qad = [QueuingApplicationDelegate new];
  53     }
  54 
  55     return qad;
  56 }
  57 
  58 - (id) init
  59 {
  60     self = [super init];
  61     if (!self) {
  62         return self;
  63     }
  64 
  65     self->queue = [[NSMutableArray arrayWithCapacity: 0] retain];
  66 
  67     // If the java application has a bundle with an Info.plist file with
  68     //  a CFBundleDocumentTypes entry, then it is set up to handle Open Doc
  69     //  and Print Doc commands for these files. Therefore java AWT will
  70     //  cache Open Doc and Print Doc events that are sent prior to a
  71     //  listener being installed by the client java application.
  72     NSBundle *bundle = [NSBundle mainBundle];
  73     fHandlesDocumentTypes = [bundle objectForInfoDictionaryKey:@"CFBundleDocumentTypes"] != nil || [bundle _hasEAWTOverride:@"DocumentHandler"];
  74     fHandlesURLTypes = [bundle objectForInfoDictionaryKey:@"CFBundleURLTypes"] != nil || [bundle _hasEAWTOverride:@"URLHandler"];
  75     if (fHandlesURLTypes) {
  76         [[NSAppleEventManager sharedAppleEventManager] setEventHandler:self
  77                                                            andSelector:@selector(_handleOpenURLEvent:withReplyEvent:)
  78                                                          forEventClass:kInternetEventClass
  79                                                             andEventID:kAEGetURL];
  80     }
  81 
  82     NSNotificationCenter *ctr = [NSNotificationCenter defaultCenter];
  83     [ctr addObserver:self selector:@selector(_willFinishLaunching) name:NSApplicationWillFinishLaunchingNotification object:nil];
  84     [ctr addObserver:self selector:@selector(_systemWillPowerOff) name:NSWorkspaceWillPowerOffNotification object:nil];
  85     [ctr addObserver:self selector:@selector(_appDidActivate) name:NSApplicationDidBecomeActiveNotification object:nil];
  86     [ctr addObserver:self selector:@selector(_appDidDeactivate) name:NSApplicationDidResignActiveNotification object:nil];
  87     [ctr addObserver:self selector:@selector(_appDidHide) name:NSApplicationDidHideNotification object:nil];
  88     [ctr addObserver:self selector:@selector(_appDidUnhide) name:NSApplicationDidUnhideNotification object:nil];
  89 
  90     return self;
  91 }
  92 
  93 - (void)dealloc
  94 {
  95     if (fHandlesURLTypes) {
  96         [[NSAppleEventManager sharedAppleEventManager] removeEventHandlerForEventClass: kInternetEventClass andEventID:kAEGetURL];
  97     }
  98 
  99     NSNotificationCenter *ctr = [NSNotificationCenter defaultCenter];
 100     Class clz = [QueuingApplicationDelegate class];
 101     [ctr removeObserver:clz];
 102 
 103     [self->queue release];
 104     self->queue = nil;
 105 
 106     [super dealloc];
 107 }
 108 
 109 
 110 - (void)_handleOpenURLEvent:(NSAppleEventDescriptor *)openURLEvent withReplyEvent:(NSAppleEventDescriptor *)replyEvent
 111 {
 112     [self->queue addObject:^(){
 113         [realDelegate _handleOpenURLEvent:openURLEvent withReplyEvent:replyEvent];
 114     }];
 115 }
 116 
 117 - (void)application:(NSApplication *)theApplication openFiles:(NSArray *)fileNames
 118 {
 119     [self->queue addObject:^(){
 120         [realDelegate application:theApplication openFiles:fileNames];
 121     }];
 122 }
 123 
 124 - (NSApplicationPrintReply)application:(NSApplication *)application printFiles:(NSArray *)fileNames withSettings:(NSDictionary *)printSettings showPrintPanels:(BOOL)showPrintPanels
 125 {
 126     if (!fHandlesDocumentTypes) {
 127         return NSPrintingCancelled;
 128     }
 129 
 130     [self->queue addObject:^(){
 131         [realDelegate application:application printFiles:fileNames withSettings:printSettings showPrintPanels:showPrintPanels];
 132     }];
 133 
 134     // well, a bit premature, but what else can we do?..
 135     return NSPrintingSuccess;
 136 }
 137 
 138 - (void)_willFinishLaunching
 139 {
 140     QueuingApplicationDelegate * q = self;
 141     [self->queue addObject:^(){
 142         [[realDelegate class] _willFinishLaunching];
 143     }];
 144 }
 145 
 146 - (BOOL)applicationShouldHandleReopen:(NSApplication *)theApplication hasVisibleWindows:(BOOL)flag
 147 {
 148     [self->queue addObject:^(){
 149         [realDelegate applicationShouldHandleReopen:theApplication hasVisibleWindows:flag];
 150     }];
 151     return YES;
 152 }
 153 
 154 - (NSApplicationTerminateReply)applicationShouldTerminate:(NSApplication *)app
 155 {
 156     [self->queue addObject:^(){
 157         [realDelegate applicationShouldTerminate:app];
 158     }];
 159     return NSTerminateLater;
 160 }
 161 
 162 - (void)_systemWillPowerOff
 163 {
 164     [self->queue addObject:^(){
 165         [[realDelegate class] _systemWillPowerOff];
 166     }];
 167 }
 168 
 169 - (void)_appDidActivate
 170 {
 171     [self->queue addObject:^(){
 172         [[realDelegate class] _appDidActivate];
 173     }];
 174 }
 175 
 176 - (void)_appDidDeactivate
 177 {
 178     [self->queue addObject:^(){
 179         [[realDelegate class] _appDidDeactivate];
 180     }];
 181 }
 182 
 183 - (void)_appDidHide
 184 {
 185     [self->queue addObject:^(){
 186         [[realDelegate class] _appDidHide];
 187     }];
 188 }
 189 
 190 - (void)_appDidUnhide
 191 {
 192     [self->queue addObject:^(){
 193         [[realDelegate class] _appDidUnhide];
 194     }];
 195 }
 196 
 197 - (void)processQueuedEventsWithTargetDelegate:(id <NSApplicationDelegate>)delegate
 198 {
 199     NSUInteger i;
 200     NSUInteger count = [self->queue count];
 201 
 202     realDelegate = delegate;
 203 
 204     for (i = 0; i < count; i++) {
 205         void (^event)() = (void (^)())[self->queue objectAtIndex: i];
 206         event();
 207     }
 208 
 209     [self->queue removeAllObjects];
 210 }
 211 
 212 @end
 213