Removed old commented-out code.
[CleverRabbit.git] / RTKTigerDocument.m
blob11ec90d002d08ca7977ef7aa155bf14ec0558614
1     //
2 //   RTKTigerDocument.m
3 //   (CleverRabbit.app)
4 //
5 //   Copyright (c) 2005 A. Karl Keller (http://karlk.net)
6 //
7 //   This code is open-source, free software, made available without warranty under
8 //   the terms of the GNU General Public License, either version 2 or later (see 
9 //   http://www.gnu.org/licenses/gpl.html or included copy); as such, it may be 
10 //   redistributed and/or modified in accordance with that document.
13 #import "RTKTigerDocument.h"
14 #import "RTKCleverRabbitController.h"
15 #import "RTKArrayCategory.h"
16 #import "RTKMutableArrayCategory.h"
17 #import "RTKStringCategory.h"
18 #import "RTKMutableAttributedStringCategory.h"
19 #import "RTKTigerTextView.h"
21 #import "Chomp.h"
23 #define RTKNOROWSELECTED -1
25 // This is likely temporary, a nasty relic of the project
26 // that the transcription code was borrowed from
27 id RTKSharedConvertor = nil;
28 id RTKSharedDatabase = nil;
29 id RTKClass = @"RTKClass";
31 // Test flag
32 //BOOL generateMetaStrings = YES;
33 BOOL generateMetaStrings = NO;
35 @implementation RTKTigerDocument
37 - (id)init
39     if(self = [super init]) {
40         NSNotificationCenter * nc = [NSNotificationCenter defaultCenter];
41         
42         [nc addObserver:self
43                selector:@selector(plainTextDelimiterChanged:)
44                    name:@"RTKPlainTextDelimiterChanged"
45                  object:nil];
46         
47         [nc addObserver:self
48                selector:@selector(definitionsChanged:)
49                    name:@"RTKDefinitionsChanged"
50                  object:nil];
51         
52         [nc addObserver:self
53                selector:@selector(fontsChanged:)
54                    name:@"RTKFontsChanged"
55                  object:nil];
56         
57         [nc addObserver:self
58                selector:@selector(transcriptionTypeChanged:)
59                    name:@"RTKTranscriptionTypeChanged"
60                  object:nil];
61                 
62         book = [[RTKBook alloc] init];
63         
64         revisionsToConvert = [[NSMutableArray alloc] init];
65         
66         revisionsToConvertLock = [[NSLock alloc] init];
67         
68         convertingLock = [[NSLock alloc] init];
69         [convertingLock lock];
70         
71         // RTKSharedDatabase and RTKSharedConvertor are global for now.
72         // They may not always be global, so don't depend on them.
73         if(!RTKSharedConvertor)
74             RTKSharedConvertor = [[RTKConvertor alloc] init];
75         if(!RTKSharedDatabase)
76             RTKSharedDatabase = [RTKSharedConvertor generalDatabase];
77         
78         [self setCreationDate:[NSDate dateWithTimeIntervalSinceNow:0.0]];
79         
80         [self setBindingsFromDictionary:nil];
81         
82         [self setVerseTypes:[NSMutableArray arrayWithObjects:
83                              @"\\v", @"\\p", @"\\c", @"\\s1", @"\\s2", @"\\r", @"\\mt1", @"\\mt2", @"\\mt3", @"\\is", @"\\ip", @"\\h", nil]];
84         [self setDictionary:[NSDictionary dictionary]];
85                 
86                 alreadyAwokeFromNib = NO;
87     }
88     return self;
91 - (void)dealloc
93     [book release];
94     [draggedVerseIndexArray release];
95     [visibleVerseIndexes release];
96     [revisionsToConvert release];
97     [verseTypes release];
98     [inputDefinitionPath release];
99     [scriptDefinitionPath release];
100     [encodingDefinitionPath release];
101     [convertingLock release];
102     [revisionsToConvertLock release];
103     [creationDate release];
104     [dictionary release];
105     
106     [super dealloc];
109 - (void)awakeFromNib
110 {    
111         if(!alreadyAwokeFromNib) {
112                 // Keep tabs on window state so as to avoid using UI objects after window has closed.
113                 windowIsOpen = YES;
114                 
115                 // Register for dragging.
116                 [versesTableView registerForDraggedTypes: [NSArray arrayWithObjects: @"RTKVersesInternalToBook", nil]];
117                 
118                 [versesTableView setTarget:self];
119                 //[versesTableView setDoubleAction:@selector(tableViewDoubleClicked)];
120                 
121                 [versesTableView setVerticalMotionCanBeginDrag:NO];
122                 
123         NSUserDefaults * d = [NSUserDefaults standardUserDefaults];
124         
125                 if(![d boolForKey:@"RTKTransliterationOn"]) {
126             
127             [scriptTableColumn setHidden:YES];
128              
129             [scriptView retain];
130                         [scriptView removeFromSuperview];
131             
132             [scriptPublishedView retain];
133             [scriptPublishedView removeFromSuperview];
134                 }
135         
136         [d addObserver:self
137             forKeyPath:@"RTKTransliterationOn"
138                options:NSKeyValueObservingOptionNew
139                context:NULL];
140         
141         [d addObserver:self
142             forKeyPath:@"RTKZVXSubstitution"
143                options:NSKeyValueObservingOptionNew
144                context:NULL];
145         
146                 
147                 [self readSplitViewRectsFromDefaults];
148                 
149                 
150                 [self ensureOneBlankVerse];
151                 
152                 [self search:searchField];
153                 [documentWindow makeFirstResponder:romanTextView];
154                 
155                 [self setDictionary:[NSDictionary dictionary]];
156         
157                 [self updateUI];
158         
159         [[romanPublishedTextView textStorage] setAttributedString:[book mutableAttributedString:YES]];  // YES = roman, NO = script
160         
161         [[scriptPublishedTextView textStorage] setAttributedString:[book mutableAttributedString:NO]];  // YES = roman, NO = script
163                 
164         [self selectVerse:[[book verses] objectAtIndex:0]];
165                 
166         [scriptTextView setAllowEditing:NO];
167         
168                 alreadyAwokeFromNib = YES;
169         }
170     [super awakeFromNib];
173 #pragma mark -
175 - (BOOL)keepBackupFile
177     return NO;
181 #pragma mark -
182 #pragma mark search
185  http://developer.apple.com/documentation/Cocoa/Conceptual/SearchFields/index.html
186  */
188 - (IBAction)search:(id)sender
190     NSString * searchString = [sender stringValue];
191     NSMutableArray * indexes = [NSMutableArray array];
192     [self setVisibleVerseIndexes:indexes];
193     
194     int i = 0;
195     if([searchString length]) {
196         NSEnumerator * e = [[book verses] objectEnumerator];
197         RTKVerse * verse;
198         while(verse = (RTKVerse *)[e nextObject]) {
199             if([verse matchesString:searchString])
200                 [indexes addObject:[NSNumber numberWithInt: i]];
201             i++;
202         }
203     } else {
204         int count = [[book verses] count];
205         for(i = 0; i < count; i++)
206             [indexes addObject:[NSNumber numberWithInt: i]];
207     }
208     [self updateUI];
209     [versesTableView noteNumberOfRowsChanged];
212 - (NSArray *)visibleVerseIndexes
214     return visibleVerseIndexes;
217 - (void)setVisibleVerseIndexes:(NSArray *)indexes
219     [indexes retain];
220     [visibleVerseIndexes release];
221     visibleVerseIndexes = indexes;
224 #pragma mark -
225 #pragma mark copy and paste
227  http://homepage.mac.com/svc/cocoa-objc-mac-os-x/
228  http://www.knuddel.org/Projects/KoKit/Documentation/Gui/Reference/NSPasteboard.html
229  http://developer.apple.com/documentation/Cocoa/Conceptual/CopyandPaste/index.html
230  */
232 - (void)cut:(id)sender
234     NSIndexSet * selectedIndexes = [versesTableView selectedRowIndexes];
235     [self copy:sender];
236     [book setVerses:(NSMutableArray *) [[book verses] arrayByRemovingObjectsAtIndexes:selectedIndexes]];
237     [self ensureOneBlankVerse];
238     
239     [self search:searchField];
240     [versesTableView noteNumberOfRowsChanged];
241     int firstIndex = [selectedIndexes firstIndex];
242     [versesTableView selectRow:(firstIndex > 0 ? firstIndex - 1 : 0) byExtendingSelection:NO];
243     [self updateUI];
246 - (void)copy:(id)sender
248     NSPasteboard *pasteboard = [NSPasteboard generalPasteboard];
249     [pasteboard declareTypes:[NSArray arrayWithObject:NSStringPboardType] owner:nil];
250         
251     NSIndexSet * selectedRowIndexes = [versesTableView selectedRowIndexes];
252     NSMutableArray * verses = [book verses];
253     NSMutableArray * selectedVerses = [verses arrayWithObjectsAtIndexes:selectedRowIndexes];
254     
255     RTKBook * newBook = [RTKBook bookWithVerses:selectedVerses];
256     NSString * contents = [newBook string];
257     [pasteboard setString:contents forType:NSStringPboardType];
258         
259         
260     RTKCleverRabbitController * appController = [NSApp delegate];
261     [appController setCopiedVersesArray:[selectedVerses deepCopy]];
262     
263     [pasteboard setData:nil forType:@"RTKBook"];
266 - (void)paste:(id)sender
268     NSPasteboard *pasteboard = [NSPasteboard generalPasteboard];
269     NSString *type = [pasteboard availableTypeFromArray:[NSArray arrayWithObject:@"RTKBook"]];
270     if (type != nil) {
271         NSUInteger lastIndex = [[versesTableView selectedRowIndexes] lastIndex];
272         if(lastIndex == NSNotFound)
273             lastIndex = [[book verses] count];
274         else
275             lastIndex++;
276         
277         RTKCleverRabbitController * appController = [NSApp delegate];
278         NSArray * pastedVerses = [[appController copiedVersesArray] deepCopy];
279         
280         [[book verses] replaceObjectsInRange:NSMakeRange(lastIndex,0)
281                         withObjectsFromArray:pastedVerses];
282                 
283         [self ensureOneBlankVerse];
284         [self search:searchField];
285         [versesTableView noteNumberOfRowsChanged];
286         [self updateUI];
287         
288         [versesTableView selectRowIndexes:[NSIndexSet indexSetWithIndexesInRange:NSMakeRange(lastIndex, [pastedVerses count])] byExtendingSelection:NO];
289     }
292 #pragma mark -
293 #pragma mark drag and drop
294 /* Some very handy links.
295  http://www.nongnu.org/gstutorial/en/ch13s04.html
296  http://borkware.com/quickies/one?topic=NSTableView
297  http://borkware.com/quickies/everything-by-date
298  */
300 - (void)setDraggedVerseIndexArray:(NSArray *)indexArray
302     [indexArray retain];
303     [draggedVerseIndexArray release];
304     draggedVerseIndexArray = indexArray;
307 - (NSArray *)draggedVerseIndexArray
309     return draggedVerseIndexArray;
312 - (BOOL)tableView: (NSTableView *)aTableView
313         writeRows: (NSArray *)rows
314      toPasteboard: (NSPasteboard *)pboard
316     [pboard declareTypes: [NSArray arrayWithObjects: @"RTKVersesInternalToBook", nil] owner: self];
317     
318     // Ok, so we aren't putting an NSData object on the pasteboard.
319     
320     // That's ok because this is easier to write and doesn't involve a lot of messing about
321     // with converting the data into different types when we just want to reorder a few verses
322     // or copy verses between documents. 
323     
324     // Interapplication drag and drop will have to be handled in
325     // a more standard data-copying fashion.
326     
327     // NSLog(@"dragging");
328     // For same document...
329     [self setDraggedVerseIndexArray:rows];
330     
331     // For between documents...
332     RTKCleverRabbitController * appController = [NSApp delegate];
333     [appController setDraggedVersesArray:[[book verses] arrayWithObjectsAtIndexes:rows]];
334     [appController setDraggedVersesOwner:self];
335     
336     return YES;
339 - (NSDragOperation)tableView: (NSTableView *)aTableView
340                 validateDrop: (id <NSDraggingInfo>)item
341                  proposedRow: (int)row
342        proposedDropOperation: (NSTableViewDropOperation)op
343 {       /* Example found at http://www.nongnu.org/gstutorial/en/ch13s04.html */
344     
345     //NSLog(@"checking drop zone");
346     
347         if(row > [[book verses] count])
348         return NSDragOperationNone;
349     
350         if([item draggingSource] == nil) {      // dragging from other application
351                 return NSDragOperationNone;
352         } else if([item draggingSource] == versesTableView) {   // dragging within document
353                 [versesTableView setDropRow:row dropOperation:NSTableViewDropAbove];
354                 return NSDragOperationGeneric;
355         } else {        // dragging between documents
356                 [versesTableView setDropRow:row dropOperation:NSTableViewDropAbove];
357                 return NSDragOperationCopy;
358         }
359         return NSDragOperationNone;
362 - (BOOL)tableView:(NSTableView*)aTableView
363        acceptDrop: (id <NSDraggingInfo>)item
364               row: (int)row
365     dropOperation:(NSTableViewDropOperation)op
367     NSPasteboard *pboard = [item draggingPasteboard];
368         RTKCleverRabbitController * appController = [NSApp delegate];
369     
370     if ([pboard availableTypeFromArray:[NSArray arrayWithObject: @"RTKVersesInternalToBook"]]) {
371         if([appController draggedVersesOwner] == self) {
372             NSMutableArray * verses = [book verses];
373             NSMutableArray * draggedVerses = [verses arrayWithObjectsAtIndexes:draggedVerseIndexArray];
374             
375             int verseIndex = 0;
376             if(row > 0)
377                 verseIndex = [[visibleVerseIndexes objectAtIndex:row - 1] intValue] + 1;
378             
379             [[verses doSelf] insertObject:[draggedVerses reverseObjectEnumerator]
380                                   atIndex:verseIndex];
381             
382             int verseCount = [draggedVerseIndexArray count];
383             
384             NSEnumerator * e = [draggedVerseIndexArray reverseObjectEnumerator];
385             NSNumber * n;
386             int selectionPointCorrection = 0;
387             while(n = [e nextObject]) {
388                 int i = [n intValue];
389                 [verses removeObjectAtIndex:(i < row ? i : i + verseCount)];
390                 
391                 if(i < row)
392                     selectionPointCorrection++;
393             }
394                         // TODO: check if not needed
395             [versesTableView reloadData];
396             
397             [versesTableView selectRowIndexes:[NSIndexSet indexSetWithIndexesInRange:
398                                                NSMakeRange((row - selectionPointCorrection), verseCount)]
399                          byExtendingSelection:NO];
400         } else {
401             NSMutableArray * verses = [book verses];
402             NSArray * draggedVerses = [appController draggedVersesArray];
403             
404             int verseIndex = 0;
405             if(row > 0)
406                 verseIndex = [[visibleVerseIndexes objectAtIndex:row - 1] intValue] + 1;
407                         
408             [[verses doSelf] insertObject:[draggedVerses reverseObjectEnumerator]
409                                   atIndex:verseIndex];
410             
411             int verseCount = [draggedVerses count];
412             
413             // TODO: check if not needed
414             [versesTableView reloadData];
415             
416             [versesTableView selectRowIndexes:[NSIndexSet indexSetWithIndexesInRange:
417                                                NSMakeRange(row, verseCount)]
418                          byExtendingSelection:NO];            
419         }
420         // TODO: Change this when undo/redo is supported
421         [self updateChangeCount:NSChangeDone];
422     }
423     [self ensureOneBlankVerse];
424     [searchField setStringValue:@""];
425     [self search:searchField];
426     [versesTableView noteNumberOfRowsChanged];
427     return YES;
430 #pragma mark -
432 // Notification Handlers
433 - (void)substituteZVXChanged
435     if([[NSUserDefaults standardUserDefaults] boolForKey:@"RTKZVXSubstitution"]) {
436         NSMutableDictionary * characterSwapDictionary = [NSMutableDictionary dictionary];
437         [characterSwapDictionary setObject:[NSNumber numberWithInt:0xe0] forKey:@"z"];
438         [characterSwapDictionary setObject:[NSNumber numberWithInt:0xf9] forKey:@"v"];
439         [characterSwapDictionary setObject:[NSNumber numberWithInt:0xe8] forKey:@"x"];
440         [romanTextView setCharacterSwaps:characterSwapDictionary];
441         [romanPublishedTextView setCharacterSwaps:characterSwapDictionary];
442     } else {
443         [romanTextView setCharacterSwaps:nil];
444         [romanPublishedTextView setCharacterSwaps:nil];
446     }
449 - (void)definitionsChanged:(id)dummy
451         if([[NSUserDefaults standardUserDefaults] boolForKey:@"RTKTransliterationOn"])
452                 [self regenerateAllScript];
455 - (void)fontsChanged:(id)dummy
457     [self updateUI];
460 - (NSString *)windowNibName
462     return @"RTKTiger";
465 - (void)windowControllerDidLoadNib:(NSWindowController *) aController
467     [super windowControllerDidLoadNib:aController];
468     [aController setShouldCloseDocument:YES];
469     [self setFieldEditor:NO];
470     [self updateUI];
471     [self substituteZVXChanged];
474 - (void)setFieldEditor:(BOOL)editor
476     [romanTextView setFieldEditor:editor];
477     [scriptTextView setFieldEditor:editor];
478     [backTranslationTextView setFieldEditor:editor];
479     [notesTextView setFieldEditor:editor];
480     [checkingTextView setFieldEditor:editor];
483 #pragma mark -
484 #pragma mark save and open
486 - (NSData *)dataOfType:(NSString *)typeName
487                  error:(NSError **)outError
489     NSData * data = nil;
490         
491     [self writeSplitViewRectsToDefaults];
492         
493     if([typeName isEqualToString:@"rtktiger"])
494          {
495          NSMutableDictionary * dict = [NSMutableDictionary dictionaryWithDictionary:dictionary];
496          
497          [dict setObject:[book dictionaryRepresentation] forKey:@"book"];
498          
499          [dict setObject:@"Keys prefixed with RTK are specific to the document in CleverRabbit.app. \n Those not prefixed store actual data."
500                   forKey:@"ANoteForPosterity"];
501          
502          [dict setObject:[[NSDate date] description] forKey:@"RTKSaveDate"];
503          [dict setObject:[creationDate description] forKey:@"RTKCreationDate"];
504          
505          // TODO: Should probably be using a better version format.
506          [dict setObject:[[NSBundle mainBundle] objectForInfoDictionaryKey:@"CFBundleVersion"]
507                   forKey:@"RTKBuildVersion"];
508          
509          [dict setObject:RTKDocumentWidth forKey:@"RTKDocumentWidth"];
510          [dict setObject:RTKDocumentHeight forKey:@"RTKDocumentHeight"];
511          
512          [dict setObject:RTKReferenceColumnWidth forKey:@"RTKReferenceColumnWidth"];
513          [dict setObject:RTKRevisionColumnWidth forKey:@"RTKRevisionColumnWidth"];
514          [dict setObject:RTKScriptColumnWidth forKey:@"RTKScriptColumnWidth"];
515          [dict setObject:RTKRomanColumnWidth forKey:@"RTKRomanColumnWidth"];
516          [dict setObject:RTKBackTranslationColumnWidth forKey:@"RTKBackTranslationColumnWidth"];
517          [dict setObject:RTKNotesColumnWidth forKey:@"RTKNotesColumnWidth"];
518          [dict setObject:RTKCheckingColumnWidth forKey:@"RTKCheckingColumnWidth"];
519          
520          NSUserDefaults * d = [NSUserDefaults standardUserDefaults];
521          
522          [d setObject:RTKDocumentWidth forKey:@"RTKDocumentWidth"];
523          [d setObject:RTKDocumentHeight forKey:@"RTKDocumentHeight"];
524          
525          [d setObject:RTKReferenceColumnWidth forKey:@"RTKReferenceColumnWidth"];
526          [d setObject:RTKRevisionColumnWidth forKey:@"RTKRevisionColumnWidth"];
527          [d setObject:RTKScriptColumnWidth forKey:@"RTKScriptColumnWidth"];
528          [d setObject:RTKRomanColumnWidth forKey:@"RTKRomanColumnWidth"];
529          [d setObject:RTKBackTranslationColumnWidth forKey:@"RTKBackTranslationColumnWidth"];
530          [d setObject:RTKNotesColumnWidth forKey:@"RTKNotesColumnWidth"];
531          [d setObject:RTKCheckingColumnWidth forKey:@"RTKCheckingColumnWidth"];
532          
533          
534          
535          data = [[dict description] dataUsingEncoding:NSUTF8StringEncoding];
536          } else if([typeName isEqualToString:@"txt"]) {
537                  
538                  NSString * string = [book string];
539                  
540                  if(!book)
541                          NSLog(@"nil book");
542                  if(!string)
543                          NSLog(@"nil string");
544                  
545                  data = [string utf8Data];
546                  
547                  if(!data)
548                          NSLog(@"nil data from outputData");
549          } else if([typeName isEqualToString:@"ptx"]) {
550                  
551                  NSString * string = [book sfmString];
552                  
553                  if(!book)
554                          NSLog(@"nil sfm book");
555                  if(!string)
556                          NSLog(@"nil sfm string");
557                  
558                  data = [string utf8Data];
559                  
560                  if(!data)
561                          NSLog(@"nil data from [string utf8Data]");
562          }
563     
564     if(data) {
565         // TODO: Change this when undo/redo is supported
566         [self updateChangeCount:NSChangeCleared];
567     }
568     return data;
571 - (NSRect) frame {
572         NSRect frame;
573         frame.size.width = frame.size.height = frame.origin.x = frame.origin.y = 0;
574         
575         NSLog(@"Don't call -frame on RTKTigerDocument!");
576         return frame;
579 // Used below in setting default or document specific values for bindings.
580 // Sets the binding value from dict if possible. 
581 // If not defined in dict, sets from standardUserDefaults.
582 - (void)setValueForKey:(id)key
583         fromDictionary:(id)dict
585     NSUserDefaults * d = [NSUserDefaults standardUserDefaults];
586     
587     id object = [dict objectForKey:key];
588     
589     if(object)
590         // Read the setting from the document if possible.
591         [self setValue:object forKey:key];
592     else
593         // If setting not present in the document, set it from the application defaults.
594         [self setValue:[d valueForKey:key] forKey:key];
597 - (void)setBindingsFromDictionary:(NSDictionary *)dict
599     [self setValueForKey:@"RTKDocumentWidth" fromDictionary:dict];
600     [self setValueForKey:@"RTKDocumentHeight" fromDictionary:dict];
602     [self setValueForKey:@"RTKReferenceColumnWidth" fromDictionary:dict];
603     [self setValueForKey:@"RTKRevisionColumnWidth" fromDictionary:dict];
604     [self setValueForKey:@"RTKScriptColumnWidth" fromDictionary:dict];
605     [self setValueForKey:@"RTKRomanColumnWidth" fromDictionary:dict];
606     [self setValueForKey:@"RTKBackTranslationColumnWidth" fromDictionary:dict];
607     [self setValueForKey:@"RTKNotesColumnWidth" fromDictionary:dict];
608     [self setValueForKey:@"RTKCheckingColumnWidth" fromDictionary:dict];
613  loadDataRepresentation:ofType: is called when a file is opened, but not when a new document is created. 
614  Given data and a type string, it creates the necessary model objects to represent the document. 
615  If called at all, it is called before awakeFromNib.
616  */
617 - (BOOL)loadDataRepresentation:(NSData *)data 
618                         ofType:(NSString *)aType
620     BOOL loaded = NO;
621     
622     if([aType isEqualToString:@"rtktiger"]) {
623         NSDictionary * dict = [[[NSString allocWithZone:[self zone]] initWithData:data 
624                                                                          encoding:NSUTF8StringEncoding] propertyList];
625         [self setDictionary:dict];
626         
627         NSDictionary * bookDict = [dict objectForKey:@"book"];
628         
629         [self setBook:[[[RTKBook alloc] initWithDictionary:bookDict] autorelease]];
630         [self setCreationDate:[NSDate dateWithString:[dict objectForKey:@"RTKCreationDate"]]];
631         
632                 // Check for old version of Type field text and update if necessary to USFM format.
633                 NSString *fileBuildVersion = [dict objectForKey:@"RTKBuildVersion"];
634                 NSString *minimumRequiredBuildVersion = @"0.2006.02.23";
635                 if([fileBuildVersion compare:minimumRequiredBuildVersion] == NSOrderedAscending) {
636                         NSLog(@"UPDATING: Old build version %@ less than minimum %@",
637                                   fileBuildVersion, minimumRequiredBuildVersion);
638                         [[[book verses] do] updateTypeFieldToUSFM];
639                 }
640                 
641                 // TODO: check that width and height are within screen size
642         [self setBindingsFromDictionary:dict];
643         
644         loaded = YES;
645     } else if([aType isEqualToString:@"txt"]) {
646         [self setBook:[[[RTKBook alloc] initWithString:
647                         [[NSMutableString allocWithZone:[self zone]] initWithData:data 
648                                                                          encoding:NSUTF8StringEncoding]] autorelease]];
649         loaded = YES;
650     } else if([aType isEqualToString:@"ptx"]) {
651         [self setBook:[[[RTKBook alloc] initWithSFMString:
652                         [[NSMutableString allocWithZone:[self zone]] initWithData:data 
653                                                                          encoding:NSUTF8StringEncoding]] autorelease]];
654         loaded = YES;
655     }
656     
657     // TODO: check if not needed
658     [versesTableView reloadData];
659     if([versesTableView numberOfRows] > 0)
660         [versesTableView selectRow:0 byExtendingSelection:NO];
661         
662     if([[NSUserDefaults standardUserDefaults] boolForKey:@"RTKTransliterationOn"])
663                 [self regenerateAllScript];
664     
665     return loaded;
668 #pragma mark -
669 #pragma mark accessors
671 - (void)setDictionary:(NSDictionary *)theDictionary
673     [theDictionary retain];
674     [dictionary release];
675     dictionary = theDictionary;
678 - (NSDictionary *)dictionary
680     return dictionary;
683 - (RTKBook *)book
685     return book;
688 - (void)setBook:(RTKBook *)theBook
690     [theBook retain];
691     [book release];
692     book = theBook;
695 - (RTKVerse *)currentVerse
697     return currentVerse;
700 - (void)setCurrentVerse:(RTKVerse *)verse
702     [verse retain];
703     [currentVerse release];
704     currentVerse = verse;
705     
706     [book setCurrentVerse: currentVerse];
709 - (RTKRevision *)currentRevision
711     return currentRevision;
714 - (void)setCurrentRevision:(RTKRevision *)revision
716     [revision retain];
717     [currentRevision release];
718     currentRevision = revision;
719     
720     [currentVerse setCurrentRevision:revision];
723 - (NSMutableArray *)verseTypes
725     return verseTypes;
728 - (void)setVerseTypes:(NSMutableArray *)theVerseTypes
730     [theVerseTypes retain];
731     [verseTypes release];
732     verseTypes = theVerseTypes;
735 - (NSDate *)creationDate
737     return creationDate;
740 - (void)setCreationDate:(NSDate *)newCreationDate
742     // Don't want to lose the current creation date if none passed in.
743     if(! newCreationDate) 
744         return;
745     
746     [newCreationDate retain];
747     [creationDate release];
748     creationDate = newCreationDate;
751 #pragma mark - UI
753 - (void)updateUI
755     [self updateFonts];
756     [self updateMenusAndButtons];
759 - (void)updateFonts
761     NSUserDefaults * d = [NSUserDefaults standardUserDefaults];
762     NSFont * font;
763     
764     font = [NSFont fontWithName:(NSString *)[d valueForKey:@"RTKScriptFontName"]
765                            size:[(NSString *) [d valueForKey:@"RTKScriptFontSize"] floatValue]];
766     if(font) [scriptTextView setFont:font];
767     
768     font = [NSFont fontWithName:(NSString *)[d valueForKey:@"RTKRomanFontName"]
769                            size:[(NSString *) [d valueForKey:@"RTKRomanFontSize"] floatValue]];
770     if(font) [romanTextView setFont:font];
771     
772     font = [NSFont fontWithName:(NSString *)[d valueForKey:@"RTKBackTranslationFontName"]
773                            size:[(NSString *) [d valueForKey:@"RTKBackTranslationFontSize"] floatValue]];
774     if(font) [backTranslationTextView setFont:font];
775     
776     font = [NSFont fontWithName:(NSString *)[d valueForKey:@"RTKNotesFontName"]
777                            size:[(NSString *) [d valueForKey:@"RTKNotesFontSize"] floatValue]];
778     if(font) [notesTextView setFont:font];
779     
780     font = [NSFont fontWithName:(NSString *)[d valueForKey:@"RTKCheckingFontName"]
781                            size:[(NSString *) [d valueForKey:@"RTKCheckingFontSize"] floatValue]];
782     if(font) [checkingTextView setFont:font];
783     
784     font = [NSFont fontWithName:(NSString *)[d valueForKey:@"RTKScriptFontName"]
785                            size:12];
786     if(font) [[scriptTableColumn dataCell] setFont:font];
787     
788     font = [NSFont fontWithName:(NSString *)[d valueForKey:@"RTKRomanFontName"]
789                            size:12];
790     if(font) [[romanTableColumn dataCell] setFont:font];
791     
792     font = [NSFont fontWithName:(NSString *)[d valueForKey:@"RTKBackTranslationFontName"]
793                            size:12];
794     if(font) [[backTranslationTableColumn dataCell] setFont:font];
795     
796     font = [NSFont fontWithName:(NSString *)[d valueForKey:@"RTKNotesFontName"]
797                            size:12];
798     if(font) [[notesTableColumn dataCell] setFont:font];
799     
800     font = [NSFont fontWithName:(NSString *)[d valueForKey:@"RTKCheckingFontName"]
801                            size:12];
802     if(font) [[checkingTableColumn dataCell] setFont:font];
805 - (void) updateMenusAndButtons
807     NSUInteger selectedRow;
808     NSIndexSet * selectedRows = [versesTableView selectedRowIndexes];
809     RTKCleverRabbitController * appController = [NSApp delegate];
810     NSUserDefaults * d = [NSUserDefaults standardUserDefaults];
811     
812     if([selectedRows count] > 1) {
813         selectedRow = -1;
814     } else {
815         selectedRow = [selectedRows firstIndex];
816     }
817     
818     if(selectedRow == NSNotFound) {
819         [[appController newVerseMenuItem] setEnabled:YES];
820         [[appController deleteVerseMenuItem] setEnabled:NO];
821         [deleteVerseButton setEnabled:NO];
822     } else {
823         [[appController newVerseMenuItem] setEnabled:YES];
824         [[appController deleteVerseMenuItem] setEnabled:YES];
825         [deleteVerseButton setEnabled:YES];
826     }
827     
828     if(selectedRow == -1 || selectedRow == NSNotFound) {
830         
831         [romanTextView setEditable:NO];
832         [backTranslationTextView setEditable:NO];
833         [notesTextView setEditable:NO];
834         [checkingTextView setEditable:NO];
835                 
836         [deleteRevisionButton setEnabled:NO];
837         [newRevisionButton setEnabled:NO];
838         
839         [[appController nextVerseMenuItem] setEnabled:NO];
840         [[appController previousVerseMenuItem] setEnabled:NO];
841         
842         [[appController newRevisionMenuItem] setEnabled:NO];
843         [[appController deleteRevisionMenuItem] setEnabled:NO];
844         [[appController nextRevisionMenuItem] setEnabled:NO];
845         [[appController previousRevisionMenuItem] setEnabled:NO];
846         
847         [[appController lockRevisionMenuItem] setEnabled:NO];
848         
849     } else {
850         
851         BOOL verseLocked = [currentVerse locked];
852         BOOL revisionLocked = [[currentVerse currentRevision] locked];
853         int revisionIndex = [currentVerse currentRevisionIndex];
854         
855         [[appController newVerseMenuItem] setEnabled:YES];
856         [[appController deleteVerseMenuItem] setEnabled:!verseLocked];
857         [[appController lockVerseMenuItem] setState:(verseLocked ? NSOnState : NSOffState)];
858         
859         [deleteVerseButton setEnabled:(!verseLocked ? NSOnState : NSOffState)];
860         
861         [referenceTableColumn setEditable:!(verseLocked || revisionLocked)];
862         [typeTableColumn setEditable:!(verseLocked || revisionLocked)];
863         [revisionTableColumn setEditable:!(verseLocked || revisionLocked)];
864         
865         [[appController lockRevisionMenuItem] setEnabled:!verseLocked];
866         [[appController lockRevisionMenuItem] setState:(revisionLocked ? NSOnState : NSOffState)];
867         [[appController deleteRevisionMenuItem] setEnabled:!(verseLocked || revisionLocked)];
868         [[appController newRevisionMenuItem] setEnabled:!verseLocked];
869         
870         [newRevisionButton setEnabled:!verseLocked];
871         [deleteRevisionButton setEnabled:!(verseLocked || revisionLocked)];
872         
873         [[appController nextRevisionMenuItem] setEnabled:
874          ((revisionIndex < [currentVerse revisionCount] - 1) && !verseLocked)];
875         [[appController previousRevisionMenuItem] setEnabled:
876          ((revisionIndex > 0) && !verseLocked)];
877         
878         [[appController nextVerseMenuItem] setEnabled:(selectedRow < [[book verses] count] - 1)];
879         [[appController previousVerseMenuItem] setEnabled:(selectedRow > 0)];
880         
881         [romanTextView setEditable:!(verseLocked || revisionLocked)];
882         [backTranslationTextView setEditable:!(verseLocked || revisionLocked)];
883         [notesTextView setEditable:!(verseLocked || revisionLocked)];
884         [checkingTextView setEditable:!(verseLocked || revisionLocked)];
885         
886         [referenceTableColumn setEditable:!verseLocked];
887         [typeTableColumn setEditable:!verseLocked];
888         [revisionTableColumn setEditable:!verseLocked];
889         }
893 - (void)readSplitViewRectsFromDefaults
895         NSUserDefaults * d = [NSUserDefaults standardUserDefaults];
896         
897         if([d boolForKey:@"RTKHorizonatalSplitViewRectSaved"]) {
898                 [rowView setFrame:NSRectFromString([d objectForKey:@"RTKRowViewRect"])];
899                 [editView setFrame:NSRectFromString([d objectForKey:@"RTKEditViewRect"])];
900         }
901     
902     if([d boolForKey:@"RTKVerticalSplitViewRectSaved"]) {
903         [verseView setFrame:NSRectFromString([d objectForKey:@"RTKVerseViewRect"])];
904         [publishedView setFrame:NSRectFromString([d objectForKey:@"RTKPublishedViewRect"])];
905     }
906     
907         if([d boolForKey:@"RTKTransliterationOn"]) {
908                 if([d boolForKey:@"RTKSplitViewRectsWithTransliterationSaved"]) {
909                         [scriptView setFrame:NSRectFromString([d objectForKey:@"RTKScriptViewRectWithTransliteration"])];
910                         [romanView setFrame:NSRectFromString([d objectForKey:@"RTKRomanViewRectWithTransliteration"])];
911                         [backTranslationView setFrame:NSRectFromString([d objectForKey:@"RTKBackTranslationViewRectWithTransliteration"])];
912                         [notesView setFrame:NSRectFromString([d objectForKey:@"RTKNotesViewRectWithTransliteration"])];
913                         [checkingView setFrame:NSRectFromString([d objectForKey:@"RTKCheckingViewRectWithTransliteration"])];
914             
915             [scriptPublishedView setFrame:NSRectFromString([d objectForKey:@"RTKScriptPublishedViewRectWithTransliteration"])];
916             [romanPublishedView setFrame:NSRectFromString([d objectForKey:@"RTKRomanPublishedViewRectWithTransliteration"])];
917                 }
918         } else {
919                 if([d boolForKey:@"RTKSplitViewRectsWithoutTransliterationSaved"]) {
920                         [romanView setFrame:NSRectFromString([d objectForKey:@"RTKRomanViewRect"])];
921                         [backTranslationView setFrame:NSRectFromString([d objectForKey:@"RTKBackTranslationViewRect"])];
922                         [notesView setFrame:NSRectFromString([d objectForKey:@"RTKNotesViewRect"])];
923                         [checkingView setFrame:NSRectFromString([d objectForKey:@"RTKCheckingViewRect"])];
924             
925             [romanPublishedView setFrame:NSRectFromString([d objectForKey:@"RTKRomanPublishedViewRect"])];
926                 }
927         }
930 - (void)writeSplitViewRectsToDefaults
932         NSUserDefaults * d = [NSUserDefaults standardUserDefaults];
933         [d setObject:[NSNumber numberWithBool:YES] forKey:@"RTKHorizonatalSplitViewRectSaved"];
934         [d setObject:NSStringFromRect([rowView frame]) forKey:@"RTKRowViewRect"];
935         [d setObject:NSStringFromRect([editView frame]) forKey:@"RTKEditViewRect"];
936     
937     [d setObject:[NSNumber numberWithBool:YES] forKey:@"RTKVerticalSplitViewRectSaved"];
938     [d setObject:NSStringFromRect([verseView frame]) forKey:@"RTKVerseViewRect"];
939     [d setObject:NSStringFromRect([publishedView frame]) forKey:@"RTKPublishedViewRect"];
941         
942         if([d boolForKey:@"RTKTransliterationOn"]) {
943                 [d setObject:[NSNumber numberWithBool:YES] forKey:@"RTKSplitViewRectsWithTransliterationSaved"];
944                 [d setObject:NSStringFromRect([scriptView frame]) forKey:@"RTKScriptViewRectWithTransliteration"];
945                 [d setObject:NSStringFromRect([romanView frame]) forKey:@"RTKRomanViewRectWithTransliteration"];
946                 [d setObject:NSStringFromRect([backTranslationView frame]) forKey:@"RTKBackTranslationViewRectWithTransliteration"];
947                 [d setObject:NSStringFromRect([notesView frame]) forKey:@"RTKNotesViewRectWithTransliteration"];
948                 [d setObject:NSStringFromRect([checkingView frame]) forKey:@"RTKCheckingViewRectWithTransliteration"];
949         
950         [d setObject:NSStringFromRect([scriptPublishedView frame]) forKey:@"RTKScriptPublishedViewRectWithTransliteration"];
951         [d setObject:NSStringFromRect([romanPublishedView frame]) forKey:@"RTKRomanPublishedViewRectWithTransliteration"];
952         } else {
953                 [d setObject:[NSNumber numberWithBool:YES] forKey:@"RTKSplitViewRectsWithoutTransliterationSaved"];
954                 [d setObject:NSStringFromRect([romanView frame]) forKey:@"RTKRomanViewRect"];
955                 [d setObject:NSStringFromRect([backTranslationView frame]) forKey:@"RTKBackTranslationViewRect"];
956                 [d setObject:NSStringFromRect([notesView frame]) forKey:@"RTKNotesViewRect"];
957                 [d setObject:NSStringFromRect([checkingView frame]) forKey:@"RTKCheckingViewRect"];
958         
959         [d setObject:NSStringFromRect([romanPublishedView frame]) forKey:@"RTKRomanPublishedViewRect"];
960         }
963 - (void)presetVerse:(RTKVerse *)verse
964  fromPrecedingVerse:(RTKVerse *)precedingVerse
966     NSString * precedingReference = [precedingVerse reference];
967     NSString * precedingType = [precedingVerse type];
968     NSString * verseNumber = [precedingReference verse];
969     NSString * reference =  [NSString stringWithFormat:@"%@ %@", 
970                              [precedingReference book],
971                              [precedingReference chapter]];
972     
973     if([verseNumber length] > 0) {
974         verseNumber = [NSString stringWithFormat:@"%i", 
975                        [verseNumber intValue] + ([precedingType isEqualToString:@"\\v"] ? 1 : 0)];
976         reference = [NSString stringWithFormat:@"%@:%@", reference, verseNumber];
977     }
978     [verse setReference:reference];
981 // Ensure that there is always at least one blank row at end of document.
982 - (void)ensureOneBlankVerse
984     NSMutableArray * verses = [book verses];
985     if(![verses count]) {
986         [verses addObject:[[[RTKVerse alloc] init] autorelease]];
987         [versesTableView noteNumberOfRowsChanged];
988         [versesTableView reloadData];
989     } else if(![(RTKVerse *) [verses objectAtIndex:[verses count] -1] blank]) {
990         RTKVerse * verse = [[[RTKVerse alloc] init] autorelease];
991         [verses addObject:verse];
992         
993         // Prefill the reference field with a verse reference derived
994         // from the preceding verse's reference.
995         RTKVerse * precedingVerse = (RTKVerse *) [verses objectAtIndex:[verses count] - 2];
996         
997         [self presetVerse:verse fromPrecedingVerse:precedingVerse];
998         
999         [versesTableView noteNumberOfRowsChanged];
1000         [versesTableView reloadData];
1001         [self search:searchField];
1002     }
1005 #pragma mark -
1006 #pragma mark menu and button handlers
1008 - (IBAction)newVerse:(id)sender
1010     NSMutableArray * verses = [book verses];
1011     int i = [versesTableView selectedRow];
1012     int newIndex = 0;
1013     RTKVerse * verse = [[[RTKVerse alloc] init] autorelease];
1014     
1015     if(i == RTKNOROWSELECTED) {
1016         [verses addObject:verse];
1017         newIndex = [verses count] - 1;
1018     } else {
1019         [verses insertObject:verse
1020                      atIndex:i + 1];
1021         newIndex = i + 1;
1022     }
1023     
1024     if(newIndex > 0)
1025         [self presetVerse:verse fromPrecedingVerse:(RTKVerse *) [verses objectAtIndex:newIndex - 1]];
1026         
1027     [searchField setStringValue:@""];
1028     [self search:searchField];
1029     
1030     [versesTableView noteNumberOfRowsChanged];
1031     [versesTableView selectRowIndexes:[[[NSIndexSet alloc] initWithIndex:newIndex] autorelease]
1032                  byExtendingSelection:NO];
1033     
1034     [self ensureOneBlankVerse];
1035     [self updateUI];
1036     
1037     // TODO: Change this when undo/redo is supported
1038     [self updateChangeCount:NSChangeDone];
1041 - (IBAction)deleteVerse:(id)sender
1043     //NSMutableArray * verses = [book verses];
1044     int currentVerseIndex = [[book verses] indexOfObject:currentVerse];
1045     
1046     NSEnumerator * e = [versesTableView selectedRowEnumerator];
1047     NSMutableArray * selectedVerseArray = [NSMutableArray new];
1048     NSNumber * rowIndexNumber;
1049     int rowIndex = 0;
1050     
1051     while(rowIndexNumber = [e nextObject]) {
1052         rowIndex = [rowIndexNumber intValue];
1053         int verseIndex = [[visibleVerseIndexes objectAtIndex:rowIndex] intValue];
1054         [selectedVerseArray addObject:[NSNumber numberWithInt:verseIndex]];
1055     }
1056     
1057     [book setVerses:(NSMutableArray *) [[book verses] arrayByRemovingObjectsAtIndexes:selectedVerseArray]];
1058     
1059     [self selectVerse:[[book verses] objectAtIndex:MIN(currentVerseIndex, [[book verses] count] -1)]];
1060     
1061     // Breaks when deleting last verse.
1062     [searchField setStringValue:@""];
1063     [self search:searchField];
1064     
1065     [versesTableView noteNumberOfRowsChanged];
1066     [self ensureOneBlankVerse];
1067     
1068     
1069     // TODO: Change this when undo/redo is supported
1070     [self updateChangeCount:NSChangeDone];
1073 - (void)nextVerse:(id)sender
1075     NSMutableArray * verses = [book verses];
1076     int currentVerseIndex = [verses indexOfObject:currentVerse];
1077     if(currentVerseIndex < [verses count] -1)
1078         [self selectVerse:[verses objectAtIndex:currentVerseIndex + 1]];
1081 - (void)previousVerse:(id)sender
1083     NSMutableArray * verses = [book verses];
1084     int currentVerseIndex = [verses indexOfObject:currentVerse];
1085     if(currentVerseIndex > 0)
1086         [self selectVerse:[verses objectAtIndex:currentVerseIndex - 1]];
1090 // Updates the UI to reflect the newly selected revision.
1091 // Returns YES on success, NO on failure.
1092 - (BOOL)selectRevision:(RTKRevision *)revision
1094     if(revision == currentRevision) return YES;
1095     
1096     currentRevision = revision;
1097     
1098     [romanTextView setString:[revision roman]];
1099     [scriptTextView setString:[revision script]];
1100     [backTranslationTextView setString:[revision backTranslation]];
1101     [notesTextView setString:[revision notes]];
1102     [checkingTextView setString:[revision checking]];
1104     return YES;
1108 // Return index of the first character of verse as represented in textView.
1109 // Used to determine verse text location in the NSTextView for yellow highlighting and scrolling purposes.
1110 - (int)indexOfVerse:(RTKVerse *)verse inTextView:(NSTextView *)textView
1112     NSEnumerator * e = [[book verses] objectEnumerator];
1113     RTKVerse * v;
1114     int i = 0;
1115     while(v = [e nextObject]) {
1116         
1117         if(v == verse)
1118             return i;
1119         
1120         i += [[v mutableAttributedString:(textView == romanPublishedTextView)] length];
1121     }
1122     return 0;
1126 - (void)updateVerse:(RTKVerse *)verse
1127        withOldIndex:(int)index
1128 inPublishedTextView:(NSTextView *)textView
1130     NSTextStorage *textStorage = [textView textStorage];
1131     
1132     NSRange range;
1133     
1134     if([textStorage length] <= index || [textStorage length] == 0) {
1135         [textStorage insertAttributedString:[verse mutableAttributedString:(textView == romanPublishedTextView)] atIndex:[textStorage length]];
1136         
1137     } else {
1138     
1139         [textStorage attribute:@"RTKVerse"
1140                   atIndex:index 
1141     longestEffectiveRange:&range
1142                        inRange:NSMakeRange(0, [textStorage length])];
1143         
1144         [textStorage replaceCharactersInRange:range withAttributedString:[verse mutableAttributedString:(textView == romanPublishedTextView)]];
1145     }
1149 - (void)highlightVerse:(RTKVerse *)verse inTextView:(NSTextView *)textView
1151     int verseIndex = [self indexOfVerse:verse inTextView:textView];
1152     NSTextStorage *textStorage = [textView textStorage];
1153     
1154     
1155     
1156     if(verseIndex >= [textStorage length])
1157         return;
1158     
1159     NSRange componentRange;
1160     NSString *component = [textStorage attribute:@"RTKVerse" 
1161                                               atIndex:verseIndex
1162                                 longestEffectiveRange:&componentRange
1163                                               inRange:NSMakeRange(0, [textStorage length])];
1164     if(componentRange.length == 0)
1165         return;
1166     
1167     [textStorage removeAttribute:NSBackgroundColorAttributeName];
1168     [textStorage addAttribute:NSBackgroundColorAttributeName value:[NSColor yellowColor] range:NSMakeRange(componentRange.location, componentRange.length - 1)];
1172 - (void)scrollVerseToVisible:(RTKVerse *)verse inTextView:(NSTextView *)textView
1174     int verseIndex = [self indexOfVerse:verse inTextView:textView];
1175     NSTextStorage *textStorage = [textView textStorage];
1176     
1177     if(verseIndex >= [textStorage length]) {
1178         NSLog(@"verseIndex >= [textStorage length] in scrollVerseToVisible");
1179         return;
1180     }
1181     
1182     NSRange firstComponentRange;
1183     NSString *firstComponent = [textStorage attribute:@"RTKVerse"
1184                                               atIndex:verseIndex
1185                                 longestEffectiveRange:&firstComponentRange
1186                                               inRange:NSMakeRange(0, [textStorage length])];
1187     
1188     [textView scrollRangeToVisible:NSMakeRange(firstComponentRange.location, firstComponentRange.length - 1)];
1192 // Updates the UI to reflect the newly selected verse.
1193 // Returns YES on success, NO on failure.
1194 - (BOOL)selectVerse:(RTKVerse *)verse
1196     // Same verse, so nothing to do.
1197     if(verse == currentVerse) return YES;
1199     // Index of verse, not of row in table, which can be different if using a filtering search.
1200     NSUInteger verseIndex = [[book verses] indexOfObject:verse];
1201     
1202     // Can't find the verse, so nothing to do.
1203     if (verseIndex == NSNotFound) return NO;
1204     
1205     // Save for next time.
1206     currentVerse = verse;
1207     
1208     // Select verse in table.
1209     [versesTableView selectRowIndexes:[NSIndexSet indexSetWithIndex:verseIndex] byExtendingSelection:NO]; // 10.3 and later.
1210     [versesTableView scrollRowToVisible:verseIndex];
1211     
1212     // Load revision into single-verse text fields.
1213     [self selectRevision:[verse currentRevision]];
1214     
1215     // There is an interaction between scrollVerseToVisible and addAttribute that causes unpredictable scrolling of the published NSTextViews when using arrow keys to navigate up and down the verse NSTableView.
1216     // Separating them by a time delay helps.
1217     
1218     
1219     //if([documentWindow firstResponder] != romanPublishedTextView)
1220         [[self performAfterDelay:0.3] scrollVerseToVisible:verse inTextView:romanPublishedTextView];
1221     
1222     //if([documentWindow firstResponder] != scriptPublishedTextView)
1223         [[self performAfterDelay:0.3] scrollVerseToVisible:verse inTextView:scriptPublishedTextView];
1224      
1225     // Highlight verse in published views and scroll to visible.
1226     [self highlightVerse:verse inTextView:romanPublishedTextView];
1227     [self highlightVerse:verse inTextView:scriptPublishedTextView];
1228     
1229     // Update Menus, Buttons, and Fonts
1230     [self updateUI];
1231     
1232     // Successfully selected a different verse.
1233     return YES;
1237 - (IBAction)lockVerse:(id)sender
1239     int selectedRow = [versesTableView selectedRow];
1240     if(selectedRow >= 0) {
1241         RTKVerse * verse = [[book verses] objectAtIndex:selectedRow];
1242         
1243         [verse setLocked:![verse locked]];
1244         
1245         // TODO:Change this when undo/redo is supported
1246         [self updateChangeCount:NSChangeDone];
1247     }
1248     [versesTableView reloadData];
1249     [self updateUI];
1252 #pragma mark -
1254 - (IBAction)newRevision:(id)sender
1256     int selectedRow = [versesTableView selectedRow];
1257     if(selectedRow >= 0) {
1258         RTKVerse * verse = [[book verses] objectAtIndex:selectedRow];
1259         NSMutableArray * revisions = [verse revisions];
1260         RTKRevision * newRevision = [[revisions objectAtIndex:[verse currentRevisionIndex]] copy];
1261         
1262         [newRevision setLocked:NO];
1263         [revisions addObject:newRevision];
1264         [verse setCurrentRevisionIndex:[revisions count] -1];
1265         
1266         [versesTableView reloadData];
1267         [self updateUI];
1268         
1269         // TODO: Change this when undo/redo is supported
1270         [self updateChangeCount:NSChangeDone];
1271     }
1274 - (IBAction)deleteRevision:(id)sender
1276     int selectedRow = [versesTableView selectedRow];
1277     if(selectedRow >= 0) {
1278         RTKVerse * verse = [[book verses] objectAtIndex:selectedRow];
1279         NSMutableArray * revisions = [verse revisions];
1280         int currentRevisionIndex = [verse currentRevisionIndex];
1281         RTKRevision * revision = [[verse revisions] objectAtIndex:currentRevisionIndex];
1282         
1283         if([revisions count] > 1 && ![revision locked]) {
1284             [revisions removeObjectAtIndex:currentRevisionIndex];
1285             
1286             if(currentRevisionIndex == [revisions count])
1287                 [verse setCurrentRevisionIndex:currentRevisionIndex -1];
1288         } else {
1289             NSLog(@"%@ trying to delete locked revision", sender);
1290         }
1291         
1292         [versesTableView reloadData];
1293         [self updateUI];
1294         
1295         // TODO: Change this when undo/redo is supported
1296         [self updateChangeCount:NSChangeDone];
1297     }
1300 // TODO: Clean up this revision switching code.
1301 - (void)nextRevision:(id)sender
1303     int selectedRow = [versesTableView selectedRow];
1304     if(selectedRow >= 0) {
1305         RTKVerse * verse = [[book verses] objectAtIndex:selectedRow];
1306         int revisionCount = [[verse revisions] count];
1307         int currentRevisionIndex = [verse currentRevisionIndex];
1308         
1309         if(currentRevisionIndex < revisionCount -1) {
1310             // TODO: check if not needed
1311             [versesTableView reloadData];
1312             [verse setCurrentRevisionIndex:currentRevisionIndex + 1];
1313             [self updateUI];
1314             
1315             // TODO: Change this when undo/redo is supported
1316             [self updateChangeCount:NSChangeDone];
1317         }
1318     }
1321 - (void)previousRevision:(id)sender
1323     int selectedRow = [versesTableView selectedRow];
1324     if(selectedRow >= 0) {
1325         RTKVerse * verse = [[book verses] objectAtIndex:selectedRow];
1326         int revisionCount = [[verse revisions] count];
1327         int currentRevisionIndex = [verse currentRevisionIndex];
1328         
1329         if(currentRevisionIndex > 0) {
1330             // TODO: check if not needed
1331             [versesTableView reloadData];
1332             [verse setCurrentRevisionIndex:currentRevisionIndex - 1];
1333             [self updateUI];
1334             
1335             // TODO: Change this when undo/redo is supported
1336             [self updateChangeCount:NSChangeDone];
1337         }
1338     }
1341 -(IBAction)switchRevision:(id)sender
1343     int selectedRow = [versesTableView selectedRow];
1344     if(selectedRow >= 0) {
1345         RTKVerse * verse = [[book verses] objectAtIndex:selectedRow];
1346         int revisionCount = [[verse revisions] count];
1347         int revisionIndex = 0;
1348                 
1349         if(revisionIndex != [verse currentRevisionIndex]) {
1350             // TODO: check if not needed
1351             [versesTableView reloadData];
1352             [verse setCurrentRevisionIndex:revisionIndex];
1353             [self updateUI];
1354             
1355             // TODO: Change this when undo/redo is supported
1356             [self updateChangeCount:NSChangeDone];
1357         }
1358     }
1361 - (IBAction)lockRevision:(id)sender
1363     int selectedRow = [versesTableView selectedRow];
1364     if(selectedRow >= 0) {
1365         RTKVerse * verse = [[book verses] objectAtIndex:selectedRow];
1366         RTKRevision * revision = [verse currentRevision];
1367                 
1368         [revision setLocked:![revision locked]];
1369         
1370         // TODO:Change this when undo/redo is supported
1371         [self updateChangeCount:NSChangeDone];
1372     }
1373     [versesTableView reloadData];
1374     [self updateUI];
1377 #pragma mark -
1378 #pragma mark window delegate methods
1380 - (void)windowWillClose:(NSNotification *)aNotification
1382     windowIsOpen = NO;
1383     [convertingLock unlock];
1386 - (void)windowDidBecomeKey:(NSNotification *)aNotification
1388         [self readSplitViewRectsFromDefaults];
1391 - (void)windowDidResignKey:(NSNotification *)aNotification
1393         [self writeSplitViewRectsToDefaults];
1396 #pragma mark -
1397 #pragma mark split view delegate methods
1399 - (void)splitViewDidResizeSubviews:(NSNotification *)aNotification {
1400         [self writeSplitViewRectsToDefaults];
1403 - (void)splitView:(NSSplitView *)sender 
1404 constrainMinCoordinate:(float *)min 
1405         maxCoordinate:(float *)max 
1406           ofSubviewAt:(int)offset
1408         (*min) = 0.0;
1409         (*max) = INFINITY;
1410         
1411         if(sender == horizontalSplitView) {
1412                 (*min) = 100.0;
1413                 (*max) = [[documentWindow contentView] frame].size.height -  150.0;
1414         }
1417 #pragma mark -
1418 #pragma mark text view delegate methods
1421  textDidChange is a delegate method that is called when the user modifies an NSTextView.
1422  We update the appropriate RTKVerse or RTKRevision object from the changed text field.
1423  */
1424 - (void)textDidChange:(NSNotification *)notification
1426     NSLog(@"textDidChange");
1427     NSTextView * changedTextView = [notification object];
1428     
1429     if(changedTextView == romanTextView) {
1430         
1431         [currentRevision setRoman:[[changedTextView string] copy]];
1432         
1433         int oldVerseIndexInPublishedTextView = [self indexOfVerse:currentVerse inTextView:romanPublishedTextView];
1434         [self updateVerse:currentVerse withOldIndex:oldVerseIndexInPublishedTextView inPublishedTextView:romanPublishedTextView];
1435         [[self performAfterDelay:0.5] scrollVerseToVisible:currentVerse inTextView:romanPublishedTextView];
1436         [self highlightVerse:currentVerse inTextView:romanPublishedTextView];
1437         
1438                 if([[NSUserDefaults standardUserDefaults] boolForKey:@"RTKTransliterationOn"])
1439                         [self convertRevision:currentRevision];
1440         
1442     } else if(changedTextView == scriptTextView) {
1443         
1444         [currentRevision setScript:[[changedTextView string] copy]];
1445     } else if(changedTextView == backTranslationTextView) {
1446         [currentRevision setBackTranslation:[[changedTextView string] copy]];
1447     } else if(changedTextView == notesTextView) {
1448         [currentRevision setNotes:[[changedTextView string] copy]];
1449     } else if(changedTextView == checkingTextView) {
1450         [currentRevision setChecking:[[changedTextView string] copy]];
1451     } else if(changedTextView == romanPublishedTextView) {
1452         //[self romanPublishedTextViewDidChange:notification];
1453         
1454     } else {
1455         NSLog(@"unhandled textview %@ sent to textDidChange", changedTextView);
1456     }
1457     // TODO: Change this when undo/redo is supported
1458     [self updateChangeCount:NSChangeDone];
1459     
1460     NSRect rowRect = [versesTableView rectOfRow:[versesTableView selectedRow]];    
1461     [versesTableView setNeedsDisplayInRect:rowRect];
1462         [self updateUI];
1463     [self ensureOneBlankVerse];
1466 // Informs the appropriate RTKVerse object of its changes.
1467 - (void)romanPublishedTextViewDidChange:(NSNotification *)notification
1469     NSLog(@"romanPublishedTextViewDidChange");
1470     NSTextStorage *textStorage = [romanPublishedTextView textStorage];
1471     NSRange selectedRange = [romanPublishedTextView selectedRange];
1472     
1473     NSRange verseRange;    
1474     RTKVerse *verse = [textStorage attribute:@"RTKVerse" 
1475                                      atIndex:selectedRange.location
1476                        longestEffectiveRange:&verseRange
1477                                      inRange:NSMakeRange(0, [textStorage length])];
1478     NSAttributedString *verseString = [textStorage attributedSubstringFromRange:verseRange];
1479     
1480     BOOL changeAccepted = [verse updateWithAttributedString:[textStorage attributedSubstringFromRange:NSMakeRange(0, [textStorage length])] 
1481                                                     atIndex:selectedRange.location];
1483     if(!changeAccepted) {
1484         NSLog(@"RTKVerse rejected change.");
1485     } else {
1486         [romanTextView setString:[[verse currentRevision] roman]];
1487         
1488         if([[NSUserDefaults standardUserDefaults] boolForKey:@"RTKTransliterationOn"])
1489                         [self convertRevision:[currentVerse currentRevision]];
1490     }
1493 - (void)textViewDidChangeSelection:(NSNotification *)notification
1494 {   
1495     NSTextView * changedTextView = [notification object];
1496     
1497     if([documentWindow firstResponder] != changedTextView)
1498         return;
1499     
1500     // Updated to == from = AKK 2012
1501     if(changedTextView == romanPublishedTextView || changedTextView == scriptPublishedTextView) {
1502         //NSLog(@"textViewDidChangeSelection %@", (changedTextView == romanPublishedTextView ? @"roman" : @"script"));
1503         NSTextStorage *textStorage = [changedTextView textStorage];
1504         NSRange selectedRange = [changedTextView selectedRange];
1505                 
1506         // Don't allow editing end of text field.
1507         if(selectedRange.location == [textStorage length]) {
1508             [(RTKTigerTextView *) changedTextView setAllowEditing:NO];
1509             return;
1510         }
1511         
1512         // Temporary logging for testing.
1513         //NSLog(@"%@", [[textStorage attributesAtIndex:selectedRange.location effectiveRange:NULL] description]);
1514         
1515         RTKVerse *firstVerse = [textStorage attribute:@"RTKVerse"
1516                                               atIndex:selectedRange.location
1517                                 longestEffectiveRange:NULL
1518                                               inRange:NSMakeRange(0, [textStorage length])];
1519         
1520         RTKVerse *lastVerse = [textStorage attribute:@"RTKVerse" 
1521                                              atIndex:(selectedRange.location + selectedRange.length)
1522                                longestEffectiveRange:NULL
1523                                              inRange:NSMakeRange(0, [textStorage length])];
1524         
1525         NSRange firstComponentRange;
1526         NSString *firstComponent = [textStorage attribute:@"RTKVerseComponent" 
1527                                                   atIndex:selectedRange.location
1528                                     longestEffectiveRange:&firstComponentRange
1529                                                   inRange:NSMakeRange(0, [textStorage length])];
1530         
1531         NSString *lastComponent = [textStorage attribute:@"RTKVerseComponent" 
1532                                                  atIndex:(selectedRange.location + selectedRange.length)
1533                                    longestEffectiveRange:NULL
1534                                                  inRange:NSMakeRange(0, [textStorage length])];
1535         
1536         NSString *nextToLastComponent = nil;
1537         NSRange nextToLastComponentRange;
1538         if( (selectedRange.location + selectedRange.length - 1) > 0 && !((selectedRange.location == 0) && (selectedRange.length == 0)) ) {
1539             nextToLastComponent = [textStorage attribute:@"RTKVerseComponent" 
1540                                                  atIndex:selectedRange.location + selectedRange.length - 1
1541                                    longestEffectiveRange:&nextToLastComponentRange
1542                                                  inRange:NSMakeRange(0, [textStorage length])];
1543         }
1544         
1545         // Don't allow editing if selection spans multiple verses or components.
1546         if((firstVerse != lastVerse) || ((firstComponent != lastComponent) && (firstComponent != nextToLastComponent)) ) {
1547             [(RTKTigerTextView *) changedTextView setAllowEditing:NO];
1548             return;
1549         }
1550         
1551         if(firstVerse != currentVerse)
1552             [self selectVerse:firstVerse];
1553          
1554         // Allow editing if selection is within the text of a verse.
1555         if([firstComponent isEqualToString:@"Verse Text"]) {
1556             [(RTKTigerTextView *) changedTextView setAllowedEditingRange:firstComponentRange];
1557             //[textStorage addAttribute:NSBackgroundColorAttributeName value:[NSColor yellowColor] range:firstComponentRange];
1558             NSRange dummyRange;
1559             [changedTextView setTypingAttributes:[textStorage attributesAtIndex:firstComponentRange.location effectiveRange:&dummyRange]];
1560         } else {
1561             if((selectedRange.location + selectedRange.length - 1) > 0) {
1562                 // Allow editing if at the end of a "Verse Text" section.
1563                 if([nextToLastComponent isEqualToString:@"Verse Text"]) {
1564                     [changedTextView setTypingAttributes:[textStorage attributesAtIndex:selectedRange.location + selectedRange.length - 1
1565                                                                            effectiveRange:NULL]];
1566                     //[textStorage addAttribute:NSBackgroundColorAttributeName value:[NSColor yellowColor] range:nextToLastComponentRange];
1567                     [(RTKTigerTextView *) changedTextView setAllowedEditingRange:nextToLastComponentRange];
1568                 } else {
1569                     [(RTKTigerTextView *) changedTextView setAllowEditing:NO];
1570                 }
1571             }
1572         }
1573     }
1574     [scriptPublishedTextView setAllowEditing:NO];
1578 - (void)reloadTableData:(id)dummy
1580     NSLog(@"- (void)reloadTableData:(id)dummy");
1581     [versesTableView reloadData];
1585 #pragma mark -
1586 #pragma mark table delegate methods
1588 - (void)tableView:(NSTableView *)tableView 
1589 didClickTableColumn:(NSTableColumn *)tableColumn
1591     NSLog(@"click in column header %@, row %li", tableColumn, (long)[versesTableView selectedRow]);
1594 - (BOOL)tableView:(NSTableView *)aTableView 
1595 shouldEditTableColumn:(NSTableColumn *)aTableColumn 
1596               row:(int)rowIndex
1598     if(aTableColumn == romanTableColumn) {
1599         [documentWindow makeFirstResponder:romanTextView];
1600     } else if(aTableColumn == scriptTableColumn) {
1601         [documentWindow makeFirstResponder:romanTextView];
1602     } else if(aTableColumn == backTranslationTableColumn) {
1603         [documentWindow makeFirstResponder:backTranslationTextView];
1604     } else if(aTableColumn == notesTableColumn) {
1605         [documentWindow makeFirstResponder:notesTextView];
1606     } else if(aTableColumn == checkingTableColumn) {
1607         [documentWindow makeFirstResponder:checkingTextView];
1608     } else if(aTableColumn == referenceTableColumn) {
1609         return YES;
1610     } else if(aTableColumn == typeTableColumn) {
1611         return YES;
1612     }
1613     return NO;
1616 - (void)tableViewSelectionDidChange:(NSNotification *)aNotification
1618     //[self updateUI];
1619     
1620     // Attempting to learn why arrow key row selection in the tableview causes unpredictable scrolling in the published view textfields.
1621     // NSLog(@"%lu",[[NSApp currentEvent] type]);
1622     
1623     NSMutableArray * verses = [book verses];
1624     int selectedRowIndex = [(NSTableView *) [aNotification object] selectedRow];
1625     if (selectedRowIndex < [verses count]) {
1626         [self selectVerse:[verses objectAtIndex:selectedRowIndex]];
1627     }
1631 - (void)tableViewSelectionIsChanging:(NSNotification *)aNotification
1633     //[self ensureOneBlankVerse];
1634     //[self updateUI];
1636  */
1639 #pragma mark -
1640 #pragma mark tableSource methods
1642 - (int)numberOfRowsInTableView:(NSTableView *)aTableView
1644     int rowCount = 0;
1645     if(aTableView == versesTableView) {
1646         rowCount = [visibleVerseIndexes count];
1647     } else {
1648         NSLog(@"object %@ calling numberOfRowsInTableView", aTableView);
1649     }
1650     return rowCount;
1653 - (id)tableView:(NSTableView *)aTableView 
1654 objectValueForTableColumn:(NSTableColumn *)aTableColumn 
1655             row:(int)rowIndex
1657     NSUserDefaults * d = [NSUserDefaults standardUserDefaults];
1658     
1659     NSParameterAssert(rowIndex >= 0 && rowIndex < [visibleVerseIndexes count]);
1660     rowIndex = [(NSNumber *)[visibleVerseIndexes objectAtIndex:rowIndex] intValue];
1661         
1662     NSString * value = @"default value";
1663     
1664     NSMutableArray * verses = [book verses];
1665     
1666     RTKVerse * verse = [verses objectAtIndex:rowIndex];
1667     int revisionIndex = [verse currentRevisionIndex];
1668     NSMutableArray * revisions = [verse revisions];
1669     RTKRevision * revision = [revisions objectAtIndex:revisionIndex];
1670     
1671     if(aTableView == versesTableView) {
1672         if(aTableColumn == referenceTableColumn) {
1673             value = [verse reference];
1674         } else if(aTableColumn == typeTableColumn) {
1675             value = @""; // has popup menu
1676                         [[aTableColumn dataCellForRow:rowIndex] setEnabled:![verse locked]];
1677         } else if(aTableColumn == revisionTableColumn) {
1678             value = @""; // has popup menu
1679                         [[aTableColumn dataCellForRow:rowIndex] setEnabled:![verse locked]];
1680                 } else if(aTableColumn == lockedTableColumn) {
1681             value = @""; // has check box
1682         } else if(aTableColumn == scriptTableColumn) {
1683             value = [revision script];
1684                 } else if(aTableColumn == romanTableColumn) {
1685             value = [revision roman];
1686         } else if(aTableColumn == backTranslationTableColumn) {
1687             value = [revision backTranslation];
1688         } else if(aTableColumn == notesTableColumn) {
1689             value = [revision notes];
1690         } else if(aTableColumn == checkingTableColumn) {
1691             value = [revision checking];
1692         } else {
1693             NSLog(@"unhandled column %@ for tableView:objectValueForTableColumn:row:", aTableColumn);
1694         }
1695     } else {
1696         NSLog(@"unhandled object %@ calling tableView:objectValueForTableColumn:row:", aTableView);
1697     }
1698     return value;
1701 // Called by dataCellForRowColumn: in RTKTigerTableColumn
1702 - (id)dataCellForRow:(int)row
1703                           column:(NSTableColumn *)column
1705         // TODO: fix possible leak
1706         NSPopUpButtonCell *cell = [NSPopUpButtonCell new];
1707         [cell setEnabled:[[[book verses] objectAtIndex:row] locked]];
1710 - (void)tableView:(NSTableView *)aTableView
1711    setObjectValue:(id)anObject 
1712    forTableColumn:(NSTableColumn *)aTableColumn
1713               row:(int)rowIndex
1715     //NSParameterAssert(rowIndex >= 0 && rowIndex < [visibleVerseIndexes count]);
1716     // Don't set values for deleted rows.  This is needed when a row is deleted during editing of one of its NSCells.l
1717     if(!(rowIndex >= 0 && rowIndex < [visibleVerseIndexes count]))
1718         return;
1719     
1720     rowIndex = [(NSNumber *)[visibleVerseIndexes objectAtIndex:rowIndex] intValue];
1721     
1722     NSMutableArray * verses = [book verses];
1723     RTKVerse * verse = [verses objectAtIndex:rowIndex];
1724     int revisionIndex = [verse currentRevisionIndex];
1725     NSMutableArray * revisions = [verse revisions];
1726     RTKRevision * revision = [revisions objectAtIndex:revisionIndex];
1727     
1728     if(aTableView == versesTableView) {
1729         if(aTableColumn == referenceTableColumn) {
1730             [verse setReference:anObject];
1731             [self selectVerse:verse];
1732         } else if(aTableColumn == typeTableColumn) {
1733             [verse setType:anObject];
1734             [self selectVerse:verse];
1735         } else if(aTableColumn == revisionTableColumn) {
1736             [verse setCurrentRevisionIndex:[(NSNumber *)anObject intValue]];
1737             [self selectRevision:[currentVerse currentRevision]];
1738             [self selectVerse:verse];
1739         } else if(aTableColumn == lockedTableColumn) {
1740             [verse setLocked:[(NSNumber *)anObject boolValue]];
1741         } else if(aTableColumn == scriptTableColumn) {
1742             [revision setScript:anObject];
1743         } else if(aTableColumn == romanTableColumn) {
1744             [revision setRoman:anObject];
1745         } else if(aTableColumn == scriptTableColumn) {
1746             [revision setScript:anObject];
1747         } else if(aTableColumn == backTranslationTableColumn) {
1748             [revision setBackTranslation:anObject];
1749         } else if(aTableColumn == notesTableColumn) {
1750             [revision setNotes:anObject];
1751         } else if(aTableColumn == checkingTableColumn) {
1752             [revision setChecking:anObject];
1753         } else {
1754             NSLog(@"unhandled column %@ for setObjectValue:ForTableColumn:Row", aTableColumn);
1755         }
1756     } else {
1757         NSLog(@"unhandled object %@ calling setObjectValue:ForTableColumn:Row", aTableView);
1758     }
1759     
1760     // TODO: Change this when undo/redo is supported
1761     [self updateChangeCount:NSChangeDone];
1762         
1763     [self ensureOneBlankVerse];
1764     [self updateUI];
1768  Provide popup menus in the type and revision columns.
1769  http://members.shaw.ca/akochoi/2003/10-05/index.html#1
1770  */
1771 - (void)tableView:(NSTableView *)aTableView 
1772   willDisplayCell:(id)aCell
1773    forTableColumn:(NSTableColumn *)aTableColumn 
1774               row:(int)rowIndex
1776     NSParameterAssert(rowIndex >= 0 && rowIndex < [visibleVerseIndexes count]);
1777     rowIndex = [(NSNumber *)[visibleVerseIndexes objectAtIndex:rowIndex] intValue];
1778     RTKVerse * verse = [[book verses] objectAtIndex:rowIndex];
1779     
1780     if(aTableColumn == revisionTableColumn) {
1781         NSMutableArray * revisions = [verse revisions];
1782         int revisionCount = [revisions count];
1783         int revisionIndex = [verse currentRevisionIndex];
1784         
1785         [(NSPopUpButtonCell *)aCell removeAllItems];
1786         int i;
1787         for(i = 0; i < revisionCount; i++) {
1788             NSString * title = [NSString stringWithFormat:@"%i of %i", i + 1, revisionCount];
1789             if([(RTKRevision *)[revisions objectAtIndex:i] locked])
1790                 title = [title stringByAppendingString:@" (Locked)"];
1791             [(NSPopUpButtonCell *)aCell addItemWithTitle:title];
1792         }
1793         [(NSPopUpButtonCell *)aCell selectItemAtIndex:revisionIndex];
1794     } else if(aTableColumn == typeTableColumn) {
1795         NSString * type = [verse type];
1796         
1797         [(NSComboBoxCell *)aCell removeAllItems];
1798         [(NSComboBoxCell *)aCell addItemsWithObjectValues:verseTypes];
1799         [(NSComboBoxCell *)aCell selectItemWithObjectValue:type];
1800         [(NSComboBoxCell *)aCell setStringValue:type];
1801         
1802     } else if(aTableColumn == lockedTableColumn) {
1803         [(NSButtonCell *)aCell setState:([verse locked] ? NSOnState : NSOffState)];
1804     }
1807 #pragma mark -
1808 #pragma mark Script Conversion
1810 - (void)transcriptionTypeChanged:(id)dummy
1812         if([[NSUserDefaults standardUserDefaults] boolForKey:@"RTKTransliterationOn"])
1813                 [self regenerateAllScript];
1816 -(void)observeValueForKeyPath:(NSString *)keyPath
1817                      ofObject:(id)object
1818                        change:(NSDictionary *)change
1819                       context:(void *)context
1821     NSLog(@"KVO: %@ changed property %@ to value %@", object, keyPath, change);
1822     
1823     if ([keyPath compare:@"RTKTransliterationOn"] == NSOrderedSame) {
1824         [self transliterationOnChanged];
1825         
1826     } else if ([keyPath compare:@"RTKZVXSubstitution"] == NSOrderedSame) {
1827         [self substituteZVXChanged];
1828     }
1829         
1830     
1833 - (void)transliterationOnChanged
1835     if([[NSUserDefaults standardUserDefaults] boolForKey:@"RTKTransliterationOn"]) {
1836         
1837         [scriptTableColumn setHidden:NO];
1838         
1839                 [splitViewOfTextViews addSubview:scriptView
1840                                                           positioned:NSWindowBelow
1841                                                           relativeTo:romanView];
1842         [scriptView release];
1843         
1844         [publishedSplitView addSubview:scriptPublishedView
1845                               positioned:NSWindowAbove
1846                               relativeTo:romanPublishedView];
1847         [scriptPublishedView release];
1848         [scriptPublishedView setHidden:NO];
1849                 
1850         [self regenerateAllScript];
1851         
1852         } else {
1853         
1854         [scriptTableColumn setHidden:YES];
1855         
1856         [scriptView retain];
1857                 [scriptView removeFromSuperview];
1858         
1859         
1860         [scriptPublishedView retain];
1861         [scriptPublishedView removeFromSuperview];
1862         [scriptPublishedView setHidden:YES];
1863         
1864         }
1865         [self readSplitViewRectsFromDefaults];
1866         [self writeSplitViewRectsToDefaults];
1867     
1868     [self scrollVerseToVisible:currentVerse inTextView:romanPublishedTextView];
1869     [self scrollVerseToVisible:currentVerse inTextView:scriptPublishedTextView];
1874 - (void)mainThreadUpdateUI:(id)dummy
1876     if(!windowIsOpen)
1877         return;
1878     NSRect rowRect = [versesTableView rectOfRow:[versesTableView selectedRow]];    
1879     [versesTableView setNeedsDisplayInRect:rowRect];
1880     
1881     [scriptTextView setString:[[currentVerse currentRevision] script]];
1882     
1883     [self updateUI];
1886 - (void)regenerateAllScript
1888     NSEnumerator * verseEnumerator = [[book verses] objectEnumerator];
1889     RTKVerse * verse;
1890         
1891     while(verse = [verseEnumerator nextObject]) {
1892         [[self doSelf] convertRevision:[[verse revisions] each]];
1893     }
1897 - (void)convertRevision:(RTKRevision *)revision
1899     [revisionsToConvertLock lock];
1900     
1901     
1902     NSUserDefaults * d = [NSUserDefaults standardUserDefaults];
1903     NSString * fontOutputString = nil;
1904     
1905     NSString * transcriptionType = [d valueForKey:@"RTKTranscriptionType"];
1906     fontOutputString = transcriptionType;
1907     {
1908         NSString * definitionDir = [[[NSBundle mainBundle] resourcePath] stringByAppendingString:@"/active_definitions/"];
1909         NSDictionary * output = [RTKSharedConvertor convertString:[revision roman]
1910                                                       inputSystem:[definitionDir stringByAppendingString:
1911                                                                    [[d objectForKey:@"RTKInputSystem"] stringByAppendingString:@".rtkinput"]]
1912                                                      scriptSystem:[definitionDir stringByAppendingString:
1913                                                                    [[d objectForKey:@"RTKScriptSystem"] stringByAppendingString:@".rtkscript"]]
1914                                                        fontSystem:[definitionDir stringByAppendingString:
1915                                                                    [[d objectForKey:@"RTKEncodingSystem"] stringByAppendingString:@".rtkfont"]]
1916                                                   withMetaStrings:generateMetaStrings
1917                                               checkForPunctuation:NO];
1918         
1919         if(generateMetaStrings)
1920             NSLog(@"%@", [output description]);
1921         
1922         fontOutputString = [output objectForKey:@"RTKFont"];
1923     }
1924     
1925     if(fontOutputString)
1926         [revision setScript:fontOutputString];
1927     
1928     if(revision == currentRevision) {
1929         [scriptTextView setString:[revision script]];
1930         
1931         int oldVerseIndexInPublishedTextView = [self indexOfVerse:currentVerse inTextView:scriptPublishedTextView];
1932         [self updateVerse:currentVerse withOldIndex:oldVerseIndexInPublishedTextView inPublishedTextView:scriptPublishedTextView];
1933         [[self performAfterDelay:0.5] scrollVerseToVisible:currentVerse inTextView:scriptPublishedTextView];
1934         [self highlightVerse:currentVerse inTextView:scriptPublishedTextView];
1936     }
1937     [revisionsToConvertLock unlock];
1941 @end