home *** CD-ROM | disk | FTP | other *** search
/ Big Green CD 8 / BGCD_8_Dev.iso / NEXTSTEP / Utilities / NCCompletionDictionary-2.6 / NCCompletionDictionary.m < prev    next >
Encoding:
Text File  |  1997-08-10  |  11.3 KB  |  356 lines

  1.  
  2. // NCCompletionDictionary.m
  3. //
  4. // Written by Norbert Heger <bertl@hal.kph.tuwien.ac.at>
  5. // Copyright (c)1997
  6. //
  7. // This file is distributed under the terms of the
  8. // GNU General Public License.
  9.  
  10. #import "NCCompletionDictionary.h"
  11. #import "NCRuntimeHacks.h"
  12. #import "NCTextView.h"
  13.  
  14. #define FILENAME @"~/Library/KeyBindings/CompletionKeyBinding.dict"
  15.  
  16. @implementation NCCompletionDictionary
  17.  
  18. static NSString *filename = nil;
  19. static NSMutableDictionary *completionDictionary = nil;
  20. static NSMutableArray *undoArray = nil;
  21. static NSMutableArray *redoArray = nil;
  22. static NSObject *nilObject = nil;
  23.  
  24. // ----------------------------------------------------------------------------------------
  25. // Class methods
  26. // ----------------------------------------------------------------------------------------
  27.  
  28. + (NSDictionary *)dictionary { return completionDictionary; }
  29. + (void)load { [[self alloc] init]; }
  30.  
  31. + (void)createDirectoryIfNecessary:(NSString *)path
  32. {
  33.     NSFileManager *manager = [NSFileManager defaultManager];
  34.     BOOL isDirectory;
  35.     BOOL fileExists = [manager fileExistsAtPath:path isDirectory:&isDirectory];
  36.  
  37.     if (!fileExists) {
  38.         [manager createDirectoryAtPath:path attributes:nil];
  39.     } else if (!isDirectory) {
  40.         NSString *shortPath = [path stringByAbbreviatingWithTildeInPath];
  41.         NSRunAlertPanel(@"Alert", @"%@ isn't a directory", @"OK", nil, nil, shortPath);
  42.     }
  43. }
  44.  
  45. + (void)initialize
  46. {
  47.     if (self == [NCCompletionDictionary class]) {
  48.         filename = [[FILENAME stringByExpandingTildeInPath] retain];
  49.         [self createDirectoryIfNecessary:[filename stringByDeletingLastPathComponent]];
  50.         completionDictionary = [[NSMutableDictionary alloc] init];
  51.         [completionDictionary addEntriesFromDictionary:[NSDictionary dictionaryWithContentsOfFile:filename]];
  52.  
  53.         undoArray = [[NSMutableArray alloc] init];
  54.         redoArray = [[NSMutableArray alloc] init];
  55.         nilObject = [[NSObject alloc] init];
  56.     }
  57. }
  58.  
  59. // ----------------------------------------------------------------------------------------
  60. // Initializing the instance
  61. // ----------------------------------------------------------------------------------------
  62.  
  63. - (void)initMenu
  64. {
  65.     NSString *title = @"Completion";
  66.     SEL action = @selector(showPanel:);
  67.  
  68.     NSMenu *submenu = [[NSMenu alloc] initWithTitle:title];
  69.     NSMenu *toolsMenu = [[[NSApp mainMenu] itemWithTitle:@"Tools"] target];
  70.     NSMenuItem *subMenuItem = [toolsMenu addItemWithTitle:title action:0 keyEquivalent:@""];
  71.  
  72.     [[submenu addItemWithTitle:@"Show Panel..." action:action keyEquivalent:@"E"] setTarget:self];
  73.     [[submenu addItemWithTitle:@"Import from Edit" action:@selector(import:) keyEquivalent:@""] setTarget:self];
  74.  
  75.     [toolsMenu setSubmenu:submenu forItem:subMenuItem];
  76. }
  77.  
  78. - (void)updateSortedKeys
  79. {
  80.     [sortedKeys release];
  81.        sortedKeys = [[completionDictionary allKeys] sortedArrayUsingSelector:@selector(compare:)];
  82.     [sortedKeys retain];
  83. }
  84.  
  85. - (id)init
  86. {
  87.     [super init];
  88.     [self initMenu];
  89.     [self updateSortedKeys];
  90.  
  91.     [NSBundle loadNibNamed:@"NCCompletionDictionary" owner:self];
  92.     [window setFrameAutosaveName:@"NCCompletionDictionary"];
  93.     [window setFrameUsingName:@"NCCompletionDictionary"];
  94.  
  95.     [textView setClass:[NCTextView class]]; // PB BugFix
  96.  
  97.     return self;
  98. }
  99.  
  100. // ----------------------------------------------------------------------------------------
  101. // Writing changes to file
  102. // ----------------------------------------------------------------------------------------
  103.  
  104. - (void)commitChanges
  105. {
  106.     [self updateSortedKeys];
  107.     [browser reloadColumn:0];
  108.     [completionDictionary writeToFile:filename atomically:YES];
  109.     [window setDocumentEdited:NO];
  110. }
  111.  
  112. // ----------------------------------------------------------------------------------------
  113. // Hmm, there's no documented way to create multiple selections...
  114. // ----------------------------------------------------------------------------------------
  115.  
  116. - (void)selectKeys:(NSArray *)keys
  117. {
  118.     NSMatrix *matrix = [browser matrixInColumn:0];
  119.     unsigned int i, count = [keys count];
  120.  
  121.     keys = [keys sortedArrayUsingSelector:@selector(compare:)];
  122.     for (i = 0; i < count; i++) {
  123.         NSString *key = [keys objectAtIndex:i];
  124.         if (i == 0) {
  125.             [browser setPath:key];
  126.         } else {
  127.             int index = [sortedKeys indexOfObject:key];
  128.             if (index >= 0) { [[matrix cellAtRow:index column:0] set]; }
  129.         }
  130.     }
  131.     [window makeFirstResponder:browser];
  132.     [self select:self];
  133. }
  134.  
  135. // ----------------------------------------------------------------------------------------
  136. // Undo / Redo
  137. // ----------------------------------------------------------------------------------------
  138.  
  139. - (void)preserveObjectsForKeys:(NSArray *)keys inArray:(NSMutableArray *)array
  140. {
  141.     NSArray *objects = [completionDictionary objectsForKeys:keys notFoundMarker:nilObject];
  142.     [array addObject:[NSDictionary dictionaryWithObjects:objects forKeys:keys]];
  143. }
  144.  
  145. - (void)preserveObjectsForKeys:(NSArray *)keys
  146. {
  147.     [self preserveObjectsForKeys:keys inArray:undoArray];
  148.     [redoArray removeAllObjects];
  149. }
  150.  
  151. - (void)performUndo:(BOOL)undo
  152. {
  153.     NSMutableArray *array = undo ? undoArray : redoArray;
  154.  
  155.     if ([array count] > 0) {
  156.         NSDictionary *dictionary = [array lastObject];
  157.         NSArray *keys = [dictionary allKeys];
  158.         unsigned int i, count = [keys count];
  159.  
  160.         [self preserveObjectsForKeys:keys inArray:(undo ? redoArray : undoArray)];
  161.  
  162.         for (i = 0; i < count; i++) {
  163.             NSString *key = [keys objectAtIndex:i];
  164.             id object = [dictionary objectForKey:key];
  165.  
  166.             if (object == nilObject) { [completionDictionary removeObjectForKey:key]; }
  167.             else { [completionDictionary setObject:object forKey:key]; }
  168.         }
  169.         [self commitChanges];
  170.         [self selectKeys:keys];
  171.         [array removeLastObject];
  172.  
  173.     } else { NSBeep(); }
  174. }
  175.  
  176. - (void)undo:(id)sender { [self performUndo:YES]; }
  177. - (void)redo:(id)sender { [self performUndo:NO]; }
  178.  
  179. - (BOOL)validateMenuItem:(NSMenuItem *)anItem
  180. {
  181.     SEL action = [anItem action];
  182.     if (action == @selector(undo:)) { return [undoArray count] > 0; }
  183.     if (action == @selector(redo:)) { return [redoArray count] > 0; }
  184.     return YES;
  185. }
  186.  
  187. // ----------------------------------------------------------------------------------------
  188. // Controller
  189. // ----------------------------------------------------------------------------------------
  190.  
  191. - (void)showPanel:(id)sender
  192. {
  193.     [window makeKeyAndOrderFront:self];
  194. }
  195.  
  196. - (void)set:(id)sender
  197. {
  198.     NSString *key = [keyTextField stringValue];
  199.  
  200.     [self preserveObjectsForKeys:[NSArray arrayWithObject:key]];
  201.     [completionDictionary setObject:[[textView string] copy] forKey:key];
  202.     [self commitChanges];
  203.     [browser setPath:key];
  204.     [keyTextField selectText:self];
  205. }
  206.  
  207. - (void)doRemove
  208. {
  209.     NSArray *cells = [browser selectedCells];
  210.     NSMutableArray *keys = [NSMutableArray array];
  211.     unsigned int i, count = [cells count];
  212.  
  213.     for (i = 0; i < count; i++) {
  214.         [keys addObject:[[cells objectAtIndex:i] stringValue]];
  215.     }
  216.     [self preserveObjectsForKeys:keys];
  217.     [completionDictionary removeObjectsForKeys:keys];
  218.     [self commitChanges];
  219.     [keyTextField selectText:self];
  220. }
  221.  
  222. - (void)remove:(id)sender
  223. {
  224.     if ([[browser selectedCells] count] > 0) { [self doRemove]; }
  225.     else { NSBeep(); }
  226. }
  227.  
  228. - (void)select:(id)sender
  229. {
  230.     NSArray *cells = [browser selectedCells];
  231.  
  232.     if ([cells count] == 1) {
  233.         NSString *key = [[cells lastObject] stringValue];
  234.         [keyTextField setStringValue:key];
  235.         [textView setString:[completionDictionary objectForKey:key]];
  236.     } else {
  237.         [keyTextField setStringValue:@""];
  238.         [textView setString:@""];
  239.     }
  240.     [window setDocumentEdited:NO];
  241. }
  242.  
  243. // ----------------------------------------------------------------------------------------
  244. // Import from .editdict
  245. // ----------------------------------------------------------------------------------------
  246.  
  247. - (NSDictionary *)dictionaryFromEdit
  248. {
  249.     NSString *path = [@"~/.editdict" stringByExpandingTildeInPath];
  250.     NSString *contents = [NSString stringWithContentsOfFile:path];
  251.     NSMutableDictionary *dictionary = nil;
  252.     
  253.     if (contents != nil) {
  254.         const char *cString = [contents cString];
  255.         const char *p = cString;
  256.  
  257.         dictionary = [NSMutableDictionary dictionary];
  258.  
  259.         while (*p != 0) {
  260.             NSString *key;
  261.             NSMutableString *value = [NSMutableString string];
  262.             const char *keyStart = p;
  263.  
  264.             while (*p != 0 && *p != '\t') p++;
  265.             key = [NSString stringWithCString:keyStart length:(p - keyStart)];
  266.             if (*p != 0) {
  267.                 BOOL escaped = NO; char c;
  268.                 p++;
  269.                 while ((c = *p++) != 0) {
  270.                     if (c == '\\') {
  271.                         escaped = !escaped;
  272.                     } else {
  273.                         if (c == '\n' && !escaped) { break; }
  274.                         escaped = NO;
  275.                     }
  276.                     if (!escaped) { [value appendFormat:@"%c", c]; }
  277.                 }
  278.                 if ([key length] && [value length]) {
  279.                     [dictionary setObject:value forKey:key];
  280.                 }
  281.             }
  282.         }
  283.     }
  284.     return dictionary;
  285. }
  286.  
  287. - (void)importFromEdit
  288. {
  289.     NSDictionary *dictionary = [self dictionaryFromEdit];
  290.     if (dictionary != nil) {
  291.         [self preserveObjectsForKeys:[dictionary allKeys]];
  292.         [completionDictionary addEntriesFromDictionary:dictionary];
  293.         [self commitChanges];
  294.         [self selectKeys:[dictionary allKeys]];
  295.     }
  296. }
  297.  
  298. - (void)import:(id)sender
  299. {
  300.     NSString *message = @"Do you really want to import the Completion Dictionary settings from ~/.editdict?";
  301.  
  302.     if (NSRunAlertPanel(@"Import", message, @"Import", @"Cancel", nil) == NSAlertDefaultReturn) {
  303.         [self importFromEdit];
  304.     }
  305. }
  306.  
  307. // ----------------------------------------------------------------------------------------
  308. // NSText Delegate
  309. // ----------------------------------------------------------------------------------------
  310.  
  311. - (void)controlTextDidChange:(NSNotification *)aNotification
  312. {
  313.     if ([aNotification object] == keyTextField) {
  314.         NSString *key = [keyTextField stringValue];
  315.         if ([completionDictionary objectForKey:key] != nil) {
  316.             [browser setPath:key];
  317.         }
  318.     }
  319. }
  320.  
  321. - (void)textDidChange:(NSNotification *)aNotification
  322. {
  323.     if ([aNotification object] == textView) {
  324.         [window setDocumentEdited:YES];
  325.     }
  326. }
  327.  
  328. // ----------------------------------------------------------------------------------------
  329. // NSBrowser Delegate
  330. // ----------------------------------------------------------------------------------------
  331.  
  332. - (int)browser:(NSBrowser *)sender numberOfRowsInColumn:(int)column
  333. {
  334.     return column == 0 ? [sortedKeys count] : 0;
  335. }
  336.  
  337. - (void)browser:(NSBrowser *)sender willDisplayCell:(id)cell atRow:(int)row column:(int)column
  338. {
  339.     if (column == 0) {
  340.         [cell setStringValue:[sortedKeys objectAtIndex:row]];
  341.         [cell setLeaf:YES];
  342.     }
  343. }
  344.  
  345. // ----------------------------------------------------------------------------------------
  346. // NSWindow Delegate
  347. // ----------------------------------------------------------------------------------------
  348.  
  349. - (void)windowDidUpdate:(NSNotification *)aNotification
  350. {
  351.     [[buttonMatrix cellAtRow:0 column:0] setEnabled:[[browser selectedCells] count] > 0];
  352.     [[buttonMatrix cellAtRow:0 column:1] setEnabled:[[keyTextField stringValue] length] > 0];
  353. }
  354.  
  355. @end
  356.