home *** CD-ROM | disk | FTP | other *** search
-
- // NCCompletionDictionary.m
- //
- // Written by Norbert Heger <bertl@hal.kph.tuwien.ac.at>
- // Copyright (c)1997
- //
- // This file is distributed under the terms of the
- // GNU General Public License.
-
- #import "NCCompletionDictionary.h"
- #import "NCRuntimeHacks.h"
- #import "NCTextView.h"
-
- #define FILENAME @"~/Library/KeyBindings/CompletionKeyBinding.dict"
-
- @implementation NCCompletionDictionary
-
- static NSString *filename = nil;
- static NSMutableDictionary *completionDictionary = nil;
- static NSMutableArray *undoArray = nil;
- static NSMutableArray *redoArray = nil;
- static NSObject *nilObject = nil;
-
- // ----------------------------------------------------------------------------------------
- // Class methods
- // ----------------------------------------------------------------------------------------
-
- + (NSDictionary *)dictionary { return completionDictionary; }
- + (void)load { [[self alloc] init]; }
-
- + (void)createDirectoryIfNecessary:(NSString *)path
- {
- NSFileManager *manager = [NSFileManager defaultManager];
- BOOL isDirectory;
- BOOL fileExists = [manager fileExistsAtPath:path isDirectory:&isDirectory];
-
- if (!fileExists) {
- [manager createDirectoryAtPath:path attributes:nil];
- } else if (!isDirectory) {
- NSString *shortPath = [path stringByAbbreviatingWithTildeInPath];
- NSRunAlertPanel(@"Alert", @"%@ isn't a directory", @"OK", nil, nil, shortPath);
- }
- }
-
- + (void)initialize
- {
- if (self == [NCCompletionDictionary class]) {
- filename = [[FILENAME stringByExpandingTildeInPath] retain];
- [self createDirectoryIfNecessary:[filename stringByDeletingLastPathComponent]];
- completionDictionary = [[NSMutableDictionary alloc] init];
- [completionDictionary addEntriesFromDictionary:[NSDictionary dictionaryWithContentsOfFile:filename]];
-
- undoArray = [[NSMutableArray alloc] init];
- redoArray = [[NSMutableArray alloc] init];
- nilObject = [[NSObject alloc] init];
- }
- }
-
- // ----------------------------------------------------------------------------------------
- // Initializing the instance
- // ----------------------------------------------------------------------------------------
-
- - (void)initMenu
- {
- NSString *title = @"Completion";
- SEL action = @selector(showPanel:);
-
- NSMenu *submenu = [[NSMenu alloc] initWithTitle:title];
- NSMenu *toolsMenu = [[[NSApp mainMenu] itemWithTitle:@"Tools"] target];
- NSMenuItem *subMenuItem = [toolsMenu addItemWithTitle:title action:0 keyEquivalent:@""];
-
- [[submenu addItemWithTitle:@"Show Panel..." action:action keyEquivalent:@"E"] setTarget:self];
- [[submenu addItemWithTitle:@"Import from Edit" action:@selector(import:) keyEquivalent:@""] setTarget:self];
-
- [toolsMenu setSubmenu:submenu forItem:subMenuItem];
- }
-
- - (void)updateSortedKeys
- {
- [sortedKeys release];
- sortedKeys = [[completionDictionary allKeys] sortedArrayUsingSelector:@selector(compare:)];
- [sortedKeys retain];
- }
-
- - (id)init
- {
- [super init];
- [self initMenu];
- [self updateSortedKeys];
-
- [NSBundle loadNibNamed:@"NCCompletionDictionary" owner:self];
- [window setFrameAutosaveName:@"NCCompletionDictionary"];
- [window setFrameUsingName:@"NCCompletionDictionary"];
-
- [textView setClass:[NCTextView class]]; // PB BugFix
-
- return self;
- }
-
- // ----------------------------------------------------------------------------------------
- // Writing changes to file
- // ----------------------------------------------------------------------------------------
-
- - (void)commitChanges
- {
- [self updateSortedKeys];
- [browser reloadColumn:0];
- [completionDictionary writeToFile:filename atomically:YES];
- [window setDocumentEdited:NO];
- }
-
- // ----------------------------------------------------------------------------------------
- // Hmm, there's no documented way to create multiple selections...
- // ----------------------------------------------------------------------------------------
-
- - (void)selectKeys:(NSArray *)keys
- {
- NSMatrix *matrix = [browser matrixInColumn:0];
- unsigned int i, count = [keys count];
-
- keys = [keys sortedArrayUsingSelector:@selector(compare:)];
- for (i = 0; i < count; i++) {
- NSString *key = [keys objectAtIndex:i];
- if (i == 0) {
- [browser setPath:key];
- } else {
- int index = [sortedKeys indexOfObject:key];
- if (index >= 0) { [[matrix cellAtRow:index column:0] set]; }
- }
- }
- [window makeFirstResponder:browser];
- [self select:self];
- }
-
- // ----------------------------------------------------------------------------------------
- // Undo / Redo
- // ----------------------------------------------------------------------------------------
-
- - (void)preserveObjectsForKeys:(NSArray *)keys inArray:(NSMutableArray *)array
- {
- NSArray *objects = [completionDictionary objectsForKeys:keys notFoundMarker:nilObject];
- [array addObject:[NSDictionary dictionaryWithObjects:objects forKeys:keys]];
- }
-
- - (void)preserveObjectsForKeys:(NSArray *)keys
- {
- [self preserveObjectsForKeys:keys inArray:undoArray];
- [redoArray removeAllObjects];
- }
-
- - (void)performUndo:(BOOL)undo
- {
- NSMutableArray *array = undo ? undoArray : redoArray;
-
- if ([array count] > 0) {
- NSDictionary *dictionary = [array lastObject];
- NSArray *keys = [dictionary allKeys];
- unsigned int i, count = [keys count];
-
- [self preserveObjectsForKeys:keys inArray:(undo ? redoArray : undoArray)];
-
- for (i = 0; i < count; i++) {
- NSString *key = [keys objectAtIndex:i];
- id object = [dictionary objectForKey:key];
-
- if (object == nilObject) { [completionDictionary removeObjectForKey:key]; }
- else { [completionDictionary setObject:object forKey:key]; }
- }
- [self commitChanges];
- [self selectKeys:keys];
- [array removeLastObject];
-
- } else { NSBeep(); }
- }
-
- - (void)undo:(id)sender { [self performUndo:YES]; }
- - (void)redo:(id)sender { [self performUndo:NO]; }
-
- - (BOOL)validateMenuItem:(NSMenuItem *)anItem
- {
- SEL action = [anItem action];
- if (action == @selector(undo:)) { return [undoArray count] > 0; }
- if (action == @selector(redo:)) { return [redoArray count] > 0; }
- return YES;
- }
-
- // ----------------------------------------------------------------------------------------
- // Controller
- // ----------------------------------------------------------------------------------------
-
- - (void)showPanel:(id)sender
- {
- [window makeKeyAndOrderFront:self];
- }
-
- - (void)set:(id)sender
- {
- NSString *key = [keyTextField stringValue];
-
- [self preserveObjectsForKeys:[NSArray arrayWithObject:key]];
- [completionDictionary setObject:[[textView string] copy] forKey:key];
- [self commitChanges];
- [browser setPath:key];
- [keyTextField selectText:self];
- }
-
- - (void)doRemove
- {
- NSArray *cells = [browser selectedCells];
- NSMutableArray *keys = [NSMutableArray array];
- unsigned int i, count = [cells count];
-
- for (i = 0; i < count; i++) {
- [keys addObject:[[cells objectAtIndex:i] stringValue]];
- }
- [self preserveObjectsForKeys:keys];
- [completionDictionary removeObjectsForKeys:keys];
- [self commitChanges];
- [keyTextField selectText:self];
- }
-
- - (void)remove:(id)sender
- {
- if ([[browser selectedCells] count] > 0) { [self doRemove]; }
- else { NSBeep(); }
- }
-
- - (void)select:(id)sender
- {
- NSArray *cells = [browser selectedCells];
-
- if ([cells count] == 1) {
- NSString *key = [[cells lastObject] stringValue];
- [keyTextField setStringValue:key];
- [textView setString:[completionDictionary objectForKey:key]];
- } else {
- [keyTextField setStringValue:@""];
- [textView setString:@""];
- }
- [window setDocumentEdited:NO];
- }
-
- // ----------------------------------------------------------------------------------------
- // Import from .editdict
- // ----------------------------------------------------------------------------------------
-
- - (NSDictionary *)dictionaryFromEdit
- {
- NSString *path = [@"~/.editdict" stringByExpandingTildeInPath];
- NSString *contents = [NSString stringWithContentsOfFile:path];
- NSMutableDictionary *dictionary = nil;
-
- if (contents != nil) {
- const char *cString = [contents cString];
- const char *p = cString;
-
- dictionary = [NSMutableDictionary dictionary];
-
- while (*p != 0) {
- NSString *key;
- NSMutableString *value = [NSMutableString string];
- const char *keyStart = p;
-
- while (*p != 0 && *p != '\t') p++;
- key = [NSString stringWithCString:keyStart length:(p - keyStart)];
- if (*p != 0) {
- BOOL escaped = NO; char c;
- p++;
- while ((c = *p++) != 0) {
- if (c == '\\') {
- escaped = !escaped;
- } else {
- if (c == '\n' && !escaped) { break; }
- escaped = NO;
- }
- if (!escaped) { [value appendFormat:@"%c", c]; }
- }
- if ([key length] && [value length]) {
- [dictionary setObject:value forKey:key];
- }
- }
- }
- }
- return dictionary;
- }
-
- - (void)importFromEdit
- {
- NSDictionary *dictionary = [self dictionaryFromEdit];
- if (dictionary != nil) {
- [self preserveObjectsForKeys:[dictionary allKeys]];
- [completionDictionary addEntriesFromDictionary:dictionary];
- [self commitChanges];
- [self selectKeys:[dictionary allKeys]];
- }
- }
-
- - (void)import:(id)sender
- {
- NSString *message = @"Do you really want to import the Completion Dictionary settings from ~/.editdict?";
-
- if (NSRunAlertPanel(@"Import", message, @"Import", @"Cancel", nil) == NSAlertDefaultReturn) {
- [self importFromEdit];
- }
- }
-
- // ----------------------------------------------------------------------------------------
- // NSText Delegate
- // ----------------------------------------------------------------------------------------
-
- - (void)controlTextDidChange:(NSNotification *)aNotification
- {
- if ([aNotification object] == keyTextField) {
- NSString *key = [keyTextField stringValue];
- if ([completionDictionary objectForKey:key] != nil) {
- [browser setPath:key];
- }
- }
- }
-
- - (void)textDidChange:(NSNotification *)aNotification
- {
- if ([aNotification object] == textView) {
- [window setDocumentEdited:YES];
- }
- }
-
- // ----------------------------------------------------------------------------------------
- // NSBrowser Delegate
- // ----------------------------------------------------------------------------------------
-
- - (int)browser:(NSBrowser *)sender numberOfRowsInColumn:(int)column
- {
- return column == 0 ? [sortedKeys count] : 0;
- }
-
- - (void)browser:(NSBrowser *)sender willDisplayCell:(id)cell atRow:(int)row column:(int)column
- {
- if (column == 0) {
- [cell setStringValue:[sortedKeys objectAtIndex:row]];
- [cell setLeaf:YES];
- }
- }
-
- // ----------------------------------------------------------------------------------------
- // NSWindow Delegate
- // ----------------------------------------------------------------------------------------
-
- - (void)windowDidUpdate:(NSNotification *)aNotification
- {
- [[buttonMatrix cellAtRow:0 column:0] setEnabled:[[browser selectedCells] count] > 0];
- [[buttonMatrix cellAtRow:0 column:1] setEnabled:[[keyTextField stringValue] length] > 0];
- }
-
- @end
-