// Copyright (c) 2002, Peter Bentley // All rights reserved. // // Redistribution and use in source and binary forms, with or without // modification, are permitted provided that the following conditions are // met: // // Redistributions of source code must retain the above copyright // notice, this list of conditions and the following disclaimer. // // Redistributions in binary form must reproduce the above copyright // notice, this list of conditions and the following disclaimer in the // documentation and/or other materials provided with the distribution. // // The name Peter Bentley may not be used to endorse or promote // products derived from this software without specific prior // written permission. // // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS // "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT // LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR // A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT // OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, // SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT // LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, // DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY // THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. #import "AppController.h" #import "OmdWindow.h" #import "OmdController.h" #import "OmdTransport.h" #import "OmdTocCtlr.h" #import "OmdDebugController.h" #import "PreferenceController.h" #import "slog.h" CVSID( "$Id: AppController.m,v 1.17 2003/08/22 18:17:49 pete Exp $" ); static AppController *theAppController = nil; void appctlr_log_out( int level, const char *msg ) { if( theAppController ) { [theAppController logOutput: msg atLevel: level]; } } @implementation AppController + (void) initialize { NSDictionary *defaultValues = [NSDictionary dictionaryWithObjectsAndKeys: @"2", OmdDebugLevelKey, @"1", OmdShowDebugKey, @"3", OmdActionIfNoUnitKey, @"0.25", OmdFastTickKey, @"3.5", OmdSlowTickKey, @"12.0", OmdStatusClearKey, nil ]; [[NSUserDefaults standardUserDefaults] registerDefaults: defaultValues]; NSLog( @"Registered defaults OK" ); } - (id) init { if( (self = [super init]) != nil ) { delay = -1.0; theAppController = self; slog_add_handler( LOG_TRACE, appctlr_log_out ); statusClearCount = 0.0; } return self; } - (void) awakeFromNib { slog( LOG_DEBUG, "AppController - Awake from NIB called" ); debugController = [[OmdDebugController alloc] init]; if( [[NSUserDefaults standardUserDefaults] integerForKey: OmdShowDebugKey] ) { [self showDebug: self]; } // Register for preference notifications [[NSNotificationCenter defaultCenter] addObserver: self selector: @selector(prefsChanged:) name: OmdPrefsChangedNotification object: nil]; // Read preferences [self prefsChanged: nil]; // Set up timer timer = nil; [self setTimerInterval: fastTickDelay]; //[timer fire]; // Make timer fire once initially } - (void) dealloc { [[NSNotificationCenter defaultCenter] removeObserver: self]; [timer invalidate]; [timer release]; [debugController release]; [preferenceController release]; [super dealloc]; } - (void) prefsChanged: (NSNotification *) notification { slowTickDelay = [[NSUserDefaults standardUserDefaults] floatForKey: OmdSlowTickKey]; fastTickDelay = [[NSUserDefaults standardUserDefaults] floatForKey: OmdFastTickKey]; statusClearDelay = [[NSUserDefaults standardUserDefaults] floatForKey: OmdStatusClearKey]; if( notification ) { if( [notification object] == OmdSlowTickKey ) { // slog( LOG_INFO, "*** Slow tick %g", slowTickDelay ); delay = 0.0; } else if( [notification object] == OmdFastTickKey ) { // slog( LOG_INFO, "*** Fast tick %g", fastTickDelay ); [self setTimerInterval: fastTickDelay]; } } } - (void) setTimerInterval: (double) interval { if( timer ) { [timer invalidate]; [timer release]; } timer = [NSTimer scheduledTimerWithTimeInterval: interval target: self selector: @selector( fastTick: ) userInfo: nil repeats: YES]; [timer retain]; } - (void) fastTick: (NSTimer *) theTimer { int i; OmdWindow *winctrler; omd_unit_t *unit; // slog( LOG_INFO, "tick" ); for( i = 0; i < omd_num_devices(); i++ ) { unit = omd_get_unit( i ); if( unit->userdata ) { winctrler = (id) unit->userdata; if( [winctrler pollUnit] < 0 ) { slog( LOG_INFO, "Unit poll failed... rescanning bus" ); [self scanUSB]; } } } if(delay < 0.0 || delay > slowTickDelay ) { delay = 0.0; [self slowTick: theTimer]; } delay += fastTickDelay; statusClearCount += fastTickDelay; if( statusClearCount >= statusClearDelay ) { statusClearCount = 0.0; [[self currentWindow] status: @""]; } } - (void) slowTick: (NSTimer *) theTimer { // slog( LOG_INFO, "TICK!" ); [self scanUSB]; } - (void) scanUSB { int i, choice, action; omd_unit_t *unit; OmdWindow *winctrler; static BOOL firstPass = YES; slog( LOG_DEBUG, "Rescanning USB for NetMD devices" ); numWindows = omd_scan_usb(); if( numWindows < 0 ) { slog( LOG_WARNING, "USB scan error" ); return; } if( numWindows == 0 ) { slog( LOG_INFO, "No NetMD devices found" ); if( firstPass ) { firstPass = NO; action = [[NSUserDefaults standardUserDefaults] integerForKey: OmdActionIfNoUnitKey]; slog( LOG_DEBUG, "No unit on first pass. Action = %d", action ); switch( action ) { case OMD_ACTION_EXIT: [[NSApplication sharedApplication] terminate: self]; break; case OMD_ACTION_IGNORE: break; case OMD_ACTION_PROBEALL: //slog( LOG_INFO, "Force scan not yet implemented" ); [self probeAll: nil]; delay = slowTickDelay; break; case OMD_ACTION_PROMPT: choice = NSRunAlertPanel( @"Warning", @"No NetMD Devices found\n\n" @"This message will not be displayed again but\n" @"Xmd can keep scanning for NetMD units.\n", @"Keep Scanning", @"Nah, Give up", nil ); if( choice != NSAlertDefaultReturn ) { [[NSApplication sharedApplication] terminate: self]; } break; default: slog( LOG_ERROR, "Unexpected startup action %d!", action ); break; } } return; } /* Scan USB data */ for( i = 0; i < numWindows; i++ ) { unit = omd_get_unit( i ); if( unit->changed ) { slog( LOG_INFO, "NetMD unit %d changed", i ); winctrler = (OmdWindow *) unit->userdata; if( winctrler ) { if( unit->present ) { slog( LOG_WARNING, "Exisitng window changed??" ); } else { slog( LOG_INFO, "Detach: Unit %d", i ); [winctrler close]; unit->userdata = NULL; } } else if( unit->present ) { slog( LOG_INFO, "New NetMD unit detected... Creating window" ); winctrler = [[OmdWindow alloc] initWithUnit: unit]; [winctrler showWindow: self]; unit->userdata = winctrler; } } } firstPass = NO; } - (void) logOutput: (const char *) message atLevel: (int) level { NSString *str; str = [[NSString alloc] initWithCString: message]; [str autorelease]; [debugController addLine: str atLevel: level]; if( level >= LOG_INFO ) { [[self currentWindow] status: str]; statusClearCount = 0.0; } } - (OmdWindow *) currentWindow { omd_unit_t *unit; if( numWindows < 1 ) { return nil; } // XXX This is Ÿber-bogus when we have more than 1 window unit = omd_get_unit( 0 ); return (OmdWindow *) unit->userdata; } - (OmdController *) currentController { return [[self currentWindow] getController]; } - (OmdTransport *) currentTransport { return [[self currentController] getTransport]; } - (OmdTocCtlr *) currentTocCtlr { return [[self currentController] getTocCtlr]; } - (IBAction) showDebug: (id) sender { [debugController showWindow: self]; } - (IBAction) play: (id) sender { [[self currentTransport] play: sender]; } - (IBAction) stop: (id) sender { [[self currentTransport] stop: sender]; } - (IBAction) pause: (id) sender { [[self currentTransport] pause: sender]; } - (IBAction) fforward: (id) sender { [[self currentTransport] fforward: sender]; } - (IBAction) rewind: (id) sender { [[self currentTransport] rewind: sender]; } - (IBAction) nextTrack: (id) sender { [[self currentTransport] nextTrack: sender]; } - (IBAction) previousTrack: (id) sender { [[self currentTransport] previousTrack: sender]; } - (IBAction) test: (id) sender { slog( LOG_INFO, "Ouch! Please don't do that..." ); [[self currentTocCtlr] test: sender]; } - (IBAction) createGroup: (id) sender { [[self currentTocCtlr] createGroup: sender]; } - (IBAction) deleteThings: (id) sender { [[self currentTocCtlr] deleteThings: sender]; } - (BOOL) validateMenuItem: (id ) menuItem { slog( LOG_TRACE, "Appcontroller validateMenuItem" ); if( [menuItem action] == @selector( showPreferencePanel: )) { return YES; } if( [menuItem action] == @selector( showDebug: )) { return YES; } if( [menuItem action] == @selector( probeAll: )) { return YES; } return [[self currentTocCtlr] validateMenuItem: menuItem]; } - (IBAction) probeAll: (id) sender { int choice; choice = NSRunAlertPanel( @"Warning", @"About to scan all USB devices\n\n" @"This shouldn't cause a problem, but you never know\n", @"Do it!", @"Hmm, maybe later", nil ); if( choice != NSAlertDefaultReturn ) { return; } slog( LOG_INFO, "Probing all USB devices to try and find Net MD ones" ); if( omd_probe_all() > 0 ) { delay = slowTickDelay; // Force rescan next tick } else { slog( LOG_INFO, "No Net MD devices found" ); } } - (IBAction) showPreferencePanel: (id) sender { if( ! preferenceController ) { preferenceController = [[PreferenceController alloc] init]; } [preferenceController showWindow: self]; [[preferenceController window] makeKeyAndOrderFront: self]; [preferenceController updateView]; } @end