/* QuizController.m - abstract quiz controller class for Popup.app Copyright (C) 2004 Rob Burns January,21 2004 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., 675 Mass Ave, Cambridge, MA 02111, USA. */ #include "QuizController.h" #include "Constants.h" @implementation QuizController + (void) initialize { NSUserDefaults *defaults = [NSUserDefaults standardUserDefaults]; NSDictionary *appDefaults = [NSDictionary dictionaryWithObjectsAndKeys: @"YES", @"Sound", @"3", @"Repetitions", nil]; [defaults registerDefaults:appDefaults]; } -(void)dealloc { RELEASE(cardStack); RELEASE(wrong); RELEASE(currentCard); RELEASE(filteredCards); RELEASE(achievementList); [super dealloc]; } - (id) initWithStack: (StackModel *)aStack { self = [super initWithWindowNibName: [self nibName] owner: self]; ASSIGN(cardStack, aStack); filteredCards = RETAIN([NSArray arrayWithArray: [cardStack filteredCards]]); achievementList = [[NSMutableArray alloc] init]; reps = [[[NSUserDefaults standardUserDefaults] objectForKey: @"Repetitions"] intValue]; wrong = [[NSSound alloc] initWithContentsOfFile: [[NSBundle mainBundle] pathForSoundResource: @"Wrong.wav"] byReference: NO]; flipflag = NO; if(flipflag == NO) flipflag = YES; [cardStack flipLangs]; clock = [NSTimer scheduledTimerWithTimeInterval: 1 target: self selector: @selector(clockTick:) userInfo: nil repeats: YES]; return self; } - (void) awakeFromNib { if( clockText ) { [clockText setStringValue: @"00:00"]; } if( cardDisplay ) { [cardDisplay setFont: [NSFont systemFontOfSize: 20]]; } } // **************** // Property methods // **************** - (StackModel *) cardStack { return cardStack; } - (int) quizMethod { return quizMethod; } - (void) setQuizMethod: (int)method { quizMethod = method; if( [self quizMethod] == SHOWONCE_QUIZ_METHOD ) target = [[cardStack filteredCards] count]; else if( [self quizMethod] == REPETITION_QUIZ_METHOD ) target = [[cardStack filteredCards] count] * reps; [self clearAchievementList]; } - (NSArray *) achievementList { return achievementList; } // ************ // Quiz methods // ************ - (void) beginQuiz: (id)sender { if( progress ) { [progress setDoubleValue: 0]; [progress setMaxValue: target]; } [self drawNextCard: self]; } - (void) drawNextCard: (id)sender { if([self isFinished]==NO) { [currentCard release]; currentCard = [cardStack drawCardUsingAchievement: achievementList method: quizMethod]; [currentCard retain]; [self displayCurrentCard: self]; } else { [self displayFinishedMessage: self]; [clock invalidate]; [self displayResults: self]; } } - (void) correctAnswer: (id)sender { [self displayCorrectMessage: self]; [self setCardPassedAtIndex: [currentCard index]]; if( progress ) [progress incrementBy: 1]; t = [NSTimer scheduledTimerWithTimeInterval: .5 target: self selector: @selector(drawNextCard:) userInfo: nil repeats: NO]; } - (void) wrongAnswer: (id)sender { [self displayWrongMessage: self]; [self setCardFailedAtIndex: [currentCard index]]; if( progress && [self quizMethod] == SHOWONCE_QUIZ_METHOD ) [progress incrementBy: 1]; t = [NSTimer scheduledTimerWithTimeInterval: 4 target: self selector: @selector(drawNextCard:) userInfo: nil repeats: NO]; } - (void) clockTick: (id) sender { NSString *time; int min, sec; if( clockText ) { time = [[NSString alloc] initWithString: [clockText stringValue]]; min = [[[time componentsSeparatedByString: @":"] objectAtIndex: 0] intValue]; sec = [[[time componentsSeparatedByString: @":"] objectAtIndex: 1] intValue]; if( sec == 59 ) { [clockText setStringValue: [NSString stringWithFormat: @"%.2d:00", min+1]]; } else { [clockText setStringValue: [NSString stringWithFormat: @"%.2d:%.2d", min, sec+1]]; } RELEASE(time); } } // ************************* // Interface related methods // ************************* - (NSString *) nibName { // default implementation returns nil. return nil; } - (NSString *) quizName { // default implementation returns nil. return nil; } -(void) displayCurrentCard: (id) sender { // default implementation is empty. } - (void) displayCorrectMessage: (id) sender { if( cardDisplay ) [cardDisplay setStringValue: _(@"Correct!")]; } - (void) displayWrongMessage: (id) sender { NSString *word = [currentCard front]; NSString *meaning = [currentCard back]; if( cardDisplay ) { [cardDisplay setStringValue: [NSString stringWithFormat: _(@"Wrong! \n %@ -> %@"), word , meaning]]; [cardDisplay setTextColor: [NSColor redColor]]; [cardDisplay setBackgroundColor: [NSColor blackColor]]; } if([[[NSUserDefaults standardUserDefaults] objectForKey: @"Sound"] isEqualToString: @"YES"]) { [wrong play]; } } - (void) displayFinishedMessage: (id) sender { if( cardDisplay ) [cardDisplay setStringValue: _(@"Finished!")]; } - (void) displayResults: (id)sender { int result; if( [self quizMethod] == SHOWONCE_QUIZ_METHOD ) { result = NSRunInformationalAlertPanel(_(@"Quiz Results"), [NSString stringWithFormat: _(@"Number of passed cards: %i \n Number of failed cards: %i"), [self correctCount], [self wrongCount]], _(@"Keep Results"), _(@"Discard Results"), nil); if( result == NSAlertDefaultReturn ) { [[cardStack scheduler] processQuiz: self]; [[self window] performClose: self]; } if( result == NSAlertAlternateReturn ) { [[self window] performClose: self]; } } else if( [self quizMethod] == REPETITION_QUIZ_METHOD ) { result = NSRunInformationalAlertPanel(_(@"Practice Results"), [NSString stringWithFormat: _(@"Correct answers: %i \n Wrong answers: %i"), [self correctCount], [self wrongCount]], _(@"OK"), nil, nil); [[self window] performClose: self]; } } // ************************ // Quiz achievement methods // ************************ - (void) clearAchievementList { int i; correctCount = wrongCount = 0; [achievementList removeAllObjects]; if( [self quizMethod] == SHOWONCE_QUIZ_METHOD ) { for( i=0; i < [filteredCards count]; i++ ) { [achievementList addObject: [NSNumber numberWithUnsignedInt: NOTUSED]]; } } else if( [self quizMethod] == REPETITION_QUIZ_METHOD ) { for( i=0; i < [filteredCards count]; i++ ) { [achievementList addObject: [NSNumber numberWithUnsignedInt: NOTUSED]]; } } } - (void) setCardPassedAtIndex: (int)index { int i; int old = 0;; if( [self quizMethod] == SHOWONCE_QUIZ_METHOD ) { for( i = 0; i < [filteredCards count]; i++ ) { if( [[filteredCards objectAtIndex: i] intValue] == index ) { break; } } [achievementList replaceObjectAtIndex: i withObject: [NSNumber numberWithInt: PASSED]]; correctCount++; } else if( [self quizMethod] == REPETITION_QUIZ_METHOD ) { for( i = 0; i < [filteredCards count]; i++ ) { if( [[filteredCards objectAtIndex: i] intValue] == index ) { break; } } old = [[achievementList objectAtIndex: i] intValue]; [achievementList replaceObjectAtIndex: i withObject: [NSNumber numberWithInt: ++old]]; correctCount++; } } - (void) setCardFailedAtIndex: (int)index { int i; if( [self quizMethod] == SHOWONCE_QUIZ_METHOD ) { for( i = 0; i < [filteredCards count]; i++ ) { if( [[filteredCards objectAtIndex: i] intValue] == index ) { break; } } [achievementList replaceObjectAtIndex: i withObject: [NSNumber numberWithInt: FAILED]]; wrongCount++; } else if( [self quizMethod] == REPETITION_QUIZ_METHOD ) { wrongCount++; } } - (BOOL) isFinished { int i; if( [self quizMethod] == SHOWONCE_QUIZ_METHOD ) { for( i=0; i < [achievementList count]; i++ ) { if( [[achievementList objectAtIndex: i] intValue] == NOTUSED ) return NO; } return YES; } else if( [self quizMethod] == REPETITION_QUIZ_METHOD ) { for( i=0; i < [achievementList count]; i++ ) { if( [[achievementList objectAtIndex: i] intValue] < reps ) return NO; } return YES; } return YES; } - (int) correctCount { return correctCount; } - (int) wrongCount { return wrongCount; } // ************************************ // NSWindowController override methdods // ************************************ - (NSString *) windowTitleForDocumentDisplayName: (NSString *) displayName { if( [self quizMethod] == REPETITION_QUIZ_METHOD ) return [NSString stringWithFormat: _(@"%@ - %@ Practice"), displayName, [self quizName]]; else return [NSString stringWithFormat: _(@"%@ - %@ Quiz"), displayName, [self quizName]]; } - (BOOL) windowShouldClose: (id) sender { if( flipflag == YES ) [cardStack flipLangs]; [[NSNotificationCenter defaultCenter] postNotificationName: @"EditStackNotification" object: cardStack]; RELEASE(self); return YES; } @end