落書きノート

ふと自分が気になった事を書いてます

Objective-C + Cocoa + AVFoundationと音声読み上げ機能を使った副音声のプログラム。完成版。

デスクトップアプリケーション向けのコードの情報が少ないので、需要あるかなぁと思って載せてみる。自分用に抜き出して使ってみてください。自分が作った副音声のプログラムから抜粋しています。

#import <Cocoa/Cocoa.h>
#import <AVKit/AVKit.h>
#import <AVFoundation/AVFoundation.h>

@interface AppDelegate : NSObject <NSApplicationDelegate>

@property (assign) IBOutlet NSWindow *window;
@property (unsafe_unretained) IBOutlet NSWindow *textViewWindow;
@property (unsafe_unretained) IBOutlet NSWindow *jumpWindow;
@property (unsafe_unretained) IBOutlet NSWindow *speechInputWindow;
@property (unsafe_unretained) IBOutlet NSWindow *speechInputFixWindow;
@property (weak) IBOutlet AVPlayerView *avView;
@property (unsafe_unretained) IBOutlet NSTextView *textView;
@property (weak) IBOutlet NSTextField *HoursField;
@property (weak) IBOutlet NSTextField *MinutesField;
@property (weak) IBOutlet NSTextField *SecondsField;
@property (weak) IBOutlet NSTextField *speechInputField;
@property (weak) IBOutlet NSTextField *speechStartSecondsField;
@property (weak) IBOutlet NSTextField *speechPauseSecondsField;
@property (weak) IBOutlet NSTextField *speechInputFixField;
@property (weak) IBOutlet NSTextField *speechStartSecondsFixField;
@property (weak) IBOutlet NSTextField *speechPauseSecondsFixField;
@property (weak) IBOutlet NSMenuItem *pauseOrPlayMenu;

@property AVPlayer *player;
@property CMTime duration;
@property NSString *text;
@property NSSpeechSynthesizer *speech;
@property NSTimer *timer;
@property BOOL saveFlag;
@property NSURL *saveFilePath;
@property NSInteger alertSaveReturnCode;
@property NSURL *nilPath;

- (IBAction)onSelectedOpenFile:(id)sender;
- (IBAction)onSelectedOpenScriptFile:(id)sender;
- (IBAction)onPauseSelected:(id)sender;
- (IBAction)onBitReturned:(id)sender;
- (IBAction)onBitProceed:(id)sender;
- (IBAction)onReturned:(id)sender;
- (IBAction)onProceed:(id)sender;
- (IBAction)onSpeechMenu:(id)sender;
- (IBAction)onSpeakingButton:(id)sender;
- (IBAction)onSpeakingCancelButton:(id)sender;
- (IBAction)onTimeJump:(id)sender;
- (IBAction)onTimeGoJump:(id)sender;
- (IBAction)onTimeJumpCancel:(id)sender;
- (IBAction)onMovieWindow:(id)sender;
- (IBAction)onScriptWindow:(id)sender;
- (IBAction)onMovieFullScreen:(id)sender;
- (IBAction)onSaveFile:(id)sender;
- (IBAction)onSaveAsFile:(id)sender;
- (IBAction)onFixSpeechMenu:(id)sender;
- (IBAction)onSpeechFixButton:(id)sender;
- (IBAction)onSpeechFixCancel:(id)sender;
- (IBAction)onNewDocument:(id)sender;

- (BOOL)applicationShouldHandleReopen:(NSApplication *)theApplication hasVisibleWindows:(BOOL)flag;
- (void)textDidChange:(NSNotification *)notification;
@end

#import "AppDelegate.h"

@implementation AppDelegate

- (void)applicationDidFinishLaunching:(NSNotification *)aNotification
{
    self.saveFlag = NO;
}

- (IBAction)onSelectedOpenFile:(id)sender {
    NSOpenPanel *openPanel  = [NSOpenPanel openPanel];
    NSArray *allowedFileTypes = [NSArray arrayWithObjects:@"mp4",@"avi",nil];
    [openPanel setAllowedFileTypes:allowedFileTypes];
    NSInteger pressedButton = [openPanel runModal];
    
    if(pressedButton == NSOKButton){
        NSURL *filePath = [openPanel URL];
        
        self.player = [[AVPlayer alloc] initWithURL:filePath];
        self.avView.player = self.player;
        self.speech = [[NSSpeechSynthesizer alloc] initWithVoice:@"com.apple.speech.synthesis.voice.kyoko.premium"];
        self.timer =[NSTimer scheduledTimerWithTimeInterval:1.0f target:self selector:@selector(startSpeech:) userInfo:nil repeats:YES];
        [self.avView.player play];
        
    }
}

- (void)startSpeech:(NSTimer*)timer{
    NSString *line;
    NSRange range,subRange;
    NSInteger count = 0;
    
    range = NSMakeRange(0, [self.textView.string length]);
    
    while (range.length > 0) {
        subRange= [self.textView.string lineRangeForRange:NSMakeRange(range.location, 0)];
        line = [self.textView.string substringWithRange:subRange];
        
        if([[line stringByTrimmingCharactersInSet:[NSCharacterSet whitespaceAndNewlineCharacterSet]] length] == 0) {

            range.location = NSMaxRange(subRange);
            range.length -= subRange.length;
            continue;
        }
        
        if(count % 2){
            NSArray *msg = [line componentsSeparatedByString:@"\t"];
            
            if([msg count] == 3){
                NSString *message = [msg objectAtIndex:1];
                NSInteger sumTime = [[msg objectAtIndex:2] intValue];
                
                if((NSInteger)CMTimeGetSeconds(self.avView.player.currentTime) == sumTime){
                    [self.speech startSpeakingString:message];
                }
                
            
            }else if([msg count] == 4){
                NSString *message = [msg objectAtIndex:1];
                NSInteger sumTime = [[msg objectAtIndex:2] intValue];
                NSInteger pauseTime = [[[msg objectAtIndex:3] substringFromIndex:1] intValue];
                
                if((NSInteger)CMTimeGetSeconds(self.avView.player.currentTime) == sumTime){
                    [self.avView.player pause];
                    [self.speech startSpeakingString:message];
                    [NSThread sleepForTimeInterval:pauseTime];
                    [self.avView.player play];
                }
            }
            
            range.location = NSMaxRange(subRange);
            range.length -= subRange.length;
            count++;
        }else{
            range.location = NSMaxRange(subRange);
            range.length -= subRange.length;
            count++;
            continue;
        }
        
        
    }
}

- (IBAction)onSelectedOpenScriptFile:(id)sender {
    if(self.saveFlag == YES){
        NSAlert *alert = [NSAlert alertWithMessageText:@"ファイル未保存"
                                     defaultButton:@"保存"
                                   alternateButton:@"キャンセル"
                                       otherButton:nil
                         informativeTextWithFormat:@"ファイルを保存していません。保存しますか?"];
        [alert beginSheetModalForWindow:self.textViewWindow
                      modalDelegate:self
                     didEndSelector:@selector(alertOpenScriptSaveDidEnd:returnCode:contextInfo:)
                        contextInfo:nil];
    }else{
        NSOpenPanel *openPanel = [NSOpenPanel openPanel];
        NSArray *allowedFileTypes = [NSArray arrayWithObjects:@"txt",nil];
        [openPanel setAllowedFileTypes:allowedFileTypes];
        NSInteger pressedButton = [openPanel runModal];
    
        if(pressedButton == NSOKButton){
            NSURL *filePath = [openPanel URL];
        
            self.saveFilePath = filePath;
            [self.textViewWindow orderFront:self];
        
            NSData *targetData = [[NSData alloc] initWithContentsOfFile:[filePath path]];
        
            self.text = [NSString stringWithContentsOfFile:[filePath path] encoding:[self detectEncoding:targetData] error:nil];
            [self.textViewWindow makeKeyAndOrderFront:self];
            [self.textView setString:@""];
            [self.textView insertText:self.text];
            [self.textView moveToBeginningOfDocument:self];
        }
    }
}

- (void)alertOpenScriptSaveDidEnd:(NSAlert *)alert returnCode:(NSInteger)returnCode contextInfo:(void *)contextInfo {
    
    if(returnCode == NSAlertDefaultReturn){
        NSFileManager *fileManager = [NSFileManager defaultManager];
        
        if(![fileManager fileExistsAtPath:[self.saveFilePath path]] || [self.saveFilePath.path length] == 0){
            NSSavePanel *savePanel = [NSSavePanel savePanel];
            NSArray *allowedFileTypes = [NSArray arrayWithObjects:@"txt",@"'TEXT'",nil];
            [savePanel setAllowedFileTypes:allowedFileTypes];
            NSInteger pressedButton = [savePanel runModal];
            
            if(pressedButton == NSOKButton){
                NSURL *filePath = [savePanel URL];
                
                self.saveFilePath = filePath;
                [self.textView.string writeToFile:[filePath path] atomically:YES encoding:NSUTF8StringEncoding error:nil];
                NSInteger targetTitleLength = [self.textViewWindow.title length];
                NSString *targetTitle = [self.textViewWindow.title substringToIndex:(targetTitleLength - 1)];
                [self.textViewWindow setTitle:targetTitle];
                self.saveFlag = NO;
            }else if(pressedButton == NSCancelButton){
                return;
            }
            
        }else{
            NSData *targetData = [[NSData alloc] initWithContentsOfFile:[self.saveFilePath path]];
            
            [self.textView.string writeToFile:[self.saveFilePath path] atomically:YES encoding:[self detectEncoding:targetData] error:nil];
            NSInteger targetTitleLength = [self.textViewWindow.title length];
            NSString *targetTitle = [self.textViewWindow.title substringToIndex:(targetTitleLength - 1)];
            [self.textViewWindow setTitle:targetTitle];
            self.saveFlag = NO;
        }
        
        NSOpenPanel *openPanel = [NSOpenPanel openPanel];
        NSArray *allowedFileTypes = [NSArray arrayWithObjects:@"txt",nil];
        [openPanel setAllowedFileTypes:allowedFileTypes];
        NSInteger pressedButton = [openPanel runModal];
        
        if(pressedButton == NSOKButton){
            NSURL *filePath = [openPanel URL];
            
            self.saveFilePath = filePath;
            [self.textViewWindow orderFront:self];
            
            NSData *targetData = [[NSData alloc] initWithContentsOfFile:[filePath path]];
            
            self.text = [NSString stringWithContentsOfFile:[filePath path] encoding:[self detectEncoding:targetData] error:nil];
            [self.textViewWindow makeKeyAndOrderFront:self];
            [self.textView setString:@""];
            [self.textView insertText:self.text];
            [self.textView moveToBeginningOfDocument:self];
        }else if(pressedButton == NSCancelButton){
            return;
        }
    }
}

- (NSStringEncoding)detectEncoding:(NSData *)data
{
    NSStringEncoding encoding;
    NSStringEncoding encodings[] = {
        NSUTF8StringEncoding,
        NSNonLossyASCIIStringEncoding,
        NSShiftJISStringEncoding,
        NSJapaneseEUCStringEncoding,
        NSMacOSRomanStringEncoding,
        NSWindowsCP1251StringEncoding,
        NSWindowsCP1252StringEncoding,
        NSWindowsCP1253StringEncoding,
        NSWindowsCP1254StringEncoding,
        NSWindowsCP1250StringEncoding,
        NSISOLatin1StringEncoding,
        NSUnicodeStringEncoding,
        0
    };
    
    int i = 0;
    NSString *try_str;
    
    if (memchr([data bytes], 0x1b, [data length]) != NULL) {
        try_str = [[NSString alloc] initWithData:data encoding:NSISO2022JPStringEncoding];
        if (try_str != nil)
            return NSISO2022JPStringEncoding;
    }
    
    while(encodings[i] != 0){
        try_str = [[NSString alloc] initWithData:data encoding:encodings[i]];
        if (try_str != nil) {
            encoding = encodings[i];
            break;
        }
        i++;
    }
    return encoding;
}

- (IBAction)onPauseSelected:(id)sender {
    if([self.avView.player rate] == 0.0){
        [self.avView.player play];
        [self.pauseOrPlayMenu setTitle:@"一時停止"];
    }else{
        [self.avView.player pause];
        [self.pauseOrPlayMenu setTitle:@"再生"];
    }
}

- (IBAction)onBitReturned:(id)sender {
    CMTime time = CMTimeMake(300, 60);
    self.duration = CMTimeSubtract(self.avView.player.currentTime, time);
    [self.avView.player seekToTime:self.duration toleranceBefore:kCMTimeZero toleranceAfter:kCMTimeZero];
    
}

- (IBAction)onBitProceed:(id)sender {
    CMTime time = CMTimeMake(300, 60);
    self.duration = CMTimeAdd(self.avView.player.currentTime, time);
    [self.avView.player seekToTime:self.duration toleranceBefore:kCMTimeZero toleranceAfter:kCMTimeZero];
}

- (IBAction)onReturned:(id)sender {
    CMTime time = CMTimeMake(1800, 60);
    self.duration = CMTimeSubtract(self.avView.player.currentTime, time);
    [self.avView.player seekToTime:self.duration toleranceBefore:kCMTimeZero toleranceAfter:kCMTimeZero];
}

- (IBAction)onProceed:(id)sender {
    CMTime time = CMTimeMake(1800, 60);
    self.duration = CMTimeAdd(self.avView.player.currentTime, time);
    [self.avView.player seekToTime:self.duration toleranceBefore:kCMTimeZero toleranceAfter:kCMTimeZero];
}

- (IBAction)onSpeechMenu:(id)sender {
    [self.avView.player pause];
    [self.speechInputWindow makeKeyAndOrderFront:self];
}

- (IBAction)onSpeakingButton:(id)sender {
    bool pauseFlag = false;
    NSString *speaking;
    NSRange fixRange;
    
    if([self.speechInputField.stringValue length] == 0){
        NSAlert *alert = [NSAlert alertWithMessageText:@"エラーメッセージ"
                                          defaultButton:@"OK"
                                        alternateButton:nil
                                            otherButton:nil
                              informativeTextWithFormat:@"読み上げる文章を入力してください。"];
        
        [alert beginSheetModalForWindow:self.speechInputWindow
                          modalDelegate:self
                         didEndSelector:@selector(alertErrorInputDidEnd:returnCode:contextInfo:)
                            contextInfo:nil];
        return;
    }else if([self.speechStartSecondsField.stringValue length] == 0){
        NSAlert *alert = [NSAlert alertWithMessageText:@"エラーメッセージ"
                                         defaultButton:@"OK"
                                       alternateButton:nil
                                           otherButton:nil
                             informativeTextWithFormat:@"読み上げる秒数を指定してください。"];
        
        [alert beginSheetModalForWindow:self.speechInputWindow
                          modalDelegate:self
                         didEndSelector:@selector(alertErrorInputDidEnd:returnCode:contextInfo:)
                            contextInfo:nil];
        return;
    }
    
    if([self.speechPauseSecondsField.stringValue length] != 0){
        pauseFlag = true;
    }
    
    if(pauseFlag == true){
        speaking = [NSString stringWithFormat:@"%@\t%@\t*%@", [self.speechInputField stringValue], [self.speechStartSecondsField stringValue], [self.speechPauseSecondsField stringValue]];
    }else{
        speaking = [NSString stringWithFormat:@"%@\t%@", [self.speechInputField stringValue],[self.speechStartSecondsField stringValue]];
    }
    
    fixRange = [self.textView selectedRange];
    
    if(fixRange.length == 0){
        NSString *saveSpeaking = [self.speechInputField stringValue];
        NSString *newline = @"\n";
        float latestTime = CMTimeGetSeconds(self.avView.player.currentTime);
        NSDate *date = [[NSDate alloc] initWithTimeIntervalSince1970:latestTime];
        NSDateFormatter *dateFormatter = [[NSDateFormatter alloc] init];
        [dateFormatter setTimeZone:[NSTimeZone timeZoneWithName:@"UTC"]];
        [dateFormatter setDateFormat:@"HH:mm:ss"];
        NSString *result = [dateFormatter stringFromDate:date];
        result = [newline stringByAppendingString:result];
        result = [result stringByAppendingString:@"\n\t"];
        speaking = [speaking stringByAppendingString:@"\n"];
        [self.textView insertText:result];
        [self.textView insertText:speaking];
        [self.speech startSpeakingString:saveSpeaking];
        [self.speechInputWindow orderOut:self];
        [self.avView.player play];
        [self.speechInputField setStringValue:@""];
        [self.speechStartSecondsField setStringValue:@""];
        [self.speechPauseSecondsField setStringValue:@""];
    }else{
        NSString *saveSpeaking = [self.speechInputField stringValue];
        float latestTime = CMTimeGetSeconds(self.avView.player.currentTime);
        NSDate *date = [[NSDate alloc] initWithTimeIntervalSince1970:latestTime];
        NSDateFormatter *dateFormatter = [[NSDateFormatter alloc] init];
        [dateFormatter setTimeZone:[NSTimeZone timeZoneWithName:@"UTC"]];
        [dateFormatter setDateFormat:@"HH:mm:ss"];
        NSString *result = [dateFormatter stringFromDate:date];
        result = [result stringByAppendingString:@"\n\t"];
        speaking = [speaking stringByAppendingString:@"\n"];
        [self.textView insertText:result];
        [self.textView insertText:speaking];
        [self.speech startSpeakingString:saveSpeaking];
        [self.speechInputWindow orderOut:self];
        [self.avView.player play];
        [self.speechInputField setStringValue:@""];
        [self.speechStartSecondsField setStringValue:@""];
        [self.speechPauseSecondsField setStringValue:@""];
    }
    
}

- (void)alertErrorInputDidEnd:(NSAlert *)alert returnCode:(NSInteger)returnCode contextInfo:(void *)contextInfo {
    if(returnCode == NSAlertDefaultReturn){
        return;
    }
}

- (IBAction)onSpeakingCancelButton:(id)sender {
    [self.speechInputWindow orderOut:self];
    [self.avView.player play];
    [self.speechInputField setStringValue:@""];
    [self.speechStartSecondsField setStringValue:@""];
    [self.speechPauseSecondsField setStringValue:@""];
}

- (IBAction)onTimeJump:(id)sender {
    [self.avView.player pause];
    [self.jumpWindow makeKeyAndOrderFront:self];
}

- (IBAction)onTimeGoJump:(id)sender {
    NSInteger hours = [self.HoursField intValue];
    NSInteger minutes = [self.MinutesField intValue];
    NSInteger seconds = [self.SecondsField intValue];
    NSInteger sumTime = hours * 60 * 60 + minutes * 60 + seconds;
    CMTime time = CMTimeMake(sumTime * 60, 60);
    self.duration = time;
    [self.avView.player seekToTime:self.duration toleranceBefore:kCMTimeZero toleranceAfter:kCMTimeZero];
    [self.avView.player play];
    [self.HoursField setStringValue:@""];
    [self.MinutesField setStringValue:@""];
    [self.SecondsField setStringValue:@""];
}

- (IBAction)onTimeJumpCancel:(id)sender {
    [self.jumpWindow orderOut:self];
    [self.avView.player play];
    [self.HoursField setStringValue:@""];
    [self.MinutesField setStringValue:@""];
    [self.SecondsField setStringValue:@""];
}

- (IBAction)onMovieWindow:(id)sender {
    [self.window makeKeyAndOrderFront:self];
}

- (IBAction)onScriptWindow:(id)sender {
    [self.textViewWindow makeKeyAndOrderFront:self];
}

- (IBAction)onMovieFullScreen:(id)sender {
    [self.window toggleFullScreen:self];
}

- (IBAction)onSaveFile:(id)sender {
    NSFileManager *fileManager = [NSFileManager defaultManager];
    
    if(![fileManager fileExistsAtPath:[self.saveFilePath path]] || [self.saveFilePath.path length] == 0){
        NSSavePanel *savePanel = [NSSavePanel savePanel];
        NSArray *allowedFileTypes = [NSArray arrayWithObjects:@"txt",@"'TEXT'",nil];
        [savePanel setAllowedFileTypes:allowedFileTypes];
        NSInteger pressedButton = [savePanel runModal];
        
        if(pressedButton == NSOKButton){
            NSURL *filePath = [savePanel URL];
            
            self.saveFilePath = filePath;
            [self.textView.string writeToFile:[filePath path] atomically:YES encoding:NSUTF8StringEncoding error:nil];
            NSInteger targetTitleLength = [self.textViewWindow.title length];
            NSString *targetTitle = [self.textViewWindow.title substringToIndex:(targetTitleLength - 1)];
            [self.textViewWindow setTitle:targetTitle];
            self.saveFlag = NO;
        }
        
    }else{
        NSData *targetData = [[NSData alloc] initWithContentsOfFile:[self.saveFilePath path]];
        
        [self.textView.string writeToFile:[self.saveFilePath path] atomically:YES encoding:[self detectEncoding:targetData] error:nil];
        NSInteger targetTitleLength = [self.textViewWindow.title length];
        NSString *targetTitle = [self.textViewWindow.title substringToIndex:(targetTitleLength - 1)];
        [self.textViewWindow setTitle:targetTitle];
        self.saveFlag = NO;
    }
}

- (IBAction)onSaveAsFile:(id)sender {
    NSSavePanel *savePanel  = [NSSavePanel savePanel];
    NSArray *allowedFileTypes = [NSArray arrayWithObjects:@"txt",@"'TEXT'",nil];
    [savePanel setAllowedFileTypes:allowedFileTypes];
    NSInteger pressedButton = [savePanel runModal];
    
    if(pressedButton == NSOKButton){
        NSURL *filePath = [savePanel URL];
        
        self.saveFilePath = filePath;
        [self.textView.string writeToFile:[filePath path] atomically:YES encoding:NSUTF8StringEncoding error:nil];
        NSInteger targetTitleLength = [self.textViewWindow.title length];
        NSString *targetTitle = [self.textViewWindow.title substringToIndex:(targetTitleLength - 1)];
        [self.textViewWindow setTitle:targetTitle];
        self.saveFlag = NO;
    }
}

- (IBAction)onFixSpeechMenu:(id)sender {
    [self.avView.player pause];
    [self.speechInputFixWindow makeKeyAndOrderFront:self];
}

- (IBAction)onSpeechFixButton:(id)sender {
    
}

- (IBAction)onSpeechFixCancel:(id)sender {

}

- (IBAction)onNewDocument:(id)sender {
    self.nilPath = [NSURL URLWithString:@""];
    
    if(self.saveFlag == YES){
        NSAlert *alert = [NSAlert alertWithMessageText:@"ファイル未保存"
                                         defaultButton:@"保存"
                                       alternateButton:@"キャンセル"
                                           otherButton:nil
                             informativeTextWithFormat:@"ファイルを保存していません。保存しますか?"];
        
        [alert beginSheetModalForWindow:self.textViewWindow
                          modalDelegate:self
                         didEndSelector:@selector(alertNewDocumentSaveDidEnd:returnCode:contextInfo:)
                            contextInfo:nil];
        
    }else{
        [self.textViewWindow makeKeyAndOrderFront:self];
        self.saveFilePath = self.nilPath;
        [self.textView setString:@""];
    }
}

- (void)alertNewDocumentSaveDidEnd:(NSAlert *)alert returnCode:(NSInteger)returnCode contextInfo:(void *)contextInfo {
    if(returnCode == NSAlertDefaultReturn){
        NSFileManager *fileManager = [NSFileManager defaultManager];
        
        if(![fileManager fileExistsAtPath:[self.saveFilePath path]] || [self.saveFilePath.path length] == 0){
            NSSavePanel *savePanel = [NSSavePanel savePanel];
            NSArray *allowedFileTypes = [NSArray arrayWithObjects:@"txt",@"'TEXT'",nil];
            [savePanel setAllowedFileTypes:allowedFileTypes];
            NSInteger pressedButton = [savePanel runModal];
            
            if(pressedButton == NSOKButton){
                NSURL *filePath = [savePanel URL];
                
                self.saveFilePath = filePath;
                [self.textView.string writeToFile:[filePath path] atomically:YES encoding:NSUTF8StringEncoding error:nil];
                NSInteger targetTitleLength = [self.textViewWindow.title length];
                NSString *targetTitle = [self.textViewWindow.title substringToIndex:(targetTitleLength - 1)];
                [self.textViewWindow setTitle:targetTitle];
                self.saveFlag = NO;
                [self.textViewWindow makeKeyAndOrderFront:self];
                self.saveFilePath = self.nilPath;
                [self.textView setString:@""];
            }
            
        }else{
            NSData *targetData = [[NSData alloc] initWithContentsOfFile:[self.saveFilePath path]];
            
            [self.textView.string writeToFile:[self.saveFilePath path] atomically:YES encoding:[self detectEncoding:targetData] error:nil];
            NSInteger targetTitleLength = [self.textViewWindow.title length];
            NSString *targetTitle = [self.textViewWindow.title substringToIndex:(targetTitleLength - 1)];
            [self.textViewWindow setTitle:targetTitle];
            self.saveFlag = NO;
            [self.textViewWindow makeKeyAndOrderFront:self];
            self.saveFilePath = self.nilPath;
            [self.textView setString:@""];
        }
    }
}

- (BOOL)applicationShouldHandleReopen:(NSApplication *)theApplication hasVisibleWindows:(BOOL)flag {
    if(flag){
        return NO;
    }else{
        [self.window orderFront:self];
        return YES;
    }
}

- (void)textDidChange:(NSNotification *)notification{
    if(self.saveFlag == NO){
        [self.textViewWindow setTitle:[self.textViewWindow.title stringByAppendingString:@"*"]];
        self.saveFlag = YES;
    }
}

@end

さて、新しい制作物に取り掛かろうかな。