MoriarityController.m

/*
 File:      MoriarityController.m
 
 Description:   This is the implementation for our "controller" class that operates as the go-between for the view (the UI) and the model (the locate database).  The point of this code is to show how you can wrap a UNIX task in a nice Cocoa GUI, and get back the output (which you could then operate on or parse further if you choose).
 
 Author:        MCF
 
 Copyright:     © Copyright 2001 Apple Computer, Inc. All rights reserved.
 
 Disclaimer:    IMPORTANT:  This Apple software is supplied to you by Apple Computer, Inc.
 ("Apple") in consideration of your agreement to the following terms, and your
 use, installation, modification or redistribution of this Apple software
 constitutes acceptance of these terms.  If you do not agree with these terms,
 please do not use, install, modify or redistribute this Apple software.
 
 In consideration of your agreement to abide by the following terms, and subject
 to these terms, Apple grants you a personal, non-exclusive license, under AppleÕs
 copyrights in this original Apple software (the "Apple Software"), to use,
 reproduce, modify and redistribute the Apple Software, with or without
 modifications, in source and/or binary forms; provided that if you redistribute
 the Apple Software in its entirety and without modifications, you must retain
 this notice and the following text and disclaimers in all such redistributions of
 the Apple Software.  Neither the name, trademarks, service marks or logos of
 Apple Computer, Inc. may be used to endorse or promote products derived from the
 Apple Software without specific prior written permission from Apple.  Except as
 expressly stated in this notice, no other rights or licenses, express or implied,
 are granted by Apple herein, including but not limited to any patent rights that
 may be infringed by your derivative works or by other works in which the Apple
 Software may be incorporated.
 
 The Apple Software is provided by Apple on an "AS IS" basis.  APPLE MAKES NO
 WARRANTIES, EXPRESS OR IMPLIED, INCLUDING WITHOUT LIMITATION THE IMPLIED
 WARRANTIES OF NON-INFRINGEMENT, MERCHANTABILITY AND FITNESS FOR A PARTICULAR
 PURPOSE, REGARDING THE APPLE SOFTWARE OR ITS USE AND OPERATION ALONE OR IN
 COMBINATION WITH YOUR PRODUCTS.
 
 IN NO EVENT SHALL APPLE BE LIABLE FOR ANY SPECIAL, INDIRECT, INCIDENTAL OR
 CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE
                        GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
 ARISING IN ANY WAY OUT OF THE USE, REPRODUCTION, MODIFICATION AND/OR DISTRIBUTION
 OF THE APPLE SOFTWARE, HOWEVER CAUSED AND WHETHER UNDER THEORY OF CONTRACT, TORT
 (INCLUDING NEGLIGENCE), STRICT LIABILITY OR OTHERWISE, EVEN IF APPLE HAS BEEN
 ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 
 Version History: 1.1/1.2 released to fix a few bugs (not always removing the notification center,
                                                  forgetting to release in some cases)
 
 */
 
 
#import "MoriarityController.h"
 
@implementation MoriarityController
 
 
// here we implement a cheesy check to determine if update.locatedb has been run on the current machine.
// a fresh Mac OS X install has a very small database file, that contains no useful information.
- (BOOL)ensureLocateDBExists
{
    NSDictionary *attr;
    
    if ([[NSFileManager defaultManager] fileExistsAtPath:@"/var/db/locate.database"]==YES)
    {
        attr=[[NSFileManager defaultManager] fileAttributesAtPath:@"/var/db/locate.database" traverseLink:YES];
        if ([attr fileSize]>4096)//we pick some size that seems large enough that it couldn't be empty
        return YES;
        else
        return NO;
    }
    else
    return NO;
}
 
// This action kicks off a locate search task if we aren't already searching for something,
// or stops the current search if one is already running
- (IBAction)sleuth:(id)sender
{
    if (findRunning)
    {
        // This stops the task and calls our callback (-processFinished)
        [searchTask stopProcess];
        // Release the memory for this wrapper object
        [searchTask release];
        searchTask=nil;
        return;
    }
    else
    {
        // If the task is still sitting around from the last run, release it
        if (searchTask!=nil)
        [searchTask release];
        // Let's allocate memory for and initialize a new TaskWrapper object, passing
        // in ourselves as the controller for this TaskWrapper object, the path
        // to the command-line tool, and the contents of the text field that 
        // displays what the user wants to search on
        searchTask=[[TaskWrapper alloc] initWithController:self arguments:[NSArray arrayWithObjects:@"/usr/bin/locate",[findTextField stringValue],nil]];
        // kick off the process asynchronously
        [searchTask startProcess];
    }
}
 
// This callback is implemented as part of conforming to the ProcessController protocol.
// It will be called whenever there is output from the TaskWrapper.
- (void)appendOutput:(NSString *)output
{
    // add the string (a chunk of the results from locate) to the NSTextView's
    // backing store, in the form of an attributed string
    [[resultsTextField textStorage] appendAttributedString: [[[NSAttributedString alloc]
                             initWithString: output] autorelease]];
    // setup a selector to be called the next time through the event loop to scroll
    // the view to the just pasted text.  We don't want to scroll right now,
    // because of a bug in Mac OS X version 10.1 that causes scrolling in the context
    // of a text storage update to starve the app of events
    [self performSelector:@selector(scrollToVisible:) withObject:nil afterDelay:0.0];
}
 
// This routine is called after adding new results to the text view's backing store.
// We now need to scroll the NSScrollView in which the NSTextView sits to the part
// that we just added at the end
- (void)scrollToVisible:(id)ignore {
    [resultsTextField scrollRangeToVisible:NSMakeRange([[resultsTextField string] length], 0)];
}
 
// A callback that gets called when a TaskWrapper is launched, allowing us to do any setup
// that is needed from the app side.  This method is implemented as a part of conforming
// to the ProcessController protocol.
- (void)processStarted
{
    findRunning=YES;
    // clear the results
    [resultsTextField setString:@""];
    // change the "Sleuth" button to say "Stop"
    [sleuthButton setTitle:@"Stop"];
}
 
// A callback that gets called when a TaskWrapper is completed, allowing us to do any cleanup
// that is needed from the app side.  This method is implemented as a part of conforming
// to the ProcessController protocol.
- (void)processFinished
{
    findRunning=NO;
    // change the button's title back for the next search
    [sleuthButton setTitle:@"Sleuth"];
}
 
// If the user closes the search window, let's just quit
-(BOOL)windowShouldClose:(id)sender
{
    [NSApp terminate:nil];
    return YES;
}
 
// Display the release notes, as chosen from the menu item in the Help menu.
- (IBAction)displayReleaseNotes:(id)sender
{
    // Grab the release notes from the Resources folder in the app bundle, and stuff 
    // them into the proper text field
    [relNotesTextField readRTFDFromFile:[[NSBundle mainBundle] pathForResource:@"ReadMe" ofType:@"rtf"]];
    [relNotesWin makeKeyAndOrderFront:self];
}
 
// when first launched, this routine is called when all objects are created
// and initialized.  It's a chance for us to set things up before the user gets
// control of the UI.
-(void)awakeFromNib
{
    findRunning=NO;
    searchTask=nil;
    // Lets make sure that there is something valid in the locate database; otherwise,
    // all searches will come back empty.
    if ([self ensureLocateDBExists]==NO)
    {
        // Explain to the user that they need to go update the database as root.
        // That is, if they want locate to be able to really find *any* file
        // on their hard drive (perhaps not great for security, but good for usability).
    NSRunAlertPanel(@"Error",@"Sorry, Moriarity's 'locate' database is missing or empty.  In a terminal, as root run '/usr/libexec/locate.updatedb' and try Moriarity again.", @"OK",NULL,NULL);
        [NSApp terminate:nil];
    }
}
 
@end