MainWindow.c

 
 
/*
    File:       MainWindow.c
    
    Description:
    routines for managing the main window.
 
    Author:     JM
 
    Copyright:  Copyright (c) 2003 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.
                
    Change History (most recent first):
        Fri, Aug 29, 2000 -- created
*/
 
#include "MainWindow.h"
#include "ScriptSupport.h"
#include "MyScriptsGlue.h"
#include "FinderCommentWindow.h"
 
 
    /* We use an AEDescList to contain the items that are displayed
    in the list in the main window.  
    
    The scripts that construct this list create a list formatted
    as a list of two element lists (the first element being the name
    of a file/folder and the second being an alias referencing the
    file/folder).  Here is the general format:
    {
        { name of item 1, alias to item 1 },
        { name of item 2, alias to item 2 },
        ...
        { name of item n, alias to item n }
    }
    
    The handlers getfolderItems and  getfinderselection create this
    list. They are defined in the file AddOnScripts.applescript.  For
    more information, take a look at that file.
        
    In the databrowser, we use item id numbers numbered starting
    at 1, 2, 3, ..., n.  These numbers also used as indexes into
    the list stored in this array. */
    
AEDescList gFolderItems; /* a list pairs (see above). */
 
Boolean gFolderItemsExists = false; /* does the list exist? */
 
 
    /* DataBrowserDataCallback is a callback we defined for providing
    data to the data browser while it is drawing the list displayed
    in the main window.  Essentially, this routine provides a translation
    function between data browser item id numbers and the strings we
    want to display in the list.  Item id numbers are set up so they are
    indexes into the list stored in gFolderItems. */
static pascal OSStatus DataBrowserDataCallback(ControlRef browser, DataBrowserItemID itemID,
        DataBrowserPropertyID property, DataBrowserItemDataRef itemData,
        Boolean changeValue) {
    OSStatus err = noErr;
    switch ( property ) {
    
        case 'item':
                /* in interface builder we defined one column with the property
                id value of 'item'.  Here we are being asked to either retrieve the
                string to be displayed for the itemID in the column identified by
                this property id. */
            if ( changeValue ) {
                    /* this would involve renaming the file. we don't want to do 
                    that here. */
                err = errDataBrowserPropertyNotSupported;
            } else {
                CFStringRef text;
                AEKeyword theAEKeyword;
                AEDescList nthPair;
                AEDesc nthName;
                
                    /* get the requested pair from the list of items.  Here the itemID is
                    used as the index. */
                err = AEGetNthDesc( &gFolderItems, itemID, typeWildCard, &theAEKeyword, &nthPair );
                if ( noErr == err ) {
                
                        /* from the pair of items returned, we know that the first item in
                        the pair will be the 'display name' of the item in the list. */
                    err = AEGetNthDesc( &nthPair, 1, typeWildCard, &theAEKeyword, &nthName);
                    if ( noErr == err ) {
                        
                            /* convert the name into a string for the data browser to use */
                        err = AEDescToCFString( &nthName, &text );
                        if ( noErr == err ) {
                        
                                /* send the string to the data browser */
                            err = SetDataBrowserItemDataText( itemData, text );
                            
                                /* the data browser makes a copy, so we can
                                release our reference */
                            CFRelease(text);
                        }
                            /* done with the name descriptor */
                        AEDisposeDesc(&nthName);
                    }
                        /* done with the pair's descriptor */
                    AEDisposeDesc(&nthPair);
                }
            }
            break;
            
                /* the remainder of these are essentially for
                handling generic data browser inquiries and they're not
                essential for understanding the sample.  comments included
                for the interested... */
        case kDataBrowserItemIsActiveProperty:
            if ( ! changeValue ) /* is it active? yes */
                err = SetDataBrowserItemDataBooleanValue( itemData, true);
            break;
        case kDataBrowserItemIsSelectableProperty:
            if ( ! changeValue ) /* can we select it? yes */
                err = SetDataBrowserItemDataBooleanValue( itemData, true);
            break;
        case kDataBrowserContainerIsSortableProperty:
        case kDataBrowserItemIsEditableProperty:
        case kDataBrowserItemIsContainerProperty:
            if ( ! changeValue ) /* can we edit it, sort it, or put things in it? no */
                err = SetDataBrowserItemDataBooleanValue( itemData, false);
            break;
            
        default: /* unrecognized property */
            err = errDataBrowserPropertyNotSupported;
            break;
    }
        /* send result */
    return err;
}
 
 
 
    /* DataBrowserNotificationCallback is called by the data browser
    control to notify clients of changes in the state of the control.
    We are only interested in cases where the user double clicks
    on an item.  In that case, we open the item that was clicked on
    and display the finder comment for editing in a finder comment window. */
static pascal void DataBrowserNotificationCallback(
        ControlRef browser,
        DataBrowserItemID item,
        DataBrowserItemNotification message) {
    if ( message == kDataBrowserItemDoubleClicked ) {
        AliasHandle localAlias;
        AEKeyword theAEKeyword;
        AEDescList nthPair;
        AEDesc nthName;
        OSStatus err;
        
            /* retrieve the nth pair from the list of items being displayed */
        err = AEGetNthDesc( &gFolderItems, item, typeWildCard, &theAEKeyword, &nthPair );
        if ( noErr == err ) {
        
                /* the second item in the pair is the alias record referencing
                the file on disk.  let's get that one */
            err = AEGetNthDesc( &nthPair, 2, typeWildCard, &theAEKeyword, &nthName);
            if ( noErr == err ) {
            
                    /* convert the item into an alias record */
                err = AEDescToAlias( &nthName, &localAlias );
                if ( noErr == err ) {
                
                        /* open a finder comment window to display the item. */
                    err = OpenFinderCommentWindow( localAlias );
                    
                        /* done with the alias */
                    DisposeHandle((Handle) localAlias);
                }
                
                    /* done with the alias descriptor. */
                AEDisposeDesc(&nthName);
            }
            
                /* done with the pair descriptor */
            AEDisposeDesc(&nthPair);
        }
    }
}
 
 
 
 
 
 
 
    /* MainWindowEventHandler is the Carbon Event handler we install on our
    application's main window.  Here we handle all of the events we are
    interested in that can occur for the main window. */
static pascal OSStatus MainWindowEventHandler(EventHandlerCallRef inHandlerCallRef, EventRef inEvent, void *inUserData) {
    OSStatus err, returnedResult;
    UInt32 eclass, ekind;
    
        /* set up locals */
    eclass = GetEventClass(inEvent);
    ekind = GetEventKind(inEvent);
    
        /* default result */
    returnedResult = eventNotHandledErr;
    
        /* dispatch the event */
    if (eclass == kEventClassWindow && ekind == kEventWindowClose) {
        
            /* exit from RunApplicationEventLoop */ 
        QuitApplicationEventLoop();
        
            /* good to go */ 
        returnedResult = noErr;
        
    } else if ( eclass == kEventClassCommand && ekind == kEventProcessCommand ) {
    
        HICommandExtended command;
        err = GetEventParameter( inEvent, kEventParamDirectObject,
                                typeHICommand, NULL, sizeof(command), NULL, &command);
        if (err == noErr) {
        
            switch ( command.commandID ) {
            
            
            
                case 'SMSG': /* the "Show Message" button in the main window */
                    {   WindowRef window;
                        ControlID controlID = { 'MESG', 0};
                        ControlRef control;
                        CFStringRef theMessageString;
                        
                            /* we stored a reference to our window in
                            the user data parameter, let's use that now... */
                        window = (WindowRef) inUserData;
                        
                            /* find the messaage text control */
                        err = GetControlByID( window, &controlID, &control );
                        if ( noErr == err ) {
                        
                                /* extract the message text from the control */
                            err = GetControlData( control, kControlEntireControl,
                                            kControlEditTextCFStringTag, sizeof(theMessageString),
                                            &theMessageString, NULL);
                            if ( noErr == err ) {
                            
                                    /* call the displaymessage script handler to go about
                                    displaying the message, interacting with the user, etc... */
                                script_displaymessage(theMessageString);
                                
                                    /* done with the string now */
                                CFRelease(theMessageString);
                            }
                        }
                    }
                        /* we handled the event */
                    returnedResult = noErr;
                    break;
                    
                    
                    
                case 'GINF': /* the "Finder 'Get Info...' Comment..." button in the main window */
                    {   AliasHandle theFile;
                    
                            /* call the selectfile script to ask the user to select a file */
                        err = script_selectfile(CFSTR("Get the Finder comment for which file?"), &theFile);
                        if ( noErr == err ) {
                        
                                /* open a window to display the file. */
                            err = OpenFinderCommentWindow(theFile);
                            
                                /* done with the alias */
                            DisposeHandle((Handle) theFile);
                        }
                    }
                        /* we handled the event */
                    returnedResult = noErr;
                    break;
                    
                    
                    
 
                    
                case 'FNDR': /* the 'Get Finder Selection' button in the main window */
                case 'FOLD': /* The 'list folder...' button in the main window */
                    {   AEDescList newFolderItems;
                            /* list a folder using one of our listing scripts.  one uses
                            the finder selection, the other prompts the user for a file. */
                        if ( 'FNDR' == command.commandID ) {
                                
                                /* use the finder selection */
                            err = script_callnamedhandler("getfinderselection", &newFolderItems);
                        } else {
                        
                                /* prompt the user for a folder and list the items*/
                            err = script_getfolderitems(CFSTR("Select a folder to list."), &newFolderItems);
                        }
                        if ( noErr == err ) {
                            WindowRef window;
                            ControlID controlID = { 'LIST', 0 };
                            ControlRef control;
                            long theCount;
                            
                                /* release the old list of items */
                            if ( gFolderItemsExists ) {
                                AEDisposeDesc(&gFolderItems);
                            }
                                /* save the new items. */
                            gFolderItems = newFolderItems;
                            gFolderItemsExists = true;
                            
                                /* we stored a reference to our window in
                                the user data parameter, let's use that now... */
                            window = (WindowRef) inUserData;
                            
                                /* get the sibling 'list' control in the main window.*/
                            err = GetControlByID( window, &controlID, &control );
                            if ( noErr == err ) {
                            
                                    /* remove all of the items currently displayed in
                                    the list. */
                                RemoveDataBrowserItems( control, kDataBrowserNoItem, 
                                                    0, NULL, kDataBrowserItemNoProperty);
                                                    
                                    /* count the number of items in the list returned
                                    by our script. */
                                err = AECountItems(&gFolderItems, &theCount);
                                if ( noErr == err) {
                                
                                        /* add the new items to the data browser control.  Worthy
                                        of note is the NULL value passed to the items parameter
                                        below.  What this means to the data browser's AddDataBrowserItems
                                        routine is that it should automatically generate data item
                                        id numbers numbering them 1, 2, 3, ..., n.  Coincidentally,
                                        item indexes used by the AEDescList accessing routines use those
                                        same numbers as indexes into the list.  So, the data browser item
                                        id numbers will be used as indexes into the gFolderItems AEDescList
                                        when we want to access those items.  */
                                    err = AddDataBrowserItems( control, kDataBrowserNoItem,
                                                theCount, NULL, kDataBrowserItemNoProperty);
                                }
                            }
                        }
                    }
                        /* we handled the event */
                    returnedResult = noErr;
                    break;
                
                    
                    
                case 'SHOW': /* the 'display a file in the finder' button in the main window */
                    {   AliasHandle theFile;
                    
                            /* ask the user to select a file for display. */
                        err = script_selectfile(CFSTR("Display a file in the Finder."), &theFile);
                        if ( noErr == err ) {
                        
                                /* call the script to display the file */
                            err = script_displayfile( theFile );
                            
                                /* done with the alias */
                            DisposeHandle((Handle) theFile);
                        }
                    }
                        /* we handled the event */
                    returnedResult = noErr;
                    break;
            }
        }
    }
 
    return returnedResult;
}
 
 
 
 
    /* OpenTheMainWindow does exactly what you may
    expect that it does if you were to use its name
    as a clue. */
OSStatus OpenTheMainWindow(void) {
 
    OSStatus err;
    IBNibRef nibRef;
 
 
        /* Create a Nib reference passing the name of the nib
        file (without the .nib extension) CreateNibReference
        only searches into the application bundle. */
    err = CreateNibReference(CFSTR("main"), &nibRef);
    if ( noErr == err ) {
    
            WindowRef window;
                
            /* Then create a window. "MainWindow" is the name of the
            window object. This name is set in InterfaceBuilder when
            the nib is created. */
        err = CreateWindowFromNib(nibRef, CFSTR("MainWindow"), &window);
 
            /* install our window's close handler */
        if ( noErr == err ) {
            EventTypeSpec mainWindowCloseEvent[] = {
                { kEventClassWindow, kEventWindowClose },
                { kEventClassCommand, kEventProcessCommand } };
                    /* Install a carbon event handler on the main window. NOTE: we store
                    a copy of the window reference in the user data parameter so we can
                    get a copy of it inside of our handler. */
            err = InstallWindowEventHandler(window, NewEventHandlerUPP(MainWindowEventHandler),
                    (sizeof(mainWindowCloseEvent)/sizeof(EventTypeSpec)), mainWindowCloseEvent, window, NULL);
        }
 
 
            /* The list that is being displayed in the window requires a
            few programmatic support routines to hook it up to the program.  We'll 
            install those routines now. */
        if ( noErr == err ) {
            ControlID controlID = { 'LIST', 0 };
            ControlRef control;
            DataBrowserCallbacks dataBrowserHooks;
            
                /* retrieve the list control from the window */
            err = GetControlByID( window, &controlID, &control );
            if ( noErr == err ) {
                
                    /* initialize the callback structure to the default
                    values.  */
                dataBrowserHooks.version = kDataBrowserLatestCallbacks;
                InitDataBrowserCallbacks(&dataBrowserHooks);
                
                    /* the first hook is for providing strings displayed in the list. Essentially
                    it translates indexes referencing items in the list into the list of items
                    returned by our scripts. */
                dataBrowserHooks.u.v1.itemDataCallback = NewDataBrowserItemDataUPP(DataBrowserDataCallback);
                
                    /* the second is a notification routine we use for detecting double
                    clicks. we use double clicks for opening finder comment editing windows
                    for items in the list when they are double clicked on. */
                dataBrowserHooks.u.v1.itemNotificationCallback = NewDataBrowserItemNotificationUPP(DataBrowserNotificationCallback);
                
                    /* install the callbacks structure in the list control */
                err = SetDataBrowserCallbacks(control, &dataBrowserHooks);
            }
        }
        
            /* The window was created hidden so show it. */
        if ( noErr == err ) {
            ShowWindow( window );
        }
 
            /* done with our nib file */
        DisposeNibReference(nibRef);
    }
 
        /* we're done */
    return err;
}