1/* MDIndexing.m 2 * 3 * Copyright (C) 2006-2013 Free Software Foundation, Inc. 4 * 5 * Author: Enrico Sersale <enrico@imago.ro> 6 * Date: February 2006 7 * 8 * This file is part of the GNUstep GWorkspace application 9 * 10 * This program is free software; you can redistribute it and/or modify 11 * it under the terms of the GNU General Public License as published by 12 * the Free Software Foundation; either version 2 of the License, or 13 * (at your option) any later version. 14 * 15 * This program is distributed in the hope that it will be useful, 16 * but WITHOUT ANY WARRANTY; without even the implied warranty of 17 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 18 * GNU General Public License for more details. 19 * 20 * You should have received a copy of the GNU General Public License 21 * along with this program; if not, write to the Free Software 22 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02111 USA. 23 */ 24 25#import <AppKit/AppKit.h> 26#import "MDIndexing.h" 27#import "CategoriesEditor.h" 28#import "StartAppWin.h" 29 30BOOL subPathOfPath(NSString *p1, NSString *p2); 31 32BOOL isDotFile(NSString *path); 33 34 35@implementation MDIndexing 36 37- (void)dealloc 38{ 39 if (statusTimer && [statusTimer isValid]) { 40 [statusTimer invalidate]; 41 } 42 DESTROY (statusTimer); 43 44 TEST_RELEASE (indexedPaths); 45 TEST_RELEASE (excludedPaths); 46 TEST_RELEASE (excludedSuffixes); 47 TEST_RELEASE (startAppWin); 48 TEST_RELEASE (indexedStatusPath); 49 TEST_RELEASE (indexedStatusLock); 50 TEST_RELEASE (statusWindow); 51 TEST_RELEASE (errorLogPath); 52 TEST_RELEASE (errorWindow); 53 54 [super dealloc]; 55} 56 57- (void)mainViewDidLoad 58{ 59 if (loaded == NO) { 60 id cell; 61 CGFloat fonth; 62 int index; 63 NSString *str; 64 NSUInteger i; 65 66 fm = [NSFileManager defaultManager]; 67 nc = [NSNotificationCenter defaultCenter]; 68 dnc = [NSDistributedNotificationCenter defaultCenter]; 69 70 indexedPaths = [NSMutableArray new]; 71 excludedPaths = [NSMutableArray new]; 72 excludedSuffixes = [NSMutableArray new]; 73 74 [self readDefaults]; 75 76 index = [tabView indexOfTabViewItemWithIdentifier: @"paths"]; 77 [[tabView tabViewItemAtIndex: index] setLabel: NSLocalizedString(@"Paths", @"")]; 78 79 index = [tabView indexOfTabViewItemWithIdentifier: @"results"]; 80 [[tabView tabViewItemAtIndex: index] setLabel: NSLocalizedString(@"Search Results", @"")]; 81 82 [indexedScroll setBorderType: NSBezelBorder]; 83 [indexedScroll setHasHorizontalScroller: YES]; 84 [indexedScroll setHasVerticalScroller: YES]; 85 86 cell = [NSBrowserCell new]; 87 fonth = [[cell font] defaultLineHeightForFont]; 88 89 indexedMatrix = [[NSMatrix alloc] initWithFrame: NSMakeRect(0, 0, 100, 100) 90 mode: NSRadioModeMatrix 91 prototype: cell 92 numberOfRows: 0 93 numberOfColumns: 0]; 94 RELEASE (cell); 95 [indexedMatrix setIntercellSpacing: NSZeroSize]; 96 [indexedMatrix setCellSize: NSMakeSize([indexedScroll contentSize].width, fonth)]; 97 [indexedMatrix setAutoscroll: YES]; 98 [indexedMatrix setAllowsEmptySelection: YES]; 99 [indexedScroll setDocumentView: indexedMatrix]; 100 RELEASE (indexedMatrix); 101 102 for (i = 0; i < [indexedPaths count]; i++) { 103 NSString *name = [indexedPaths objectAtIndex: i]; 104 NSUInteger count = [[indexedMatrix cells] count]; 105 106 [indexedMatrix insertRow: count]; 107 cell = [indexedMatrix cellAtRow: count column: 0]; 108 [cell setStringValue: name]; 109 [cell setLeaf: YES]; 110 } 111 112 [self adjustMatrix: indexedMatrix]; 113 [indexedMatrix sizeToCells]; 114 [indexedMatrix setTarget: self]; 115 [indexedMatrix setAction: @selector(indexedMatrixAction:)]; 116 117 [indexedRemove setEnabled: ([[excludedMatrix cells] count] > 0)]; 118 119 [excludedScroll setBorderType: NSBezelBorder]; 120 [excludedScroll setHasHorizontalScroller: YES]; 121 [excludedScroll setHasVerticalScroller: YES]; 122 123 excludedMatrix = [[NSMatrix alloc] initWithFrame: NSMakeRect(0, 0, 100, 100) 124 mode: NSRadioModeMatrix 125 prototype: [[NSBrowserCell new] autorelease] 126 numberOfRows: 0 127 numberOfColumns: 0]; 128 [excludedMatrix setIntercellSpacing: NSZeroSize]; 129 [excludedMatrix setCellSize: NSMakeSize([excludedScroll contentSize].width, fonth)]; 130 [excludedMatrix setAutoscroll: YES]; 131 [excludedMatrix setAllowsEmptySelection: YES]; 132 [excludedScroll setDocumentView: excludedMatrix]; 133 RELEASE (excludedMatrix); 134 135 for (i = 0; i < [excludedPaths count]; i++) { 136 NSString *path = [excludedPaths objectAtIndex: i]; 137 NSUInteger count = [[excludedMatrix cells] count]; 138 139 [excludedMatrix insertRow: count]; 140 cell = [excludedMatrix cellAtRow: count column: 0]; 141 [cell setStringValue: path]; 142 [cell setLeaf: YES]; 143 } 144 145 [self adjustMatrix: excludedMatrix]; 146 [excludedMatrix sizeToCells]; 147 [excludedMatrix setTarget: self]; 148 [excludedMatrix setAction: @selector(excludedMatrixAction:)]; 149 150 [excludedRemove setEnabled: ([[excludedMatrix cells] count] > 0)]; 151 152 [suffixScroll setBorderType: NSBezelBorder]; 153 [suffixScroll setHasHorizontalScroller: YES]; 154 [suffixScroll setHasVerticalScroller: YES]; 155 156 suffixMatrix = [[NSMatrix alloc] initWithFrame: NSMakeRect(0, 0, 100, 100) 157 mode: NSRadioModeMatrix 158 prototype: [[NSBrowserCell new] autorelease] 159 numberOfRows: 0 160 numberOfColumns: 0]; 161 [suffixMatrix setIntercellSpacing: NSZeroSize]; 162 [suffixMatrix setCellSize: NSMakeSize([suffixScroll contentSize].width, fonth)]; 163 [suffixMatrix setAutoscroll: YES]; 164 [suffixMatrix setAllowsEmptySelection: YES]; 165 [suffixScroll setDocumentView: suffixMatrix]; 166 RELEASE (suffixMatrix); 167 168 for (i = 0; i < [excludedSuffixes count]; i++) { 169 NSString *path = [excludedSuffixes objectAtIndex: i]; 170 NSUInteger count = [[suffixMatrix cells] count]; 171 172 [suffixMatrix insertRow: count]; 173 cell = [suffixMatrix cellAtRow: count column: 0]; 174 [cell setStringValue: path]; 175 [cell setLeaf: YES]; 176 } 177 178 [self adjustMatrix: suffixMatrix]; 179 [suffixMatrix sizeToCells]; 180 [suffixMatrix setTarget: self]; 181 [suffixMatrix setAction: @selector(suffixMatrixAction:)]; 182 183 [suffixField setStringValue: @""]; 184 185 [suffixRemove setEnabled: ([[suffixMatrix cells] count] > 0)]; 186 187 pathsUnselReply = NSUnselectNow; 188 searchResultsReply = NSUnselectNow; 189 loaded = YES; 190 191 [revertButton setEnabled: NO]; 192 [applyButton setEnabled: NO]; 193 194 startAppWin = [[StartAppWin alloc] init]; 195 196 [statusWindow setTitle: NSLocalizedString(@"Status", @"")]; 197 [statusWindow setFrameUsingName: @"mdindexing_status_win"]; 198 [statusWindow setDelegate: self]; 199 200 [statusScroll setBorderType: NSBezelBorder]; 201 [statusScroll setHasHorizontalScroller: NO]; 202 [statusScroll setHasVerticalScroller: YES]; 203 statusView = [[NSTextView alloc] initWithFrame: [[statusScroll contentView] bounds]]; 204 [statusView setEditable: NO]; 205 [statusView setSelectable: NO]; 206 [statusView setVerticallyResizable: YES]; 207 [statusView setHorizontallyResizable: NO]; 208 [statusView setFont: [NSFont userFixedPitchFontOfSize: 0]]; 209 [statusScroll setDocumentView: statusView]; 210 RELEASE (statusView); 211 212 [errorWindow setTitle: NSLocalizedString(@"Error log", @"")]; 213 [errorWindow setFrameUsingName: @"mdindexing_error_win"]; 214 [errorWindow setDelegate: self]; 215 [errorScroll setBorderType: NSBezelBorder]; 216 [errorScroll setHasHorizontalScroller: NO]; 217 [errorScroll setHasVerticalScroller: YES]; 218 219 errorView = [[NSTextView alloc] initWithFrame: [[errorScroll contentView] bounds]]; 220 [errorView setEditable: NO]; 221 [errorView setSelectable: NO]; 222 [errorView setVerticallyResizable: YES]; 223 [errorView setHorizontallyResizable: YES]; 224 [errorView setFont: [NSFont userFixedPitchFontOfSize: 0]]; 225 [errorScroll setDocumentView: errorView]; 226 RELEASE (errorView); 227 228 // 229 // Search Results 230 // 231 str = @"Drag categories to change the order in which results appear."; 232 [searchResTitle setStringValue: NSLocalizedString(str, @"")]; 233 234 str = @"Only selected categories will appear in search results."; 235 [searchResSubtitle setStringValue: NSLocalizedString(str, @"")]; 236 237 [searchResEditor setMdindexing: self]; 238 239 [searchResApply setTitle: NSLocalizedString(@"Apply", @"")]; 240 241 indexedStatusPath = nil; 242 errorLogPath = nil; 243 statusTimer = nil; 244 [self setupDbPaths]; 245 246 mdextractor = nil; 247 [self connectMDExtractor]; 248 } 249} 250 251- (NSPreferencePaneUnselectReply)shouldUnselect 252{ 253 if ((pathsUnselReply == NSUnselectNow) 254 && (searchResultsReply == NSUnselectNow)) { 255 return NSUnselectNow; 256 } 257 258 return NSUnselectCancel; 259} 260 261- (void)didSelect 262{ 263 if (mdextractor == nil) { 264 if (NSRunAlertPanel(nil, 265 NSLocalizedString(@"The mdextractor connection died.\nDo you want to restart it?", @""), 266 NSLocalizedString(@"Yes", @""), 267 NSLocalizedString(@"No", @""), 268 nil)) { 269 [self connectMDExtractor]; 270 } 271 } 272} 273 274- (void)willUnselect 275{ 276 if ([statusWindow isVisible]) { 277 [statusWindow close]; 278 } 279 if ([errorWindow isVisible]) { 280 [errorWindow close]; 281 } 282} 283 284- (void)indexedMatrixAction:(id)sender 285{ 286 [indexedRemove setEnabled: ([[indexedMatrix cells] count] > 0)]; 287} 288 289- (IBAction)indexedButtAction:(id)sender 290{ 291 NSPreferencePaneUnselectReply oldReply = pathsUnselReply; 292 NSArray *cells = [indexedMatrix cells]; 293 NSUInteger count = [cells count]; 294 id cell; 295 NSUInteger i; 296 297#define IND_ERR_RETURN(x) \ 298do { \ 299NSRunAlertPanel(nil, \ 300NSLocalizedString(x, @""), \ 301NSLocalizedString(@"Ok", @""), \ 302nil, \ 303nil); \ 304pathsUnselReply = oldReply; \ 305return; \ 306} while (0) 307 308 if (sender == indexedAdd) { 309 NSString *path; 310 311 pathsUnselReply = NSUnselectCancel; 312 path = [self chooseNewPath]; 313 314 if (path) { 315 if (isDotFile(path)) { 316 IND_ERR_RETURN (@"Paths containing \'.\' are not indexable!"); 317 } 318 319 if ([indexedPaths containsObject: path]) { 320 IND_ERR_RETURN (@"The path is already present!"); 321 } 322 323 for (i = 0; i < [indexedPaths count]; i++) { 324 if (subPathOfPath([indexedPaths objectAtIndex: i], path)) { 325 IND_ERR_RETURN (@"This path is a subpath of an already indexable path!"); 326 } 327 } 328 329 for (i = 0; i < [excludedPaths count]; i++) { 330 NSString *exclpath = [excludedPaths objectAtIndex: i]; 331 332 if ([path isEqual: exclpath] || subPathOfPath(exclpath, path)) { 333 IND_ERR_RETURN (@"This path is excluded from the indexable paths!"); 334 } 335 } 336 337 [indexedPaths addObject: path]; 338 339 [indexedMatrix insertRow: count]; 340 cell = [indexedMatrix cellAtRow: count column: 0]; 341 [cell setStringValue: path]; 342 [cell setLeaf: YES]; 343 [self adjustMatrix: indexedMatrix]; 344 [indexedMatrix sizeToCells]; 345 [indexedMatrix selectCellAtRow: count column: 0]; 346 347 [indexedMatrix sendAction]; 348 349 } else { 350 pathsUnselReply = oldReply; 351 } 352 353 } else if (sender == indexedRemove) { 354 cell = [indexedMatrix selectedCell]; 355 356 if (cell) { 357 NSInteger row, col; 358 359 [indexedPaths removeObject: [cell stringValue]]; 360 361 [indexedMatrix getRow: &row column: &col ofCell: cell]; 362 [indexedMatrix removeRow: row]; 363 [self adjustMatrix: indexedMatrix]; 364 [indexedMatrix sizeToCells]; 365 366 [indexedMatrix sendAction]; 367 368 pathsUnselReply = NSUnselectCancel; 369 370 } else { 371 pathsUnselReply = oldReply; 372 } 373 } 374 375 [revertButton setEnabled: (pathsUnselReply != NSUnselectNow)]; 376 [applyButton setEnabled: (pathsUnselReply != NSUnselectNow)]; 377} 378 379- (void)excludedMatrixAction:(id)sender 380{ 381 [excludedRemove setEnabled: ([[excludedMatrix cells] count] > 0)]; 382} 383 384- (IBAction)excludedButtAction:(id)sender 385{ 386 NSPreferencePaneUnselectReply oldReply = pathsUnselReply; 387 NSArray *cells = [excludedMatrix cells]; 388 NSUInteger count = [cells count]; 389 id cell; 390 NSUInteger i; 391 392#define EXCL_ERR_RETURN(x) \ 393do { \ 394NSRunAlertPanel(nil, \ 395NSLocalizedString(x, @""), \ 396NSLocalizedString(@"Ok", @""), \ 397nil, \ 398nil); \ 399pathsUnselReply = oldReply; \ 400return; \ 401} while (0) 402 403 if (sender == excludedAdd) { 404 NSString *path; 405 406 pathsUnselReply = NSUnselectCancel; 407 path = [self chooseNewPath]; 408 409 if (path) { 410 BOOL valid = NO; 411 412 if (isDotFile(path)) { 413 IND_ERR_RETURN (@"Paths containing \'.\' are not indexable by default!"); 414 } 415 416 for (i = 0; i < [indexedPaths count]; i++) { 417 if (subPathOfPath([indexedPaths objectAtIndex: i], path)) { 418 valid = YES; 419 break; 420 } 421 } 422 423 if (valid == NO) { 424 EXCL_ERR_RETURN (@"An excluded path must be a subpath of an indexable path!"); 425 } 426 427 if ([excludedPaths containsObject: path]) { 428 EXCL_ERR_RETURN (@"The path is already present!"); 429 } 430 431 for (i = 0; i < [excludedPaths count]; i++) { 432 if (subPathOfPath([excludedPaths objectAtIndex: i], path)) { 433 EXCL_ERR_RETURN (@"This path is a subpath of an already excluded path!"); 434 } 435 } 436 437 for (i = 0; i < [indexedPaths count]; i++) { 438 NSString *idxpath = [indexedPaths objectAtIndex: i]; 439 440 if ([path isEqual: idxpath] || subPathOfPath(path, idxpath)) { 441 EXCL_ERR_RETURN (@"This path would exclude a path defined as indexable!"); 442 } 443 } 444 445 [excludedPaths addObject: path]; 446 447 [excludedMatrix insertRow: count]; 448 cell = [excludedMatrix cellAtRow: count column: 0]; 449 [cell setStringValue: path]; 450 [cell setLeaf: YES]; 451 [self adjustMatrix: excludedMatrix]; 452 [excludedMatrix sizeToCells]; 453 [excludedMatrix selectCellAtRow: count column: 0]; 454 455 [excludedMatrix sendAction]; 456 457 } else { 458 pathsUnselReply = oldReply; 459 } 460 461 } else if (sender == excludedRemove) { 462 cell = [excludedMatrix selectedCell]; 463 464 if (cell) { 465 NSInteger row, col; 466 467 [excludedPaths removeObject: [cell stringValue]]; 468 469 [excludedMatrix getRow: &row column: &col ofCell: cell]; 470 [excludedMatrix removeRow: row]; 471 [self adjustMatrix: excludedMatrix]; 472 [excludedMatrix sizeToCells]; 473 474 [excludedMatrix sendAction]; 475 476 pathsUnselReply = NSUnselectCancel; 477 478 } else { 479 pathsUnselReply = oldReply; 480 } 481 } 482 483 [revertButton setEnabled: (pathsUnselReply != NSUnselectNow)]; 484 [applyButton setEnabled: (pathsUnselReply != NSUnselectNow)]; 485} 486 487- (void)suffixMatrixAction:(id)sender 488{ 489 [suffixRemove setEnabled: ([[suffixMatrix cells] count] > 0)]; 490} 491 492- (IBAction)suffixButtAction:(id)sender 493{ 494 NSPreferencePaneUnselectReply oldReply = pathsUnselReply; 495 NSArray *cells = [suffixMatrix cells]; 496 NSUInteger count = [cells count]; 497 id cell; 498 499#define SUFF_ERR_RETURN(x) \ 500do { \ 501NSRunAlertPanel(nil, \ 502NSLocalizedString(x, @""), \ 503NSLocalizedString(@"Ok", @""), \ 504nil, \ 505nil); \ 506pathsUnselReply = oldReply; \ 507[suffixField setStringValue: @""]; \ 508return; \ 509} while (0) 510 511 if (sender == suffixAdd) { 512 NSString *suff = [suffixField stringValue]; 513 514 pathsUnselReply = NSUnselectCancel; 515 516 if ([suff length]) { 517 NSCharacterSet *set = [NSCharacterSet characterSetWithCharactersInString: @". "]; 518 519 if ([suff rangeOfCharacterFromSet: set].location != NSNotFound) { 520 SUFF_ERR_RETURN (@"Invalid character in suffix!"); 521 } 522 523 if ([excludedSuffixes containsObject: suff]) { 524 SUFF_ERR_RETURN (@"The suffix is already present!"); 525 } 526 527 [excludedSuffixes addObject: suff]; 528 529 [suffixMatrix insertRow: count]; 530 cell = [suffixMatrix cellAtRow: count column: 0]; 531 [cell setStringValue: suff]; 532 [cell setLeaf: YES]; 533 [self adjustMatrix: suffixMatrix]; 534 [suffixMatrix sizeToCells]; 535 [suffixMatrix selectCellAtRow: count column: 0]; 536 537 [suffixMatrix sendAction]; 538 539 } else { 540 pathsUnselReply = oldReply; 541 } 542 543 } else if (sender == suffixRemove) { 544 cell = [suffixMatrix selectedCell]; 545 546 if (cell) { 547 NSInteger row, col; 548 549 [excludedSuffixes removeObject: [cell stringValue]]; 550 551 [suffixMatrix getRow: &row column: &col ofCell: cell]; 552 [suffixMatrix removeRow: row]; 553 [self adjustMatrix: suffixMatrix]; 554 [suffixMatrix sizeToCells]; 555 556 [suffixMatrix sendAction]; 557 558 pathsUnselReply = NSUnselectCancel; 559 560 } else { 561 pathsUnselReply = oldReply; 562 } 563 } 564 565 [suffixField setStringValue: @""]; 566 567 [revertButton setEnabled: (pathsUnselReply != NSUnselectNow)]; 568 [applyButton setEnabled: (pathsUnselReply != NSUnselectNow)]; 569} 570 571- (IBAction)enableSwitchAction:(id)sender 572{ 573 BOOL oldEnabled = indexingEnabled; 574 575 indexingEnabled = ([enableSwitch state] == NSOnState); 576 577 [revertButton setEnabled: (oldEnabled != indexingEnabled)]; 578 [applyButton setEnabled: (oldEnabled != indexingEnabled)]; 579} 580 581- (IBAction)revertButtAction:(id)sender 582{ 583 id cell; 584 NSUInteger i; 585 586 DESTROY (indexedPaths); 587 DESTROY (excludedPaths); 588 DESTROY (excludedSuffixes); 589 590 indexedPaths = [NSMutableArray new]; 591 excludedPaths = [NSMutableArray new]; 592 excludedSuffixes = [NSMutableArray new]; 593 594 [self readDefaults]; 595 596 if ([indexedMatrix numberOfColumns] > 0) { 597 [indexedMatrix removeColumn: 0]; 598 } 599 600 for (i = 0; i < [indexedPaths count]; i++) { 601 NSString *name = [indexedPaths objectAtIndex: i]; 602 NSUInteger count = [[indexedMatrix cells] count]; 603 604 [indexedMatrix insertRow: count]; 605 cell = [indexedMatrix cellAtRow: count column: 0]; 606 [cell setStringValue: name]; 607 [cell setLeaf: YES]; 608 } 609 610 [self adjustMatrix: indexedMatrix]; 611 [indexedMatrix sizeToCells]; 612 613 [indexedRemove setEnabled: ([[indexedMatrix cells] count] > 0)]; 614 615 if ([excludedMatrix numberOfColumns] > 0) { 616 [excludedMatrix removeColumn: 0]; 617 } 618 619 for (i = 0; i < [excludedPaths count]; i++) { 620 NSString *path = [excludedPaths objectAtIndex: i]; 621 NSUInteger count = [[excludedMatrix cells] count]; 622 623 [excludedMatrix insertRow: count]; 624 cell = [excludedMatrix cellAtRow: count column: 0]; 625 [cell setStringValue: path]; 626 [cell setLeaf: YES]; 627 } 628 629 [self adjustMatrix: excludedMatrix]; 630 [excludedMatrix sizeToCells]; 631 632 [excludedRemove setEnabled: ([[excludedMatrix cells] count] > 0)]; 633 634 if ([suffixMatrix numberOfColumns] > 0) { 635 [suffixMatrix removeColumn: 0]; 636 } 637 638 for (i = 0; i < [excludedSuffixes count]; i++) { 639 NSString *suff = [excludedSuffixes objectAtIndex: i]; 640 NSUInteger count = [[suffixMatrix cells] count]; 641 642 [suffixMatrix insertRow: count]; 643 cell = [suffixMatrix cellAtRow: count column: 0]; 644 [cell setStringValue: suff]; 645 [cell setLeaf: YES]; 646 } 647 648 [self adjustMatrix: suffixMatrix]; 649 [suffixMatrix sizeToCells]; 650 651 [suffixRemove setEnabled: ([[suffixMatrix cells] count] > 0)]; 652 653 pathsUnselReply = NSUnselectNow; 654 [revertButton setEnabled: NO]; 655 [applyButton setEnabled: NO]; 656} 657 658- (IBAction)applyButtAction:(id)sender 659{ 660 [self applyChanges]; 661 pathsUnselReply = NSUnselectNow; 662 [revertButton setEnabled: NO]; 663 [applyButton setEnabled: NO]; 664} 665 666- (NSString *)chooseNewPath 667{ 668 NSOpenPanel *openPanel = [NSOpenPanel openPanel]; 669 int result; 670 671 [openPanel setTitle: NSLocalizedString(@"Choose directory", @"")]; 672 [openPanel setAllowsMultipleSelection: NO]; 673 [openPanel setCanChooseFiles: NO]; 674 [openPanel setCanChooseDirectories: YES]; 675 676 result = [openPanel runModalForDirectory: nil file: nil types: nil]; 677 678 if (result == NSOKButton) { 679 return [openPanel filename]; 680 } 681 682 return nil; 683} 684 685- (void)adjustMatrix:(NSMatrix *)matrix 686{ 687 NSArray *cells = [matrix cells]; 688 689 if (cells && [cells count]) 690 { 691 NSSize cellsize = [matrix cellSize]; 692 CGFloat margin = 10.0; 693 CGFloat maxw = margin; 694 NSDictionary *fontAttr; 695 NSUInteger i; 696 697 fontAttr = [NSDictionary dictionaryWithObject: [[cells objectAtIndex: 0] font] 698 forKey: NSFontAttributeName]; 699 700 for (i = 0; i < [cells count]; i++) { 701 NSString *str = [[cells objectAtIndex: i] stringValue]; 702 CGFloat strw = [str sizeWithAttributes: fontAttr].width + margin; 703 704 maxw = (strw > maxw) ? strw : maxw; 705 } 706 707 if (maxw > cellsize.width) { 708 [matrix setCellSize: NSMakeSize(maxw, cellsize.height)]; 709 } 710 } 711} 712 713- (void)setupDbPaths 714{ 715 NSString *dbdir; 716 NSString *lockpath; 717 BOOL isdir; 718 719 dbdir = [NSSearchPathForDirectoriesInDomains(NSLibraryDirectory, NSUserDomainMask, YES) lastObject]; 720 dbdir = [dbdir stringByAppendingPathComponent: @"gmds"]; 721 722 if (([fm fileExistsAtPath: dbdir isDirectory: &isdir] &isdir) == NO) { 723 if ([fm createDirectoryAtPath: dbdir attributes: nil] == NO) { 724 NSRunAlertPanel(nil, 725 NSLocalizedString(@"unable to create the db directory.", @""), 726 NSLocalizedString(@"Ok", @""), 727 nil, 728 nil); 729 return; 730 } 731 } 732 733 dbdir = [dbdir stringByAppendingPathComponent: @".db"]; 734 735 if (([fm fileExistsAtPath: dbdir isDirectory: &isdir] &isdir) == NO) { 736 if ([fm createDirectoryAtPath: dbdir attributes: nil] == NO) { 737 NSRunAlertPanel(nil, 738 NSLocalizedString(@"unable to create the db directory.", @""), 739 NSLocalizedString(@"Ok", @""), 740 nil, 741 nil); 742 return; 743 } 744 } 745 746 ASSIGN (indexedStatusPath, [dbdir stringByAppendingPathComponent: @"status.plist"]); 747 748 ASSIGN (errorLogPath, [dbdir stringByAppendingPathComponent: @"error.log"]); 749 750 lockpath = [dbdir stringByAppendingPathComponent: @"extractors.lock"]; 751 indexedStatusLock = [[NSDistributedLock alloc] initWithPath: lockpath]; 752} 753 754- (void)connectMDExtractor 755{ 756 if (mdextractor == nil) { 757 mdextractor = [NSConnection rootProxyForConnectionWithRegisteredName: @"mdextractor" 758 host: @""]; 759 760 if (mdextractor == nil) { 761 NSString *cmd; 762 int i; 763 764 cmd = [NSTask launchPathForTool: @"mdextractor"]; 765 766 [startAppWin showWindowWithTitle: @"MDIndexing" 767 appName: @"mdextractor" 768 operation: NSLocalizedString(@"starting:", @"") 769 maxProgValue: 80.0]; 770 771 [NSTask launchedTaskWithLaunchPath: cmd arguments: nil]; 772 773 for (i = 1; i <= 80; i++) { 774 [startAppWin updateProgressBy: 1.0]; 775 [[NSRunLoop currentRunLoop] runUntilDate: 776 [NSDate dateWithTimeIntervalSinceNow: 0.1]]; 777 778 mdextractor = [NSConnection rootProxyForConnectionWithRegisteredName: @"mdextractor" 779 host: @""]; 780 if (mdextractor) { 781 [startAppWin updateProgressBy: 80.0 - i]; 782 break; 783 } 784 } 785 786 [[startAppWin win] close]; 787 } 788 789 if (mdextractor) { 790 [mdextractor setProtocolForProxy: @protocol(MDExtractorProtocol)]; 791 RETAIN (mdextractor); 792 793 [[NSNotificationCenter defaultCenter] addObserver: self 794 selector: @selector(mdextractorConnectionDidDie:) 795 name: NSConnectionDidDieNotification 796 object: [mdextractor connectionForProxy]]; 797 } else { 798 NSRunAlertPanel(nil, 799 NSLocalizedString(@"unable to contact mdextractor!", @""), 800 NSLocalizedString(@"Ok", @""), 801 nil, 802 nil); 803 } 804 } 805} 806 807- (void)mdextractorConnectionDidDie:(NSNotification *)notif 808{ 809 id connection = [notif object]; 810 811 [nc removeObserver: self 812 name: NSConnectionDidDieNotification 813 object: connection]; 814 815 NSAssert(connection == [mdextractor connectionForProxy], 816 NSInternalInconsistencyException); 817 RELEASE (mdextractor); 818 mdextractor = nil; 819 820 if ([self isSelected]) { 821 if (NSRunAlertPanel(nil, 822 NSLocalizedString(@"The mdextractor connection died.\nDo you want to restart it?", @""), 823 NSLocalizedString(@"Yes", @""), 824 NSLocalizedString(@"No", @""), 825 nil)) { 826 [self connectMDExtractor]; 827 } 828 } 829} 830 831- (IBAction)statusButtAction:(id)sender 832{ 833 if ([statusWindow isVisible] == NO) { 834 [statusWindow makeKeyAndOrderFront: nil]; 835 836 [self readIndexedPathsStatus: nil]; 837 838 if (statusTimer && [statusTimer isValid]) { 839 [statusTimer invalidate]; 840 } 841 DESTROY (statusTimer); 842 843 statusTimer = [NSTimer scheduledTimerWithTimeInterval: 5.0 844 target: self 845 selector: @selector(readIndexedPathsStatus:) 846 userInfo: nil 847 repeats: YES]; 848 RETAIN (statusTimer); 849 } 850} 851 852- (IBAction)errorButtAction:(id)sender 853{ 854 NSString *errstr = @""; 855 856 if ([fm fileExistsAtPath: errorLogPath]) { 857 NS_DURING 858 { 859 errstr = [NSString stringWithContentsOfFile: errorLogPath]; 860 } 861 NS_HANDLER 862 { 863 errstr = @""; 864 } 865 NS_ENDHANDLER 866 } 867 868 [errorView setString: errstr]; 869// [errorView sizeToFit]; 870 871 if ([errorWindow isVisible] == NO) { 872 [errorWindow makeKeyAndOrderFront: nil]; 873 } 874} 875 876- (void)readIndexedPathsStatus:(id)sender 877{ 878 CREATE_AUTORELEASE_POOL(arp); 879 880 if (indexedStatusPath && [fm isReadableFileAtPath: indexedStatusPath]) { 881 NSArray *status = nil; 882 883 if ([indexedStatusLock tryLock] == NO) { 884 unsigned sleeps = 0; 885 886 if ([[indexedStatusLock lockDate] timeIntervalSinceNow] < -20.0) { 887 NS_DURING 888 { 889 [indexedStatusLock breakLock]; 890 } 891 NS_HANDLER 892 { 893 NSLog(@"Unable to break lock %@ ... %@", indexedStatusLock, localException); 894 } 895 NS_ENDHANDLER 896 } 897 898 for (sleeps = 0; sleeps < 10; sleeps++) { 899 if ([indexedStatusLock tryLock]) { 900 break; 901 } 902 903 sleeps++; 904 [NSThread sleepUntilDate: [NSDate dateWithTimeIntervalSinceNow: 0.1]]; 905 } 906 907 if (sleeps >= 10) { 908 NSLog(@"Unable to obtain lock %@", indexedStatusLock); 909 RELEASE (arp); 910 return; 911 } 912 } 913 914 status = [NSArray arrayWithContentsOfFile: indexedStatusPath]; 915 [indexedStatusLock unlock]; 916 917 if (status) { 918 NSMutableString *str = [NSMutableString string]; 919 NSUInteger i; 920 921 for (i = 0; i < [status count]; i++) { 922 NSDictionary *info = [status objectAtIndex: i]; 923 NSString *path = [info objectForKey: @"path"]; 924 BOOL indexed = [[info objectForKey: @"indexed"] boolValue]; 925 NSNumber *fcount = [info objectForKey: @"count"]; 926 NSDate *startTime = [info objectForKey: @"start_time"]; 927 NSDate *endTime = [info objectForKey: @"end_time"]; 928 NSArray *subPaths = [info objectForKey: @"subpaths"]; 929 930 [str appendFormat: @"%@\n", path]; 931 [str appendFormat: @" indexed: %@\n", (indexed ? @"YES" : @"NO")]; 932 933 if (startTime) { 934 [str appendFormat: @" start: %@\n", [startTime description]]; 935 } 936 if (endTime) { 937 [str appendFormat: @" end: %@\n", [endTime description]]; 938 } 939 if (fcount) { 940 [str appendFormat: @" files: %lu\n", [fcount unsignedLongValue]]; 941 } 942 943 if (subPaths && [subPaths count]) { 944 unsigned j; 945 946 [str appendString: @" subpaths:\n"]; 947 948 for (j = 0; j < [subPaths count]; j++) { 949 info = [subPaths objectAtIndex: j]; 950 path = [info objectForKey: @"path"]; 951 indexed = [[info objectForKey: @"indexed"] boolValue]; 952 fcount = [info objectForKey: @"count"]; 953 startTime = [info objectForKey: @"start_time"]; 954 endTime = [info objectForKey: @"end_time"]; 955 956 [str appendFormat: @" %@\n", path]; 957 [str appendFormat: @" indexed: %@\n", (indexed ? @"YES" : @"NO")]; 958 959 if (startTime) { 960 [str appendFormat: @" start: %@\n", [startTime description]]; 961 } 962 if (endTime) { 963 [str appendFormat: @" end: %@\n", [endTime description]]; 964 } 965 if (fcount) { 966 [str appendFormat: @" files: %lu\n", [fcount unsignedLongValue]]; 967 } 968 } 969 } 970 971 [str appendString: @"\n"]; 972 } 973 974 [statusView setString: str]; 975 [statusView sizeToFit]; 976 } 977 } 978 979 RELEASE (arp); 980} 981 982- (void)windowWillClose:(NSNotification *)aNotification 983{ 984 id win = [aNotification object]; 985 986 if (win == statusWindow) { 987 if (statusTimer && [statusTimer isValid]) { 988 [statusTimer invalidate]; 989 } 990 DESTROY (statusTimer); 991 992 [statusWindow saveFrameUsingName: @"mdindexing_status_win"]; 993 994 } else if (win == errorWindow) { 995 [errorWindow saveFrameUsingName: @"mdindexing_error_win"]; 996 } 997} 998 999- (void)readDefaults 1000{ 1001 NSUserDefaults *defaults = [NSUserDefaults standardUserDefaults]; 1002 id entry; 1003 1004 [defaults synchronize]; 1005 1006 entry = [defaults arrayForKey: @"GSMetadataIndexablePaths"]; 1007 if (entry) { 1008 [indexedPaths addObjectsFromArray: entry]; 1009 1010 } else { 1011 NSArray *dirs; 1012 NSUInteger i; 1013 1014 [indexedPaths addObject: NSHomeDirectory()]; 1015 1016 dirs = NSSearchPathForDirectoriesInDomains(NSAllApplicationsDirectory, 1017 NSAllDomainsMask, YES); 1018 [indexedPaths addObjectsFromArray: dirs]; 1019 1020 dirs = NSSearchPathForDirectoriesInDomains(NSLibraryDirectory, 1021 NSAllDomainsMask, YES); 1022 for (i = 0; i < [dirs count]; i++) { 1023 NSString *dir = [dirs objectAtIndex: i]; 1024 NSString *path = [dir stringByAppendingPathComponent: @"Headers"]; 1025 1026 if ([fm fileExistsAtPath: path]) { 1027 [indexedPaths addObject: path]; 1028 } 1029 1030 path = [dir stringByAppendingPathComponent: @"Documentation"]; 1031 1032 if ([fm fileExistsAtPath: path]) { 1033 [indexedPaths addObject: path]; 1034 } 1035 } 1036 } 1037 1038 entry = [defaults arrayForKey: @"GSMetadataExcludedPaths"]; 1039 if (entry) { 1040 [excludedPaths addObjectsFromArray: entry]; 1041 } 1042 1043 entry = [defaults arrayForKey: @"GSMetadataExcludedSuffixes"]; 1044 if (entry == nil) { 1045 entry = [NSArray arrayWithObjects: @"a", @"d", @"dylib", @"er1", 1046 @"err", @"extinfo", @"frag", @"la", 1047 @"log", @"o", @"out", @"part", 1048 @"sed", @"so", @"status", @"temp", 1049 @"tmp", 1050 nil]; 1051 } 1052 1053 [excludedSuffixes addObjectsFromArray: entry]; 1054 1055 indexingEnabled = [defaults boolForKey: @"GSMetadataIndexingEnabled"]; 1056 [enableSwitch setState: (indexingEnabled ? NSOnState : NSOffState)]; 1057} 1058 1059- (void)applyChanges 1060{ 1061 CREATE_AUTORELEASE_POOL(arp); 1062 NSUserDefaults *defaults; 1063 NSMutableDictionary *domain; 1064 NSMutableDictionary *info; 1065 1066 defaults = [NSUserDefaults standardUserDefaults]; 1067 [defaults synchronize]; 1068 domain = [[defaults persistentDomainForName: NSGlobalDomain] mutableCopy]; 1069 1070 [domain setObject: indexedPaths forKey: @"GSMetadataIndexablePaths"]; 1071 [domain setObject: excludedPaths forKey: @"GSMetadataExcludedPaths"]; 1072 [domain setObject: excludedSuffixes forKey: @"GSMetadataExcludedSuffixes"]; 1073 [domain setObject: [NSNumber numberWithBool: indexingEnabled] 1074 forKey: @"GSMetadataIndexingEnabled"]; 1075 1076 [defaults setPersistentDomain: domain forName: NSGlobalDomain]; 1077 [defaults synchronize]; 1078 RELEASE (domain); 1079 1080 info = [NSMutableDictionary dictionary]; 1081 1082 [info setObject: indexedPaths forKey: @"GSMetadataIndexablePaths"]; 1083 [info setObject: excludedPaths forKey: @"GSMetadataExcludedPaths"]; 1084 [info setObject: excludedSuffixes forKey: @"GSMetadataExcludedSuffixes"]; 1085 [info setObject: [NSNumber numberWithBool: indexingEnabled] 1086 forKey: @"GSMetadataIndexingEnabled"]; 1087 1088 [dnc postNotificationName: @"GSMetadataIndexedDirectoriesChanged" 1089 object: nil 1090 userInfo: info]; 1091 1092 RELEASE (arp); 1093} 1094 1095// 1096// Search Results 1097// 1098- (IBAction)searchResButtAction:(id)sender 1099{ 1100 if (sender == searchResApply) { 1101 [searchResEditor applyChanges]; 1102 } else { 1103 [searchResEditor revertChanges]; 1104 } 1105} 1106 1107- (void)searchResultDidStartEditing 1108{ 1109 [searchResRevert setEnabled: YES]; 1110 [searchResApply setEnabled: YES]; 1111 searchResultsReply = NSUnselectCancel; 1112} 1113 1114- (void)searchResultDidEndEditing 1115{ 1116 [searchResRevert setEnabled: NO]; 1117 [searchResApply setEnabled: NO]; 1118 searchResultsReply = NSUnselectNow; 1119} 1120 1121@end 1122 1123 1124BOOL subPathOfPath(NSString *p1, NSString *p2) 1125{ 1126 int l1 = [p1 length]; 1127 int l2 = [p2 length]; 1128 1129 if ((l1 > l2) || ([p1 isEqual: p2])) { 1130 return NO; 1131 } else if ([[p2 substringToIndex: l1] isEqual: p1]) { 1132 if ([[p2 pathComponents] containsObject: [p1 lastPathComponent]]) { 1133 return YES; 1134 } 1135 } 1136 1137 return NO; 1138} 1139 1140BOOL isDotFile(NSString *path) 1141{ 1142 NSArray *components; 1143 NSEnumerator *e; 1144 NSString *c; 1145 BOOL found; 1146 1147 if (path == nil) 1148 return NO; 1149 1150 found = NO; 1151 components = [path pathComponents]; 1152 e = [components objectEnumerator]; 1153 while ((c = [e nextObject]) && !found) 1154 { 1155 if (([c length] > 0) && ([c characterAtIndex:0] == '.')) 1156 found = YES; 1157 } 1158 1159 return found; 1160} 1161 1162