#import "BoomWindowController.h" @implementation BoomWindowController - (id)initWithResource:(id )newResource { self = [self initWithWindowNibName:@"boom"]; if( !self ) return nil; boomRec = (BoomRec *) calloc( 1, sizeof(BoomRec) ); [[newResource data] getBytes:boomRec]; // fill in default values if necessary if( boomRec->GraphicIndex < 0 || boomRec->GraphicIndex > 63 ) boomRec->GraphicIndex = 0; if( (boomRec->SoundIndex < 0 || boomRec->SoundIndex > 63) && (boomRec->SoundIndex != -1) ) boomRec->SoundIndex = 0; if( boomRec->FrameAdvance < 1 || boomRec->FrameAdvance > 1000 ) boomRec->FrameAdvance = 100; // use resource values to create NS objects silent = (boomRec->SoundIndex == -1); image = [[NSNumber alloc] initWithShort:boomRec->GraphicIndex + kMinBoomSpinID]; sound = [[NSNumber alloc] initWithShort:(silent? kMinBoomSoundID : boomRec->SoundIndex + kMinBoomSoundID)]; frameRate = [[NSNumber alloc] initWithShort:boomRec->FrameAdvance]; return self; } - (void)windowDidLoad { [super windowDidLoad]; // set combo box data sources [graphicsField setDelegate:spinDataSource]; [graphicsField setDataSource:spinDataSource]; [soundField setDelegate:soundDataSource]; [soundField setDataSource:soundDataSource]; // set notifications for ending editing on a combo box [localCenter addObserver:self selector:@selector(comboBoxWillPopUp:) name:NSComboBoxWillPopUpNotification object:nil]; [localCenter addObserver:self selector:@selector(controlTextDidChange:) name:NSComboBoxWillDismissNotification object:nil]; [localCenter addObserver:self selector:@selector(controlTextDidChange:) name:NSControlTextDidChangeNotification object:nil]; // mark window changed if initial values were invalid if( ![[resource data] isEqualToData:[NSData dataWithBytes:boomRec length:sizeof(BoomRec)]] ) { [resource touch]; [self setDocumentEdited:YES]; } [self update]; [self showWindow:self]; } - (void)update { // graphics [graphicsField setObjectValue:[spinDataSource stringValueForResID:image]]; [frameRateField setObjectValue:frameRate]; // sound [soundField setObjectValue:[soundDataSource stringValueForResID:sound]]; [soundButton setState:!silent]; [soundField setEnabled:!silent]; [playButton setEnabled:!silent]; // image well [imageWell setImage:[[[NSImage alloc] initWithData:[(id )[NSClassFromString(@"Resource") resourceOfType:[plugBundle localizedStringForKey:@"spin" value:@"" table:@"Resource Types"] andID:image inDocument:nil] data]] autorelease]]; } - (void)comboBoxWillPopUp:(NSNotification *)notification { id sender = [notification object]; if( sender == graphicsField ) [spinDataSource parseForString:[sender stringValue] withinRange:NSMakeRange(kMinBoomSpinID, kBoomSpinIDRange) sorted:YES]; else if( sender == soundField ) [soundDataSource parseForString:[sender stringValue] withinRange:NSMakeRange(kMinBoomSoundID, kBoomSoundIDRange) sorted:YES]; if( [sender class] == [NSComboBox class] ) [sender reloadData]; } - (void)controlTextDidChange:(NSNotification *)notification { id sender = [notification object]; if( sender == graphicsField && [sender stringValue] ) { id old = image; image = [[DataSource resIDFromStringValue:[sender stringValue]] retain]; if( ![image isEqualToNumber:old] ) [resource touch]; [old release]; } else if( sender == soundField && [sender stringValue] ) { id old = sound; sound = [[DataSource resIDFromStringValue:[sender stringValue]] retain]; if( ![sound isEqualToNumber:old] ) [resource touch]; [old release]; } else if( sender == frameRateField ) { id old = frameRate; frameRate = [[NSNumber alloc] initWithInt:[sender intValue]]; if( ![frameRate isEqualToNumber:old] ) [resource touch]; [old release]; } // hack to simply & easily parse combo boxes [self comboBoxWillPopUp:notification]; [self setDocumentEdited:[resource isDirty]]; } - (IBAction)toggleSilence:(id)sender { silent = ![soundButton state]; [soundField setEnabled:!silent]; [playButton setEnabled:!silent]; [resource touch]; [self setDocumentEdited:YES]; } - (IBAction)playSound:(id)sender { NSData *data = [(id )[NSClassFromString(@"Resource") resourceOfType:[plugBundle localizedStringForKey:@"snd" value:@"" table:@"Resource Types"] andID:sound inDocument:nil] data]; if( data && [data length] != 0 ) { SndListPtr sndPtr = (SndListPtr) [data bytes]; SndPlay( nil, &sndPtr, false ); } else NSBeep(); } - (NSDictionary *)validateValues { NSMutableDictionary *errorValues = [NSMutableDictionary dictionary]; // put current values into boomRec boomRec->GraphicIndex = [image shortValue] - kMinBoomSpinID; boomRec->SoundIndex = [sound shortValue] - kMinBoomSoundID; boomRec->FrameAdvance = [frameRate shortValue]; if( silent ) boomRec->SoundIndex = -1; // verify values are valid if( boomRec->GraphicIndex < 0 || boomRec->GraphicIndex > 63 ) [errorValues setObject:@"must match a spin resource with ID between 400 and 463." forKey:@"Graphics"]; if( boomRec->SoundIndex < -1 || boomRec->SoundIndex > 63 ) [errorValues setObject:@"must match a sound resource with ID between 300 and 363." forKey:@"Sound"]; if( boomRec->FrameAdvance < 1 || boomRec->FrameAdvance > 1000 ) [errorValues setObject:@"cannot be below 0% or above 1000%." forKey:@"Frame Advance"]; // all values fell within acceptable range return errorValues; } - (void)saveResource { // save new data into resource structure (should have already been validated, and boomRec filled out correctly) [resource setData:[NSData dataWithBytes:boomRec length:sizeof(BoomRec)]]; } @end