/* * Controller.m - Simple application window management. * * $Id$ * * Basilisk II (C) 1997-2008 Christian Bauer * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #import "Controller.h" #import "Emulator.h" #import "sysdeps.h" // Types used in Basilisk C++ code #import #import #define DEBUG 0 #import #import "misc_macosx.h" #import "video_macosx.h" @implementation Controller // // Standard NSApplication methods that we override // - (id) init { #ifdef ENABLE_MULTIPLE emulators = [NSMutableArray new]; #endif return [super init]; } - (void) dealloc { #ifdef ENABLE_MULTIPLE [emulators dealloc]; #endif [super dealloc]; } - (void) awakeFromNib { #ifdef ENABLE_MULTIPLE [self NewEmulator: self]; // So the user gets something on screen #endif [[NSApplication sharedApplication] setDelegate: self]; // Enable applicationShouldTerminate } - (void) sendEvent: (NSEvent *)event; { // We can either process the event ourselves, // or pass it to our superclass for the other UI objects to process bool passToSuper = false; if ( [self isAnyEmulatorDisplayingSheets] || [[thePrefsEditor window] isVisible] || ! [self isAnyEmulatorRunning] ) passToSuper = true; if ( [[theEmulator screen] isFullScreen] ) passToSuper = false; if ( passToSuper ) [super sendEvent: event]; // NSApplication default else { NSEventType type = [event type]; if ( type == NSKeyUp || type == NSKeyDown || type == NSFlagsChanged ) [self dispatchKeyEvent: event type: type]; else [self dispatchEvent: event type: type]; } } // NSApplication methods which are invoked through delegation - (BOOL) applicationShouldTerminateAfterLastWindowClosed: (NSApplication *)app { return YES; } - (NSApplicationTerminateReply) applicationShouldTerminate: (NSApplication *)app { short count; const char *stillRunningMessage; if ( [thePrefsEditor hasEdited] ) if ( ChoiceAlert("Preferences have been edited", "Save changes", "Quit") ) SavePrefs(); // if ( edited ) // { // NSString *title = [NSString stringWithCString: getString(STR_WARNING_ALERT_TITLE)], // *msg = @"Preferences have been edited", // *def = @"Save changes", // *alt = @"Quit Application", // *other = @"Continue"; // // switch ( NSRunAlertPanel(title, msg, def, alt, other, nil) ) // { // case NSAlertDefault: savePrefs(); // case NSAlertAlternate: return NSTerminateNow; // case NSAlertOther: return NSTerminateCancel; // } // } if ( [[thePrefsEditor window] isVisible] ) [[thePrefsEditor window] performClose: self]; count = [self emulatorCreatedCount]; if ( count > 0 ) { if ( count > 1 ) stillRunningMessage = "Emulators are still running\nExiting Basilisk may lose data"; else stillRunningMessage = "Emulator is still running\nExiting Basilisk may lose data"; if ( ! ChoiceAlert(stillRunningMessage, "Exit", "Continue") ) return NSTerminateCancel; // NSTerminateLater? } return NSTerminateNow; } // Event dispatching, called by sendEvent - (void) dispatchKeyEvent: (NSEvent *)event type: (NSEventType)type { EmulatorView *view; #ifdef ENABLE_MULTIPLE // We need to work out what window's Emulator should receive these messages int tmp; for ( tmp = 0; tmp < [emulators count], ++tmp ) { theEmulator = [emulators objectAtIndex: tmp]; view = [theEmulator screen]; if ( [ theEmulator isRunning ] && ( [[theEmulator window] isKeyWindow] || [view isFullScreen] ) ) break; } if ( tmp < [emulators count] ) // i.e. if we exited the for loop #else view = [theEmulator screen]; if ( [theEmulator isRunning] && ( [[theEmulator window] isKeyWindow] || [view isFullScreen] ) ) #endif { D(NSLog(@"Got a key event - %d\n", [event keyCode])); switch ( type ) { case NSKeyUp: [view keyUp: event]; break; case NSKeyDown: D(NSLog(@"%s - NSKeyDown - %@", __PRETTY_FUNCTION__, event)); [view keyDown: event]; break; case NSFlagsChanged: [view flagsChanged: event]; break; default: NSLog(@"%s - Sent a non-key event (logic error)", __PRETTY_FUNCTION__); [super sendEvent: event]; } } else // No Basilisk window is key (maybe a panel or pane). [super sendEvent: event]; // Call NSApplication default } - (void) dispatchEvent: (NSEvent *)event type: (NSEventType)type { EmulatorView *view; BOOL fullScreen; #ifdef ENABLE_MULTIPLE // We need to work out what window's Emulator should receive these messages int tmp; for ( tmp = 0; tmp < [emulators count], ++tmp ) { theEmulator = [emulators objectAtIndex: tmp]; view = [theEmulator screen]; fullScreen = [view isFullScreen]; if ( [theEmulator isRunning] && ( fullScreen || [[theEmulator window] isMainWindow] ) ) break; } if ( tmp < [emulators count] ) // i.e. if we exited the for loop #else view = [theEmulator screen]; fullScreen = [view isFullScreen]; if ( [theEmulator isRunning] && ( fullScreen || [[theEmulator window] isMainWindow] ) ) #endif { if ( fullScreen || [view mouseInView: event] ) { switch ( type ) { case NSLeftMouseDown: [view mouseDown: event]; break; case NSLeftMouseUp: [view mouseUp: event]; break; case NSLeftMouseDragged: case NSMouseMoved: if ( fullScreen ) [view fullscreenMouseMove]; else [view processMouseMove: event]; break; default: [super sendEvent: event]; // NSApplication default } return; } } // Either the pointer is not in the Emulator's screen, no Basilisk window is running, // or no Basilisk window is main (e.g. there might be a panel or pane up). // // We should just be calling NSApp's default sendEvent, but then for some reason // mouseMoved events are still passed to our EmulatorView, so we filter them out. if ( type != NSMouseMoved ) [super sendEvent: event]; } // Methods to display documentation: - (IBAction) HelpHowTo: (id)sender { NSString *path = [[NSBundle mainBundle] pathForResource: @"HowTo" ofType: @"html"]; if ( ! path ) InfoSheet(@"Cannot find HowTo.html", [theEmulator window]); else if ( ! [[NSWorkspace sharedWorkspace] openFile: path] ) InfoSheet(@"Cannot open HowTo.html with default app", [theEmulator window]); } - (IBAction) HelpToDo: (id)sender { NSString *path = [[NSBundle mainBundle] pathForResource: @"ToDo" ofType: @"html"]; if ( ! path ) InfoSheet(@"Cannot find ToDo.html", [theEmulator window]); else if ( ! [[NSWorkspace sharedWorkspace] openFile: path withApplication: @"TextEdit"] ) InfoSheet(@"Cannot open ToDo.html with TextEdit", [theEmulator window]); } - (IBAction) HelpVersions: (id)sender { NSString *path = [[NSBundle mainBundle] pathForResource: @"Versions" ofType: @"html"]; if ( ! path ) InfoSheet(@"Cannot find Versions.html", [theEmulator window]); else if ( ! [[NSWorkspace sharedWorkspace] openFile: path withApplication: @"TextEdit"] ) InfoSheet(@"Cannot open Versions.html with TextEdit", [theEmulator window]); } // Menu items which for managing more than one window #ifdef ENABLE_MULTIPLE - (IBAction) NewEmulator: (id)sender { NSString *title; if ( ! [NSBundle loadNibNamed:@"Win512x342" owner:self] ) { NSLog(@"%s - LoadNibNamed@Win512x342 failed", __PRETTY_FUNCTION__); return; } if ( theEmulator == nil) { NSLog(@"%s - Newly created emulator's NIB stuff not fully linked?", __PRETTY_FUNCTION__); return; } [emulators addObject: theEmulator]; title = [NSString localizedStringWithFormat:@"BasiliskII Emulator %d", [emulators count]]; [theEmulator -> win setTitle: title]; } - (IBAction) PauseAll: (id)sender { [emulators makeObjectsPerformSelector:@selector(Suspend:) withObject:self]; } - (IBAction) RunAll: (id)sender { [emulators makeObjectsPerformSelector:@selector(Resume:) withObject:self]; } - (IBAction) TerminateAll: (id)sender { [emulators makeObjectsPerformSelector:@selector(Terminate:) withObject:self]; } #endif - (BOOL) isAnyEmulatorDisplayingSheets { #ifdef ENABLE_MULTIPLE int tmp; for ( tmp = 0; tmp < [emulators count], ++tmp ) if ( [[[emulators objectAtIndex: tmp] window] attachedSheet] ) break; if ( tmp < [emulators count] ) // i.e. if we exited the for loop #else if ( [[theEmulator window] attachedSheet] ) #endif return TRUE; return FALSE; } - (BOOL) isAnyEmulatorRunning { #ifdef ENABLE_MULTIPLE int tmp; for ( tmp = 0; tmp < [emulators count], ++tmp ) if ( [[emulators objectAtIndex: tmp] isRunning] ) break; if ( tmp < [emulators count] ) // i.e. if we exited the for loop #else if ( [theEmulator isRunning] ) #endif return TRUE; return FALSE; } - (short) emulatorCreatedCount { short count = 0; #ifdef ENABLE_MULTIPLE int tmp; for ( tmp = 0; tmp < [emulators count], ++tmp ) if ( [[emulators objectAtIndex: tmp] uaeCreated] ) ++count; #else if ( [theEmulator uaeCreated] ) ++count; #endif return count; } @end