5 // Copyright (c) 2005 A. Karl Keller (http://karlk.net)
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"
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";
32 //BOOL generateMetaStrings = YES;
33 BOOL generateMetaStrings = NO;
35 @implementation RTKTigerDocument
39 if(self = [super init]) {
40 NSNotificationCenter * nc = [NSNotificationCenter defaultCenter];
43 selector:@selector(plainTextDelimiterChanged:)
44 name:@"RTKPlainTextDelimiterChanged"
48 selector:@selector(definitionsChanged:)
49 name:@"RTKDefinitionsChanged"
53 selector:@selector(fontsChanged:)
54 name:@"RTKFontsChanged"
58 selector:@selector(transcriptionTypeChanged:)
59 name:@"RTKTranscriptionTypeChanged"
62 book = [[RTKBook alloc] init];
64 revisionsToConvert = [[NSMutableArray alloc] init];
66 revisionsToConvertLock = [[NSLock alloc] init];
68 convertingLock = [[NSLock alloc] init];
69 [convertingLock lock];
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];
78 [self setCreationDate:[NSDate dateWithTimeIntervalSinceNow:0.0]];
80 [self setBindingsFromDictionary:nil];
82 [self setVerseTypes:[NSMutableArray arrayWithObjects:
83 @"\\v", @"\\p", @"\\c", @"\\s1", @"\\s2", @"\\r", @"\\mt1", @"\\mt2", @"\\mt3", @"\\is", @"\\ip", @"\\h", nil]];
84 [self setDictionary:[NSDictionary dictionary]];
86 alreadyAwokeFromNib = NO;
94 [draggedVerseIndexArray release];
95 [visibleVerseIndexes release];
96 [revisionsToConvert release];
98 [inputDefinitionPath release];
99 [scriptDefinitionPath release];
100 [encodingDefinitionPath release];
101 [convertingLock release];
102 [revisionsToConvertLock release];
103 [creationDate release];
104 [dictionary release];
111 if(!alreadyAwokeFromNib) {
112 // Keep tabs on window state so as to avoid using UI objects after window has closed.
115 // Register for dragging.
116 [versesTableView registerForDraggedTypes: [NSArray arrayWithObjects: @"RTKVersesInternalToBook", nil]];
118 [versesTableView setTarget:self];
119 //[versesTableView setDoubleAction:@selector(tableViewDoubleClicked)];
121 [versesTableView setVerticalMotionCanBeginDrag:NO];
123 NSUserDefaults * d = [NSUserDefaults standardUserDefaults];
125 if(![d boolForKey:@"RTKTransliterationOn"]) {
127 [scriptTableColumn setHidden:YES];
130 [scriptView removeFromSuperview];
132 [scriptPublishedView retain];
133 [scriptPublishedView removeFromSuperview];
137 forKeyPath:@"RTKTransliterationOn"
138 options:NSKeyValueObservingOptionNew
142 forKeyPath:@"RTKZVXSubstitution"
143 options:NSKeyValueObservingOptionNew
147 [self readSplitViewRectsFromDefaults];
150 [self ensureOneBlankVerse];
152 [self search:searchField];
153 [documentWindow makeFirstResponder:romanTextView];
155 [self setDictionary:[NSDictionary dictionary]];
159 [[romanPublishedTextView textStorage] setAttributedString:[book mutableAttributedString:YES]]; // YES = roman, NO = script
161 [[scriptPublishedTextView textStorage] setAttributedString:[book mutableAttributedString:NO]]; // YES = roman, NO = script
164 [self selectVerse:[[book verses] objectAtIndex:0]];
166 [scriptTextView setAllowEditing:NO];
168 alreadyAwokeFromNib = YES;
170 [super awakeFromNib];
175 - (BOOL)keepBackupFile
185 http://developer.apple.com/documentation/Cocoa/Conceptual/SearchFields/index.html
188 - (IBAction)search:(id)sender
190 NSString * searchString = [sender stringValue];
191 NSMutableArray * indexes = [NSMutableArray array];
192 [self setVisibleVerseIndexes:indexes];
195 if([searchString length]) {
196 NSEnumerator * e = [[book verses] objectEnumerator];
198 while(verse = (RTKVerse *)[e nextObject]) {
199 if([verse matchesString:searchString])
200 [indexes addObject:[NSNumber numberWithInt: i]];
204 int count = [[book verses] count];
205 for(i = 0; i < count; i++)
206 [indexes addObject:[NSNumber numberWithInt: i]];
209 [versesTableView noteNumberOfRowsChanged];
212 - (NSArray *)visibleVerseIndexes
214 return visibleVerseIndexes;
217 - (void)setVisibleVerseIndexes:(NSArray *)indexes
220 [visibleVerseIndexes release];
221 visibleVerseIndexes = indexes;
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
232 - (void)cut:(id)sender
234 NSIndexSet * selectedIndexes = [versesTableView selectedRowIndexes];
236 [book setVerses:(NSMutableArray *) [[book verses] arrayByRemovingObjectsAtIndexes:selectedIndexes]];
237 [self ensureOneBlankVerse];
239 [self search:searchField];
240 [versesTableView noteNumberOfRowsChanged];
241 int firstIndex = [selectedIndexes firstIndex];
242 [versesTableView selectRow:(firstIndex > 0 ? firstIndex - 1 : 0) byExtendingSelection:NO];
246 - (void)copy:(id)sender
248 NSPasteboard *pasteboard = [NSPasteboard generalPasteboard];
249 [pasteboard declareTypes:[NSArray arrayWithObject:NSStringPboardType] owner:nil];
251 NSIndexSet * selectedRowIndexes = [versesTableView selectedRowIndexes];
252 NSMutableArray * verses = [book verses];
253 NSMutableArray * selectedVerses = [verses arrayWithObjectsAtIndexes:selectedRowIndexes];
255 RTKBook * newBook = [RTKBook bookWithVerses:selectedVerses];
256 NSString * contents = [newBook string];
257 [pasteboard setString:contents forType:NSStringPboardType];
260 RTKCleverRabbitController * appController = [NSApp delegate];
261 [appController setCopiedVersesArray:[selectedVerses deepCopy]];
263 [pasteboard setData:nil forType:@"RTKBook"];
266 - (void)paste:(id)sender
268 NSPasteboard *pasteboard = [NSPasteboard generalPasteboard];
269 NSString *type = [pasteboard availableTypeFromArray:[NSArray arrayWithObject:@"RTKBook"]];
271 NSUInteger lastIndex = [[versesTableView selectedRowIndexes] lastIndex];
272 if(lastIndex == NSNotFound)
273 lastIndex = [[book verses] count];
277 RTKCleverRabbitController * appController = [NSApp delegate];
278 NSArray * pastedVerses = [[appController copiedVersesArray] deepCopy];
280 [[book verses] replaceObjectsInRange:NSMakeRange(lastIndex,0)
281 withObjectsFromArray:pastedVerses];
283 [self ensureOneBlankVerse];
284 [self search:searchField];
285 [versesTableView noteNumberOfRowsChanged];
288 [versesTableView selectRowIndexes:[NSIndexSet indexSetWithIndexesInRange:NSMakeRange(lastIndex, [pastedVerses count])] byExtendingSelection:NO];
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
300 - (void)setDraggedVerseIndexArray:(NSArray *)indexArray
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];
318 // Ok, so we aren't putting an NSData object on the pasteboard.
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.
324 // Interapplication drag and drop will have to be handled in
325 // a more standard data-copying fashion.
327 // NSLog(@"dragging");
328 // For same document...
329 [self setDraggedVerseIndexArray:rows];
331 // For between documents...
332 RTKCleverRabbitController * appController = [NSApp delegate];
333 [appController setDraggedVersesArray:[[book verses] arrayWithObjectsAtIndexes:rows]];
334 [appController setDraggedVersesOwner:self];
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 */
345 //NSLog(@"checking drop zone");
347 if(row > [[book verses] count])
348 return NSDragOperationNone;
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;
359 return NSDragOperationNone;
362 - (BOOL)tableView:(NSTableView*)aTableView
363 acceptDrop: (id <NSDraggingInfo>)item
365 dropOperation:(NSTableViewDropOperation)op
367 NSPasteboard *pboard = [item draggingPasteboard];
368 RTKCleverRabbitController * appController = [NSApp delegate];
370 if ([pboard availableTypeFromArray:[NSArray arrayWithObject: @"RTKVersesInternalToBook"]]) {
371 if([appController draggedVersesOwner] == self) {
372 NSMutableArray * verses = [book verses];
373 NSMutableArray * draggedVerses = [verses arrayWithObjectsAtIndexes:draggedVerseIndexArray];
377 verseIndex = [[visibleVerseIndexes objectAtIndex:row - 1] intValue] + 1;
379 [[verses doSelf] insertObject:[draggedVerses reverseObjectEnumerator]
382 int verseCount = [draggedVerseIndexArray count];
384 NSEnumerator * e = [draggedVerseIndexArray reverseObjectEnumerator];
386 int selectionPointCorrection = 0;
387 while(n = [e nextObject]) {
388 int i = [n intValue];
389 [verses removeObjectAtIndex:(i < row ? i : i + verseCount)];
392 selectionPointCorrection++;
394 // TODO: check if not needed
395 [versesTableView reloadData];
397 [versesTableView selectRowIndexes:[NSIndexSet indexSetWithIndexesInRange:
398 NSMakeRange((row - selectionPointCorrection), verseCount)]
399 byExtendingSelection:NO];
401 NSMutableArray * verses = [book verses];
402 NSArray * draggedVerses = [appController draggedVersesArray];
406 verseIndex = [[visibleVerseIndexes objectAtIndex:row - 1] intValue] + 1;
408 [[verses doSelf] insertObject:[draggedVerses reverseObjectEnumerator]
411 int verseCount = [draggedVerses count];
413 // TODO: check if not needed
414 [versesTableView reloadData];
416 [versesTableView selectRowIndexes:[NSIndexSet indexSetWithIndexesInRange:
417 NSMakeRange(row, verseCount)]
418 byExtendingSelection:NO];
420 // TODO: Change this when undo/redo is supported
421 [self updateChangeCount:NSChangeDone];
423 [self ensureOneBlankVerse];
424 [searchField setStringValue:@""];
425 [self search:searchField];
426 [versesTableView noteNumberOfRowsChanged];
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];
443 [romanTextView setCharacterSwaps:nil];
444 [romanPublishedTextView setCharacterSwaps:nil];
449 - (void)definitionsChanged:(id)dummy
451 if([[NSUserDefaults standardUserDefaults] boolForKey:@"RTKTransliterationOn"])
452 [self regenerateAllScript];
455 - (void)fontsChanged:(id)dummy
460 - (NSString *)windowNibName
465 - (void)windowControllerDidLoadNib:(NSWindowController *) aController
467 [super windowControllerDidLoadNib:aController];
468 [aController setShouldCloseDocument:YES];
469 [self setFieldEditor:NO];
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];
484 #pragma mark save and open
486 - (NSData *)dataOfType:(NSString *)typeName
487 error:(NSError **)outError
491 [self writeSplitViewRectsToDefaults];
493 if([typeName isEqualToString:@"rtktiger"])
495 NSMutableDictionary * dict = [NSMutableDictionary dictionaryWithDictionary:dictionary];
497 [dict setObject:[book dictionaryRepresentation] forKey:@"book"];
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"];
502 [dict setObject:[[NSDate date] description] forKey:@"RTKSaveDate"];
503 [dict setObject:[creationDate description] forKey:@"RTKCreationDate"];
505 // TODO: Should probably be using a better version format.
506 [dict setObject:[[NSBundle mainBundle] objectForInfoDictionaryKey:@"CFBundleVersion"]
507 forKey:@"RTKBuildVersion"];
509 [dict setObject:RTKDocumentWidth forKey:@"RTKDocumentWidth"];
510 [dict setObject:RTKDocumentHeight forKey:@"RTKDocumentHeight"];
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"];
520 NSUserDefaults * d = [NSUserDefaults standardUserDefaults];
522 [d setObject:RTKDocumentWidth forKey:@"RTKDocumentWidth"];
523 [d setObject:RTKDocumentHeight forKey:@"RTKDocumentHeight"];
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"];
535 data = [[dict description] dataUsingEncoding:NSUTF8StringEncoding];
536 } else if([typeName isEqualToString:@"txt"]) {
538 NSString * string = [book string];
543 NSLog(@"nil string");
545 data = [string utf8Data];
548 NSLog(@"nil data from outputData");
549 } else if([typeName isEqualToString:@"ptx"]) {
551 NSString * string = [book sfmString];
554 NSLog(@"nil sfm book");
556 NSLog(@"nil sfm string");
558 data = [string utf8Data];
561 NSLog(@"nil data from [string utf8Data]");
565 // TODO: Change this when undo/redo is supported
566 [self updateChangeCount:NSChangeCleared];
573 frame.size.width = frame.size.height = frame.origin.x = frame.origin.y = 0;
575 NSLog(@"Don't call -frame on RTKTigerDocument!");
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];
587 id object = [dict objectForKey:key];
590 // Read the setting from the document if possible.
591 [self setValue:object forKey:key];
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.
617 - (BOOL)loadDataRepresentation:(NSData *)data
618 ofType:(NSString *)aType
622 if([aType isEqualToString:@"rtktiger"]) {
623 NSDictionary * dict = [[[NSString allocWithZone:[self zone]] initWithData:data
624 encoding:NSUTF8StringEncoding] propertyList];
625 [self setDictionary:dict];
627 NSDictionary * bookDict = [dict objectForKey:@"book"];
629 [self setBook:[[[RTKBook alloc] initWithDictionary:bookDict] autorelease]];
630 [self setCreationDate:[NSDate dateWithString:[dict objectForKey:@"RTKCreationDate"]]];
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];
641 // TODO: check that width and height are within screen size
642 [self setBindingsFromDictionary:dict];
645 } else if([aType isEqualToString:@"txt"]) {
646 [self setBook:[[[RTKBook alloc] initWithString:
647 [[NSMutableString allocWithZone:[self zone]] initWithData:data
648 encoding:NSUTF8StringEncoding]] autorelease]];
650 } else if([aType isEqualToString:@"ptx"]) {
651 [self setBook:[[[RTKBook alloc] initWithSFMString:
652 [[NSMutableString allocWithZone:[self zone]] initWithData:data
653 encoding:NSUTF8StringEncoding]] autorelease]];
657 // TODO: check if not needed
658 [versesTableView reloadData];
659 if([versesTableView numberOfRows] > 0)
660 [versesTableView selectRow:0 byExtendingSelection:NO];
662 if([[NSUserDefaults standardUserDefaults] boolForKey:@"RTKTransliterationOn"])
663 [self regenerateAllScript];
669 #pragma mark accessors
671 - (void)setDictionary:(NSDictionary *)theDictionary
673 [theDictionary retain];
674 [dictionary release];
675 dictionary = theDictionary;
678 - (NSDictionary *)dictionary
688 - (void)setBook:(RTKBook *)theBook
695 - (RTKVerse *)currentVerse
700 - (void)setCurrentVerse:(RTKVerse *)verse
703 [currentVerse release];
704 currentVerse = verse;
706 [book setCurrentVerse: currentVerse];
709 - (RTKRevision *)currentRevision
711 return currentRevision;
714 - (void)setCurrentRevision:(RTKRevision *)revision
717 [currentRevision release];
718 currentRevision = revision;
720 [currentVerse setCurrentRevision:revision];
723 - (NSMutableArray *)verseTypes
728 - (void)setVerseTypes:(NSMutableArray *)theVerseTypes
730 [theVerseTypes retain];
731 [verseTypes release];
732 verseTypes = theVerseTypes;
735 - (NSDate *)creationDate
740 - (void)setCreationDate:(NSDate *)newCreationDate
742 // Don't want to lose the current creation date if none passed in.
743 if(! newCreationDate)
746 [newCreationDate retain];
747 [creationDate release];
748 creationDate = newCreationDate;
756 [self updateMenusAndButtons];
761 NSUserDefaults * d = [NSUserDefaults standardUserDefaults];
764 font = [NSFont fontWithName:(NSString *)[d valueForKey:@"RTKScriptFontName"]
765 size:[(NSString *) [d valueForKey:@"RTKScriptFontSize"] floatValue]];
766 if(font) [scriptTextView setFont:font];
768 font = [NSFont fontWithName:(NSString *)[d valueForKey:@"RTKRomanFontName"]
769 size:[(NSString *) [d valueForKey:@"RTKRomanFontSize"] floatValue]];
770 if(font) [romanTextView setFont:font];
772 font = [NSFont fontWithName:(NSString *)[d valueForKey:@"RTKBackTranslationFontName"]
773 size:[(NSString *) [d valueForKey:@"RTKBackTranslationFontSize"] floatValue]];
774 if(font) [backTranslationTextView setFont:font];
776 font = [NSFont fontWithName:(NSString *)[d valueForKey:@"RTKNotesFontName"]
777 size:[(NSString *) [d valueForKey:@"RTKNotesFontSize"] floatValue]];
778 if(font) [notesTextView setFont:font];
780 font = [NSFont fontWithName:(NSString *)[d valueForKey:@"RTKCheckingFontName"]
781 size:[(NSString *) [d valueForKey:@"RTKCheckingFontSize"] floatValue]];
782 if(font) [checkingTextView setFont:font];
784 font = [NSFont fontWithName:(NSString *)[d valueForKey:@"RTKScriptFontName"]
786 if(font) [[scriptTableColumn dataCell] setFont:font];
788 font = [NSFont fontWithName:(NSString *)[d valueForKey:@"RTKRomanFontName"]
790 if(font) [[romanTableColumn dataCell] setFont:font];
792 font = [NSFont fontWithName:(NSString *)[d valueForKey:@"RTKBackTranslationFontName"]
794 if(font) [[backTranslationTableColumn dataCell] setFont:font];
796 font = [NSFont fontWithName:(NSString *)[d valueForKey:@"RTKNotesFontName"]
798 if(font) [[notesTableColumn dataCell] setFont:font];
800 font = [NSFont fontWithName:(NSString *)[d valueForKey:@"RTKCheckingFontName"]
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];
812 if([selectedRows count] > 1) {
815 selectedRow = [selectedRows firstIndex];
818 if(selectedRow == NSNotFound) {
819 [[appController newVerseMenuItem] setEnabled:YES];
820 [[appController deleteVerseMenuItem] setEnabled:NO];
821 [deleteVerseButton setEnabled:NO];
823 [[appController newVerseMenuItem] setEnabled:YES];
824 [[appController deleteVerseMenuItem] setEnabled:YES];
825 [deleteVerseButton setEnabled:YES];
828 if(selectedRow == -1 || selectedRow == NSNotFound) {
831 [romanTextView setEditable:NO];
832 [backTranslationTextView setEditable:NO];
833 [notesTextView setEditable:NO];
834 [checkingTextView setEditable:NO];
836 [deleteRevisionButton setEnabled:NO];
837 [newRevisionButton setEnabled:NO];
839 [[appController nextVerseMenuItem] setEnabled:NO];
840 [[appController previousVerseMenuItem] setEnabled:NO];
842 [[appController newRevisionMenuItem] setEnabled:NO];
843 [[appController deleteRevisionMenuItem] setEnabled:NO];
844 [[appController nextRevisionMenuItem] setEnabled:NO];
845 [[appController previousRevisionMenuItem] setEnabled:NO];
847 [[appController lockRevisionMenuItem] setEnabled:NO];
851 BOOL verseLocked = [currentVerse locked];
852 BOOL revisionLocked = [[currentVerse currentRevision] locked];
853 int revisionIndex = [currentVerse currentRevisionIndex];
855 [[appController newVerseMenuItem] setEnabled:YES];
856 [[appController deleteVerseMenuItem] setEnabled:!verseLocked];
857 [[appController lockVerseMenuItem] setState:(verseLocked ? NSOnState : NSOffState)];
859 [deleteVerseButton setEnabled:(!verseLocked ? NSOnState : NSOffState)];
861 [referenceTableColumn setEditable:!(verseLocked || revisionLocked)];
862 [typeTableColumn setEditable:!(verseLocked || revisionLocked)];
863 [revisionTableColumn setEditable:!(verseLocked || revisionLocked)];
865 [[appController lockRevisionMenuItem] setEnabled:!verseLocked];
866 [[appController lockRevisionMenuItem] setState:(revisionLocked ? NSOnState : NSOffState)];
867 [[appController deleteRevisionMenuItem] setEnabled:!(verseLocked || revisionLocked)];
868 [[appController newRevisionMenuItem] setEnabled:!verseLocked];
870 [newRevisionButton setEnabled:!verseLocked];
871 [deleteRevisionButton setEnabled:!(verseLocked || revisionLocked)];
873 [[appController nextRevisionMenuItem] setEnabled:
874 ((revisionIndex < [currentVerse revisionCount] - 1) && !verseLocked)];
875 [[appController previousRevisionMenuItem] setEnabled:
876 ((revisionIndex > 0) && !verseLocked)];
878 [[appController nextVerseMenuItem] setEnabled:(selectedRow < [[book verses] count] - 1)];
879 [[appController previousVerseMenuItem] setEnabled:(selectedRow > 0)];
881 [romanTextView setEditable:!(verseLocked || revisionLocked)];
882 [backTranslationTextView setEditable:!(verseLocked || revisionLocked)];
883 [notesTextView setEditable:!(verseLocked || revisionLocked)];
884 [checkingTextView setEditable:!(verseLocked || revisionLocked)];
886 [referenceTableColumn setEditable:!verseLocked];
887 [typeTableColumn setEditable:!verseLocked];
888 [revisionTableColumn setEditable:!verseLocked];
893 - (void)readSplitViewRectsFromDefaults
895 NSUserDefaults * d = [NSUserDefaults standardUserDefaults];
897 if([d boolForKey:@"RTKHorizonatalSplitViewRectSaved"]) {
898 [rowView setFrame:NSRectFromString([d objectForKey:@"RTKRowViewRect"])];
899 [editView setFrame:NSRectFromString([d objectForKey:@"RTKEditViewRect"])];
902 if([d boolForKey:@"RTKVerticalSplitViewRectSaved"]) {
903 [verseView setFrame:NSRectFromString([d objectForKey:@"RTKVerseViewRect"])];
904 [publishedView setFrame:NSRectFromString([d objectForKey:@"RTKPublishedViewRect"])];
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"])];
915 [scriptPublishedView setFrame:NSRectFromString([d objectForKey:@"RTKScriptPublishedViewRectWithTransliteration"])];
916 [romanPublishedView setFrame:NSRectFromString([d objectForKey:@"RTKRomanPublishedViewRectWithTransliteration"])];
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"])];
925 [romanPublishedView setFrame:NSRectFromString([d objectForKey:@"RTKRomanPublishedViewRect"])];
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"];
937 [d setObject:[NSNumber numberWithBool:YES] forKey:@"RTKVerticalSplitViewRectSaved"];
938 [d setObject:NSStringFromRect([verseView frame]) forKey:@"RTKVerseViewRect"];
939 [d setObject:NSStringFromRect([publishedView frame]) forKey:@"RTKPublishedViewRect"];
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"];
950 [d setObject:NSStringFromRect([scriptPublishedView frame]) forKey:@"RTKScriptPublishedViewRectWithTransliteration"];
951 [d setObject:NSStringFromRect([romanPublishedView frame]) forKey:@"RTKRomanPublishedViewRectWithTransliteration"];
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"];
959 [d setObject:NSStringFromRect([romanPublishedView frame]) forKey:@"RTKRomanPublishedViewRect"];
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]];
973 if([verseNumber length] > 0) {
974 verseNumber = [NSString stringWithFormat:@"%i",
975 [verseNumber intValue] + ([precedingType isEqualToString:@"\\v"] ? 1 : 0)];
976 reference = [NSString stringWithFormat:@"%@:%@", reference, verseNumber];
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];
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];
997 [self presetVerse:verse fromPrecedingVerse:precedingVerse];
999 [versesTableView noteNumberOfRowsChanged];
1000 [versesTableView reloadData];
1001 [self search:searchField];
1006 #pragma mark menu and button handlers
1008 - (IBAction)newVerse:(id)sender
1010 NSMutableArray * verses = [book verses];
1011 int i = [versesTableView selectedRow];
1013 RTKVerse * verse = [[[RTKVerse alloc] init] autorelease];
1015 if(i == RTKNOROWSELECTED) {
1016 [verses addObject:verse];
1017 newIndex = [verses count] - 1;
1019 [verses insertObject:verse
1025 [self presetVerse:verse fromPrecedingVerse:(RTKVerse *) [verses objectAtIndex:newIndex - 1]];
1027 [searchField setStringValue:@""];
1028 [self search:searchField];
1030 [versesTableView noteNumberOfRowsChanged];
1031 [versesTableView selectRowIndexes:[[[NSIndexSet alloc] initWithIndex:newIndex] autorelease]
1032 byExtendingSelection:NO];
1034 [self ensureOneBlankVerse];
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];
1046 NSEnumerator * e = [versesTableView selectedRowEnumerator];
1047 NSMutableArray * selectedVerseArray = [NSMutableArray new];
1048 NSNumber * rowIndexNumber;
1051 while(rowIndexNumber = [e nextObject]) {
1052 rowIndex = [rowIndexNumber intValue];
1053 int verseIndex = [[visibleVerseIndexes objectAtIndex:rowIndex] intValue];
1054 [selectedVerseArray addObject:[NSNumber numberWithInt:verseIndex]];
1057 [book setVerses:(NSMutableArray *) [[book verses] arrayByRemovingObjectsAtIndexes:selectedVerseArray]];
1059 [self selectVerse:[[book verses] objectAtIndex:MIN(currentVerseIndex, [[book verses] count] -1)]];
1061 // Breaks when deleting last verse.
1062 [searchField setStringValue:@""];
1063 [self search:searchField];
1065 [versesTableView noteNumberOfRowsChanged];
1066 [self ensureOneBlankVerse];
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;
1096 currentRevision = revision;
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]];
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];
1115 while(v = [e nextObject]) {
1120 i += [[v mutableAttributedString:(textView == romanPublishedTextView)] length];
1126 - (void)updateVerse:(RTKVerse *)verse
1127 withOldIndex:(int)index
1128 inPublishedTextView:(NSTextView *)textView
1130 NSTextStorage *textStorage = [textView textStorage];
1134 if([textStorage length] <= index || [textStorage length] == 0) {
1135 [textStorage insertAttributedString:[verse mutableAttributedString:(textView == romanPublishedTextView)] atIndex:[textStorage length]];
1139 [textStorage attribute:@"RTKVerse"
1141 longestEffectiveRange:&range
1142 inRange:NSMakeRange(0, [textStorage length])];
1144 [textStorage replaceCharactersInRange:range withAttributedString:[verse mutableAttributedString:(textView == romanPublishedTextView)]];
1149 - (void)highlightVerse:(RTKVerse *)verse inTextView:(NSTextView *)textView
1151 int verseIndex = [self indexOfVerse:verse inTextView:textView];
1152 NSTextStorage *textStorage = [textView textStorage];
1156 if(verseIndex >= [textStorage length])
1159 NSRange componentRange;
1160 NSString *component = [textStorage attribute:@"RTKVerse"
1162 longestEffectiveRange:&componentRange
1163 inRange:NSMakeRange(0, [textStorage length])];
1164 if(componentRange.length == 0)
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];
1177 if(verseIndex >= [textStorage length]) {
1178 NSLog(@"verseIndex >= [textStorage length] in scrollVerseToVisible");
1182 NSRange firstComponentRange;
1183 NSString *firstComponent = [textStorage attribute:@"RTKVerse"
1185 longestEffectiveRange:&firstComponentRange
1186 inRange:NSMakeRange(0, [textStorage length])];
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];
1202 // Can't find the verse, so nothing to do.
1203 if (verseIndex == NSNotFound) return NO;
1205 // Save for next time.
1206 currentVerse = verse;
1208 // Select verse in table.
1209 [versesTableView selectRowIndexes:[NSIndexSet indexSetWithIndex:verseIndex] byExtendingSelection:NO]; // 10.3 and later.
1210 [versesTableView scrollRowToVisible:verseIndex];
1212 // Load revision into single-verse text fields.
1213 [self selectRevision:[verse currentRevision]];
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.
1219 //if([documentWindow firstResponder] != romanPublishedTextView)
1220 [[self performAfterDelay:0.3] scrollVerseToVisible:verse inTextView:romanPublishedTextView];
1222 //if([documentWindow firstResponder] != scriptPublishedTextView)
1223 [[self performAfterDelay:0.3] scrollVerseToVisible:verse inTextView:scriptPublishedTextView];
1225 // Highlight verse in published views and scroll to visible.
1226 [self highlightVerse:verse inTextView:romanPublishedTextView];
1227 [self highlightVerse:verse inTextView:scriptPublishedTextView];
1229 // Update Menus, Buttons, and Fonts
1232 // Successfully selected a different verse.
1237 - (IBAction)lockVerse:(id)sender
1239 int selectedRow = [versesTableView selectedRow];
1240 if(selectedRow >= 0) {
1241 RTKVerse * verse = [[book verses] objectAtIndex:selectedRow];
1243 [verse setLocked:![verse locked]];
1245 // TODO:Change this when undo/redo is supported
1246 [self updateChangeCount:NSChangeDone];
1248 [versesTableView reloadData];
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];
1262 [newRevision setLocked:NO];
1263 [revisions addObject:newRevision];
1264 [verse setCurrentRevisionIndex:[revisions count] -1];
1266 [versesTableView reloadData];
1269 // TODO: Change this when undo/redo is supported
1270 [self updateChangeCount:NSChangeDone];
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];
1283 if([revisions count] > 1 && ![revision locked]) {
1284 [revisions removeObjectAtIndex:currentRevisionIndex];
1286 if(currentRevisionIndex == [revisions count])
1287 [verse setCurrentRevisionIndex:currentRevisionIndex -1];
1289 NSLog(@"%@ trying to delete locked revision", sender);
1292 [versesTableView reloadData];
1295 // TODO: Change this when undo/redo is supported
1296 [self updateChangeCount:NSChangeDone];
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];
1309 if(currentRevisionIndex < revisionCount -1) {
1310 // TODO: check if not needed
1311 [versesTableView reloadData];
1312 [verse setCurrentRevisionIndex:currentRevisionIndex + 1];
1315 // TODO: Change this when undo/redo is supported
1316 [self updateChangeCount:NSChangeDone];
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];
1329 if(currentRevisionIndex > 0) {
1330 // TODO: check if not needed
1331 [versesTableView reloadData];
1332 [verse setCurrentRevisionIndex:currentRevisionIndex - 1];
1335 // TODO: Change this when undo/redo is supported
1336 [self updateChangeCount:NSChangeDone];
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;
1349 if(revisionIndex != [verse currentRevisionIndex]) {
1350 // TODO: check if not needed
1351 [versesTableView reloadData];
1352 [verse setCurrentRevisionIndex:revisionIndex];
1355 // TODO: Change this when undo/redo is supported
1356 [self updateChangeCount:NSChangeDone];
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];
1368 [revision setLocked:![revision locked]];
1370 // TODO:Change this when undo/redo is supported
1371 [self updateChangeCount:NSChangeDone];
1373 [versesTableView reloadData];
1378 #pragma mark window delegate methods
1380 - (void)windowWillClose:(NSNotification *)aNotification
1383 [convertingLock unlock];
1386 - (void)windowDidBecomeKey:(NSNotification *)aNotification
1388 [self readSplitViewRectsFromDefaults];
1391 - (void)windowDidResignKey:(NSNotification *)aNotification
1393 [self writeSplitViewRectsToDefaults];
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
1411 if(sender == horizontalSplitView) {
1413 (*max) = [[documentWindow contentView] frame].size.height - 150.0;
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.
1424 - (void)textDidChange:(NSNotification *)notification
1426 NSLog(@"textDidChange");
1427 NSTextView * changedTextView = [notification object];
1429 if(changedTextView == romanTextView) {
1431 [currentRevision setRoman:[[changedTextView string] copy]];
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];
1438 if([[NSUserDefaults standardUserDefaults] boolForKey:@"RTKTransliterationOn"])
1439 [self convertRevision:currentRevision];
1442 } else if(changedTextView == scriptTextView) {
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];
1455 NSLog(@"unhandled textview %@ sent to textDidChange", changedTextView);
1457 // TODO: Change this when undo/redo is supported
1458 [self updateChangeCount:NSChangeDone];
1460 NSRect rowRect = [versesTableView rectOfRow:[versesTableView selectedRow]];
1461 [versesTableView setNeedsDisplayInRect:rowRect];
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];
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];
1480 BOOL changeAccepted = [verse updateWithAttributedString:[textStorage attributedSubstringFromRange:NSMakeRange(0, [textStorage length])]
1481 atIndex:selectedRange.location];
1483 if(!changeAccepted) {
1484 NSLog(@"RTKVerse rejected change.");
1486 [romanTextView setString:[[verse currentRevision] roman]];
1488 if([[NSUserDefaults standardUserDefaults] boolForKey:@"RTKTransliterationOn"])
1489 [self convertRevision:[currentVerse currentRevision]];
1493 - (void)textViewDidChangeSelection:(NSNotification *)notification
1495 NSTextView * changedTextView = [notification object];
1497 if([documentWindow firstResponder] != changedTextView)
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];
1506 // Don't allow editing end of text field.
1507 if(selectedRange.location == [textStorage length]) {
1508 [(RTKTigerTextView *) changedTextView setAllowEditing:NO];
1512 // Temporary logging for testing.
1513 //NSLog(@"%@", [[textStorage attributesAtIndex:selectedRange.location effectiveRange:NULL] description]);
1515 RTKVerse *firstVerse = [textStorage attribute:@"RTKVerse"
1516 atIndex:selectedRange.location
1517 longestEffectiveRange:NULL
1518 inRange:NSMakeRange(0, [textStorage length])];
1520 RTKVerse *lastVerse = [textStorage attribute:@"RTKVerse"
1521 atIndex:(selectedRange.location + selectedRange.length)
1522 longestEffectiveRange:NULL
1523 inRange:NSMakeRange(0, [textStorage length])];
1525 NSRange firstComponentRange;
1526 NSString *firstComponent = [textStorage attribute:@"RTKVerseComponent"
1527 atIndex:selectedRange.location
1528 longestEffectiveRange:&firstComponentRange
1529 inRange:NSMakeRange(0, [textStorage length])];
1531 NSString *lastComponent = [textStorage attribute:@"RTKVerseComponent"
1532 atIndex:(selectedRange.location + selectedRange.length)
1533 longestEffectiveRange:NULL
1534 inRange:NSMakeRange(0, [textStorage length])];
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])];
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];
1551 if(firstVerse != currentVerse)
1552 [self selectVerse:firstVerse];
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];
1559 [changedTextView setTypingAttributes:[textStorage attributesAtIndex:firstComponentRange.location effectiveRange:&dummyRange]];
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];
1569 [(RTKTigerTextView *) changedTextView setAllowEditing:NO];
1574 [scriptPublishedTextView setAllowEditing:NO];
1578 - (void)reloadTableData:(id)dummy
1580 NSLog(@"- (void)reloadTableData:(id)dummy");
1581 [versesTableView reloadData];
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
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) {
1610 } else if(aTableColumn == typeTableColumn) {
1616 - (void)tableViewSelectionDidChange:(NSNotification *)aNotification
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]);
1623 NSMutableArray * verses = [book verses];
1624 int selectedRowIndex = [(NSTableView *) [aNotification object] selectedRow];
1625 if (selectedRowIndex < [verses count]) {
1626 [self selectVerse:[verses objectAtIndex:selectedRowIndex]];
1631 - (void)tableViewSelectionIsChanging:(NSNotification *)aNotification
1633 //[self ensureOneBlankVerse];
1640 #pragma mark tableSource methods
1642 - (int)numberOfRowsInTableView:(NSTableView *)aTableView
1645 if(aTableView == versesTableView) {
1646 rowCount = [visibleVerseIndexes count];
1648 NSLog(@"object %@ calling numberOfRowsInTableView", aTableView);
1653 - (id)tableView:(NSTableView *)aTableView
1654 objectValueForTableColumn:(NSTableColumn *)aTableColumn
1657 NSUserDefaults * d = [NSUserDefaults standardUserDefaults];
1659 NSParameterAssert(rowIndex >= 0 && rowIndex < [visibleVerseIndexes count]);
1660 rowIndex = [(NSNumber *)[visibleVerseIndexes objectAtIndex:rowIndex] intValue];
1662 NSString * value = @"default value";
1664 NSMutableArray * verses = [book verses];
1666 RTKVerse * verse = [verses objectAtIndex:rowIndex];
1667 int revisionIndex = [verse currentRevisionIndex];
1668 NSMutableArray * revisions = [verse revisions];
1669 RTKRevision * revision = [revisions objectAtIndex:revisionIndex];
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];
1693 NSLog(@"unhandled column %@ for tableView:objectValueForTableColumn:row:", aTableColumn);
1696 NSLog(@"unhandled object %@ calling tableView:objectValueForTableColumn:row:", aTableView);
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
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]))
1720 rowIndex = [(NSNumber *)[visibleVerseIndexes objectAtIndex:rowIndex] intValue];
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];
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];
1754 NSLog(@"unhandled column %@ for setObjectValue:ForTableColumn:Row", aTableColumn);
1757 NSLog(@"unhandled object %@ calling setObjectValue:ForTableColumn:Row", aTableView);
1760 // TODO: Change this when undo/redo is supported
1761 [self updateChangeCount:NSChangeDone];
1763 [self ensureOneBlankVerse];
1768 Provide popup menus in the type and revision columns.
1769 http://members.shaw.ca/akochoi/2003/10-05/index.html#1
1771 - (void)tableView:(NSTableView *)aTableView
1772 willDisplayCell:(id)aCell
1773 forTableColumn:(NSTableColumn *)aTableColumn
1776 NSParameterAssert(rowIndex >= 0 && rowIndex < [visibleVerseIndexes count]);
1777 rowIndex = [(NSNumber *)[visibleVerseIndexes objectAtIndex:rowIndex] intValue];
1778 RTKVerse * verse = [[book verses] objectAtIndex:rowIndex];
1780 if(aTableColumn == revisionTableColumn) {
1781 NSMutableArray * revisions = [verse revisions];
1782 int revisionCount = [revisions count];
1783 int revisionIndex = [verse currentRevisionIndex];
1785 [(NSPopUpButtonCell *)aCell removeAllItems];
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];
1793 [(NSPopUpButtonCell *)aCell selectItemAtIndex:revisionIndex];
1794 } else if(aTableColumn == typeTableColumn) {
1795 NSString * type = [verse type];
1797 [(NSComboBoxCell *)aCell removeAllItems];
1798 [(NSComboBoxCell *)aCell addItemsWithObjectValues:verseTypes];
1799 [(NSComboBoxCell *)aCell selectItemWithObjectValue:type];
1800 [(NSComboBoxCell *)aCell setStringValue:type];
1802 } else if(aTableColumn == lockedTableColumn) {
1803 [(NSButtonCell *)aCell setState:([verse locked] ? NSOnState : NSOffState)];
1808 #pragma mark Script Conversion
1810 - (void)transcriptionTypeChanged:(id)dummy
1812 if([[NSUserDefaults standardUserDefaults] boolForKey:@"RTKTransliterationOn"])
1813 [self regenerateAllScript];
1816 -(void)observeValueForKeyPath:(NSString *)keyPath
1818 change:(NSDictionary *)change
1819 context:(void *)context
1821 NSLog(@"KVO: %@ changed property %@ to value %@", object, keyPath, change);
1823 if ([keyPath compare:@"RTKTransliterationOn"] == NSOrderedSame) {
1824 [self transliterationOnChanged];
1826 } else if ([keyPath compare:@"RTKZVXSubstitution"] == NSOrderedSame) {
1827 [self substituteZVXChanged];
1833 - (void)transliterationOnChanged
1835 if([[NSUserDefaults standardUserDefaults] boolForKey:@"RTKTransliterationOn"]) {
1837 [scriptTableColumn setHidden:NO];
1839 [splitViewOfTextViews addSubview:scriptView
1840 positioned:NSWindowBelow
1841 relativeTo:romanView];
1842 [scriptView release];
1844 [publishedSplitView addSubview:scriptPublishedView
1845 positioned:NSWindowAbove
1846 relativeTo:romanPublishedView];
1847 [scriptPublishedView release];
1848 [scriptPublishedView setHidden:NO];
1850 [self regenerateAllScript];
1854 [scriptTableColumn setHidden:YES];
1856 [scriptView retain];
1857 [scriptView removeFromSuperview];
1860 [scriptPublishedView retain];
1861 [scriptPublishedView removeFromSuperview];
1862 [scriptPublishedView setHidden:YES];
1865 [self readSplitViewRectsFromDefaults];
1866 [self writeSplitViewRectsToDefaults];
1868 [self scrollVerseToVisible:currentVerse inTextView:romanPublishedTextView];
1869 [self scrollVerseToVisible:currentVerse inTextView:scriptPublishedTextView];
1874 - (void)mainThreadUpdateUI:(id)dummy
1878 NSRect rowRect = [versesTableView rectOfRow:[versesTableView selectedRow]];
1879 [versesTableView setNeedsDisplayInRect:rowRect];
1881 [scriptTextView setString:[[currentVerse currentRevision] script]];
1886 - (void)regenerateAllScript
1888 NSEnumerator * verseEnumerator = [[book verses] objectEnumerator];
1891 while(verse = [verseEnumerator nextObject]) {
1892 [[self doSelf] convertRevision:[[verse revisions] each]];
1897 - (void)convertRevision:(RTKRevision *)revision
1899 [revisionsToConvertLock lock];
1902 NSUserDefaults * d = [NSUserDefaults standardUserDefaults];
1903 NSString * fontOutputString = nil;
1905 NSString * transcriptionType = [d valueForKey:@"RTKTranscriptionType"];
1906 fontOutputString = transcriptionType;
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];
1919 if(generateMetaStrings)
1920 NSLog(@"%@", [output description]);
1922 fontOutputString = [output objectForKey:@"RTKFont"];
1925 if(fontOutputString)
1926 [revision setScript:fontOutputString];
1928 if(revision == currentRevision) {
1929 [scriptTextView setString:[revision script]];
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];
1937 [revisionsToConvertLock unlock];