Mac OS X Reference Library Apple Developer
Search

MyWindowController.m

/*
     File: MyWindowController.m 
 Abstract: Header file for this sample's main NSWindowController "TestWindow".
  
  Version: 1.2 
  
 Disclaimer: IMPORTANT:  This Apple software is supplied to you by Apple 
 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 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. 
  
 Copyright (C) 2009 Apple Inc. All Rights Reserved. 
  
 */
 
#import "MyWindowController.h"
#import "GetPathsOperation.h"
#import "LoadOperation.h"
 
// -------------------------------------------------------------------------------
@interface MyWindowController (Private)
 
- (void)loadFilePaths:(NSString *)fromPath;
- (void)setResultsString:(NSString *)string;
- (void)updateProgress:(NSTimer *)t;
- (NSTimer *)timer;
- (void)setTimer:(NSTimer *)value;
- (void)updateCountIndicator;
 
@end
 
 
@implementation MyWindowController
 
// -------------------------------------------------------------------------------
//  awakeFromNib:
// -------------------------------------------------------------------------------
- (void)awakeFromNib
{   
    // register for the notification when an image file has been loaded by the NSOperation: "LoadOperation"
    [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(anyThread_handleLoadedImages:) name:LoadImageDidFinish object:nil];
    
    // make sure double-click on a table row calls "doubleClickAction"
    [myTableView setTarget:self];
    [myTableView setDoubleAction:@selector(doubleClickAction:)];
}
 
// -------------------------------------------------------------------------------
//  init:
// -------------------------------------------------------------------------------
- (id)init
{
    [super init];
    
    queue = [[NSOperationQueue alloc] init];
    tableRecords = [[NSMutableArray alloc] init];
    
    return self;
}
 
// -------------------------------------------------------------------------------
//  dealloc:
// -------------------------------------------------------------------------------
-(void)dealloc
{
    [tableRecords release];
    [queue release];
    [super dealloc];
}
 
// -------------------------------------------------------------------------------
//  updateCountIndicator:
//
//  Canned routine for updating the number of items in the table (used in several places).
// -------------------------------------------------------------------------------
-(void)updateCountIndicator
{
    // set the number of images found indicator string
    NSString *resultStr = [NSString stringWithFormat:@"Images found: %ld", [tableRecords count]];
    [self setResultsString: resultStr];
}
 
// -------------------------------------------------------------------------------
//  applicationShouldTerminateAfterLastWindowClosed:sender
//
//  NSApplication delegate method placed here so the sample conveniently quits
//  after we close the window.
// -------------------------------------------------------------------------------
- (BOOL)applicationShouldTerminateAfterLastWindowClosed:(NSApplication *)sender
{
    return YES;
}
 
// -------------------------------------------------------------------------------
//  mainThread_handleLoadedImages:note
//
//  The method used to modify the table's data source on the main thread.
//  This will cause the table to update itself once the NSArrayController is changed.
//
//  The notification contains an NSDictionary containing the image file's info
//  to add to the table view.
// -------------------------------------------------------------------------------
- (void)mainThread_handleLoadedImages:(NSNotification *)note
{
    // Pending NSNotifications can possibly back up while waiting to be executed,
    // and if the user stops the queue, we may have left-over pending
    // notifications to process.
    //
    // So make sure we have "active" running NSOperations in the queue
    // if we are to continuously add found image files to the table view.
    // Otherwise, we let any remaining notifications drain out.
    //
    if ([myStopButton isEnabled])
    {
        [tableRecords addObject:[note userInfo]];
        [myTableView reloadData];
        
        // set the number of images found indicator string
        [self updateCountIndicator];
    }
}
 
// -------------------------------------------------------------------------------
//  anyThread_handleLoadedImages:note
//
//  This method is called from any possible thread (any NSOperation) used to 
//  update our table view and its data source.
//  
//  The notification contains the NSDictionary containing the image file's info
//  to add to the table view.
// -------------------------------------------------------------------------------
- (void)anyThread_handleLoadedImages:(NSNotification *)note
{
    // update our table view on the main thread
    [self performSelectorOnMainThread:@selector(mainThread_handleLoadedImages:) withObject:note waitUntilDone:NO];
}
 
// -------------------------------------------------------------------------------
//  alertDidEnd:
// -------------------------------------------------------------------------------
-(void)alertDidEnd:(NSAlert *)alert returnCode:(NSInteger)returnCode contextInfo:(void *)contextInfo
{}
 
// -------------------------------------------------------------------------------
//  windowShouldClose:
// -------------------------------------------------------------------------------
- (BOOL)windowShouldClose:(id)sender
{
    // are you sure you want to close, (threads running)
    NSInteger numOperationsRunning = [[queue operations] count];
    
    if (numOperationsRunning > 0)
    {
        NSAlert *alert = [NSAlert alertWithMessageText: @"Image files are currently loading."
                                    defaultButton: @"OK"
                                  alternateButton: nil
                                      otherButton: nil
                        informativeTextWithFormat: @"Please click the \"Stop\" button before closing."];
        [alert beginSheetModalForWindow: [self window] modalDelegate:self didEndSelector:@selector(alertDidEnd:returnCode:contextInfo:) contextInfo:nil];
    }
    
    return (numOperationsRunning == 0);
}
 
// -------------------------------------------------------------------------------
//  loadFilePaths:fromPath
// -------------------------------------------------------------------------------
-(void)loadFilePaths:(NSString *)fromPath
{
    [queue cancelAllOperations];
    
    // start the GetPathsOperation with the root path to start the search
    GetPathsOperation* getPathsOp = [[GetPathsOperation alloc] initWithRootPath:fromPath operationClass:[LoadOperation class] queue:queue];
    
    [queue addOperation: getPathsOp];   // this will start the "GetPathsOperation"
    [getPathsOp release];
}
 
// -------------------------------------------------------------------------------
//  setResultsString:string
// -------------------------------------------------------------------------------
-(void)setResultsString:(NSString *)string
{
    [self willChangeValueForKey:@"imagesFoundStr"];
    imagesFoundStr = [NSMutableString stringWithString:string];
    [self didChangeValueForKey:@"imagesFoundStr"];
}
 
 
#pragma mark - Actions
 
// -------------------------------------------------------------------------------
//  doubleClickAction:sender
//
//  Inspect our selected objects (user double-clicked them).
// -------------------------------------------------------------------------------
- (void)doubleClickAction:(id)sender
{
    NSTableView *theTableView = (NSTableView *)sender;
    NSInteger selectedRow = [theTableView selectedRow];
    if (selectedRow != -1)
    {
        NSDictionary* objectDict = [tableRecords objectAtIndex: selectedRow];
        if (objectDict != nil)
        {
            NSString *pathStr = [objectDict valueForKey: @"path"];
            NSString *completeURLStr = [pathStr stringByAppendingPathComponent:[objectDict valueForKey: @"name"]];
            
            [[NSWorkspace sharedWorkspace] openURL:[NSURL fileURLWithPath:completeURLStr]];
        }
    }
}
 
// -------------------------------------------------------------------------------
//  stopAction:sender
// -------------------------------------------------------------------------------
- (IBAction)stopAction:(id)sender
{
    [queue cancelAllOperations];
        
    [myStopButton setEnabled:NO];
    [myStartButton setEnabled:YES];
 
    [myProgressInd setHidden:YES];
    [myProgressInd stopAnimation:self];
    
    [self updateCountIndicator];
}
 
// -------------------------------------------------------------------------------
//  chooseDidEnd:panel:returnCode:contextInfo
// -------------------------------------------------------------------------------
-(void)chooseDidEnd:(NSOpenPanel *)panel returnCode:(int)returnCode contextInfo:(void *)contextInfo
{
    [panel orderOut:self];
    [panel release];
    
    if (returnCode == NSFileHandlingPanelOKButton)
    {
        // user has chosen a directory, start finding image files:
        
        [tableRecords removeAllObjects];    // clear the table data
        [self updateCountIndicator];
        
        [myStopButton setEnabled:YES];
        [myStartButton setEnabled:NO];
        
        [myProgressInd setHidden:NO];
        [myProgressInd startAnimation:self];
        
        [self loadFilePaths:[[panel URL] path]];    // start the file search NSOperation
        
        // schedule our update timer for the spinning gear control
        [self setTimer: [NSTimer scheduledTimerWithTimeInterval: 1.0
                                                         target: self
                                                       selector: @selector(updateProgress:)
                                                       userInfo: nil
                                                        repeats: YES]];
    }
}
 
// -------------------------------------------------------------------------------
//  startAction:sender
// -------------------------------------------------------------------------------
- (IBAction)startAction:(id)sender
{   
    NSOpenPanel *openPanel = [[NSOpenPanel alloc] init];
    
    [openPanel setResolvesAliases:YES];
    [openPanel setCanChooseDirectories:YES];
    [openPanel setAllowsMultipleSelection:NO];
    [openPanel setCanChooseFiles:NO];
    [openPanel setPrompt:@"Choose"];
    [openPanel setMessage:@"Choose a directory that has a large number image files:"];
    [openPanel setTitle:@"Choose"];
    
    [openPanel beginSheetForDirectory:nil file:nil types:nil modalForWindow:[self window] modalDelegate:self didEndSelector:@selector(chooseDidEnd:returnCode:contextInfo:) contextInfo:nil];
}
 
 
#pragma mark - Timer Support
 
// -------------------------------------------------------------------------------
//  updateProgress:timer
// -------------------------------------------------------------------------------
-(void)updateProgress:(NSTimer *)t
{
    if ([[queue operations] count] == 0)
    {
        [t invalidate];
        [self setTimer: nil];
        
        [myProgressInd stopAnimation:self];
        [myProgressInd setHidden:YES];
        [myStopButton setEnabled:NO];
        [myStartButton setEnabled:YES];
        
        // set the number of images found indicator string
        [self updateCountIndicator];
    }
}
 
// -------------------------------------------------------------------------------
//  timer:
// -------------------------------------------------------------------------------
- (NSTimer *)timer
{
    return [[timer retain] autorelease];
}
 
// -------------------------------------------------------------------------------
//  setTimer:value
// -------------------------------------------------------------------------------
- (void)setTimer:(NSTimer *)value
{
    if (timer != value)
    {
        [timer release];
        timer = [value retain];
    }
}
 
#pragma mark - Data Source
 
// -------------------------------------------------------------------------------
//  numberOfRowsInTableView:aTableView
// -------------------------------------------------------------------------------
- (int)numberOfRowsInTableView:(NSTableView *)aTableView
{
    return [tableRecords count];
}
 
// -------------------------------------------------------------------------------
//  objectValueForTableColumn:aTableColumn:row
// -------------------------------------------------------------------------------
- (id)tableView:(NSTableView *)aTableView objectValueForTableColumn:(NSTableColumn *)aTableColumn row:(int)rowIndex
{
    id theRecord, theValue;
    
    theRecord = [tableRecords objectAtIndex:rowIndex];
    theValue = [theRecord objectForKey:[aTableColumn identifier]];
    
    return theValue;
}
 
// -------------------------------------------------------------------------------
//  sortWithDescriptor:descriptor
// -------------------------------------------------------------------------------
- (void)sortWithDescriptor:(id)descriptor
{
    NSMutableArray *sorted = [[NSMutableArray alloc] initWithCapacity:1];
    [sorted addObjectsFromArray:[tableRecords sortedArrayUsingDescriptors:[NSArray arrayWithObject:descriptor]]];
    [tableRecords removeAllObjects];
    [tableRecords addObjectsFromArray:sorted];
    [myTableView reloadData];
    [sorted release];
}
 
// -------------------------------------------------------------------------------
//  didClickTableColumn:tableColumn
// -------------------------------------------------------------------------------
- (void)tableView:(NSTableView *)inTableView didClickTableColumn:(NSTableColumn *)tableColumn
{   
    NSArray *allColumns=[inTableView tableColumns];
    NSInteger i;
    for (i=0; i<[inTableView numberOfColumns]; i++) 
    {
        if ([allColumns objectAtIndex:i]!=tableColumn)
        {
            [inTableView setIndicatorImage:nil inTableColumn:[allColumns objectAtIndex:i]];
        }
    }
    [inTableView setHighlightedTableColumn:tableColumn];
    
    if ([inTableView indicatorImageInTableColumn:tableColumn] != [NSImage imageNamed:@"NSAscendingSortIndicator"])
    {
        [inTableView setIndicatorImage:[NSImage imageNamed:@"NSAscendingSortIndicator"] inTableColumn:tableColumn];  
        NSSortDescriptor *sortDesc = [[NSSortDescriptor alloc] initWithKey:[tableColumn identifier] ascending:YES];
        [self sortWithDescriptor:sortDesc];
        [sortDesc release];
    }
    else
    {
        [inTableView setIndicatorImage:[NSImage imageNamed:@"NSDescendingSortIndicator"] inTableColumn:tableColumn];
        NSSortDescriptor *sortDesc = [[NSSortDescriptor alloc] initWithKey:[tableColumn identifier] ascending:NO];
        [self sortWithDescriptor:sortDesc];
        [sortDesc release];
    }
}
 
@end



Last updated: 2009-09-11

Did this document help you? Yes It's good, but... Not helpful...