Sources/MSASSubroutines.c

// MSASSubroutines.c
//
// Original version by Jon Lansdell and Nigel Humphreys.
// 4.0 and 3.1 updates by Greg Sutton.
// Human Interface changes and GX Printing by Don Swatman
// ©Apple Computer Inc 1996, all rights reserved.
 
#include "MSASSubroutines.h"
 
#include <AppleScript.h>
#include <Resources.h>
#include <AERegistry.h>
#include <ASRegistry.h>
#include <TextUtils.h>
 
#ifdef THINK_C
    #include "PLStrs.h"
#else
    #include <PLStringFuncs.h>
#endif
 
#include "MSAppleEvents.h"
#include "MSAEUtils.h"
#include "MSGlobals.h"
#include "MSWindow.h"
 
 
const short kSEScriptID = 128;
 
extern ComponentInstance gScriptingComponent;
 
#ifdef THINK_C
    extern pascal StringPtr PLstrcpy(StringPtr str1, StringPtr str2);
    extern pascal StringPtr PLstrcat(StringPtr str1, StringPtr str2);
#endif
 
AEIdleUPP       gAEIdleUPP;
 
OSAID           gScript1ID;
OSAID           gScript2ID;
// Script 3 targets a script application - its script is not loaded
OSAID           gScript4ID;
OSAID           gScript5ID;
 
// Variables to target script application
ProcessSerialNumber gScriptAppPSN;
AEDesc              gScriptAppAddress;
 
Str31           gScript1Name = "\pscript shift";
Str31           gScript2Name = "\pscript datestring";
Str31           gScript3Name = "\pscript topseeturvee";
Str31           gScript4Name = "\pscript changecreator";
Str31           gScript5Name = "\pscript get/set selection";
 
 
// Launch an application into the background given an FSSpec
 
OSErr FSSpecLaunchApplication(const FSSpec *fileSpec, ProcessSerialNumber *PSN)
{
    LaunchParamBlockRec launchRec;
    OSErr               err;
 
    launchRec.launchBlockID = extendedBlock;
    launchRec.launchEPBLength = extendedBlockLen;
    launchRec.launchFileFlags = 0;
    launchRec.launchControlFlags = launchContinue + launchNoFileFlags + launchDontSwitch;
    launchRec.launchAppSpec = (FSSpecPtr)fileSpec;
    launchRec.launchAppParameters = nil;
    
    err = LaunchApplication(&launchRec);
 
    if (err == noErr)
        *PSN = launchRec.launchProcessSN;
        
    return(err);
}
 
// Set up the scripts we can send AppleEvents to them.
// Launch the script application.
 
void    SetUpScripts( void )
{
    FSSpec      aSpec;
    OSErr       err;
 
    aSpec.vRefNum = gAppRec.theSpec.vRefNum;
    aSpec.parID = gAppRec.theSpec.parID;
 
    gAEIdleUPP = NewAEIdleProc(IdleProc);   // Universal Procedure - only used in here
 
    PLstrcpy( aSpec.name, gScript1Name );
    (void)LoadScriptFromResFile( &aSpec, kSEScriptID, &gScript1ID );
 
    PLstrcpy( aSpec.name, gScript2Name );
    (void)LoadScriptFromResFile( &aSpec, kSEScriptID, &gScript2ID );
 
    PLstrcpy( aSpec.name, gScript3Name );
    err = FSSpecLaunchApplication( &aSpec, &gScriptAppPSN );
    if (err == noErr) 
        AECreateDesc( typeProcessSerialNumber,(Ptr)&gScriptAppPSN, 
                        sizeof( ProcessSerialNumber ), &gScriptAppAddress );
    else
        gScriptAppAddress.dataHandle = NULL;
 
    PLstrcpy( aSpec.name, gScript4Name );
    (void)LoadScriptFromResFile( &aSpec, kSEScriptID, &gScript4ID );
 
    PLstrcpy( aSpec.name, gScript5Name );
    (void)LoadScriptFromResFile( &aSpec, kSEScriptID, &gScript5ID );
 
}
 
// Given a FileSpec open the resource fork and Load the script resource of resID.
// The resource file will be closed afterwards.
 
OSErr   LoadScriptFromResFile( FSSpec *theSpec, short theResID, OSAID *theScriptID )
{
    short       aFileRef;
    OSErr       err;
 
    *theScriptID = kOSANullScript;
        
    aFileRef = FSpOpenResFile( theSpec, fsRdPerm );
    err = ResError( );
 
    
    if ( noErr == err && aFileRef > 0 )
    {
        err = LoadScriptFromResFileRef( aFileRef, theResID, theScriptID );
        
        CloseResFile( aFileRef );
    }
   
    return err;
}
 
// Given a file reference number for a resource file, and a resource ID
//  for a 'scpt' resource, this routine loads the already compiled script
//  and sets theScriptID to the components reference for it.
 
OSErr   LoadScriptFromResFileRef( short theRefNum, short theResID, OSAID *theScriptID )
{
    short   saveRefNum = CurResFile();  // save current resource
    AEDesc  aScriptDesc = { typeNull, NULL };
    Handle  aHandle;
    OSErr   err;
    
    *theScriptID = kOSANullScript;      // NULL before we do anything
    
    if ( ! theRefNum )
        return errAENoSuchObject;
    
    UseResFile( theRefNum );            // set this resource to be current
 
    aHandle = (Handle)Get1Resource( kOSAScriptResourceType, theResID );
    err = ResError( );
    if ( noErr != err ) goto done;
 
    HLock(aHandle);
    err = AECreateDesc( typeOSAGenericStorage, (Ptr)*aHandle,
                            GetHandleSize( aHandle ), &aScriptDesc );
    HUnlock(aHandle);
    if ( noErr != err ) goto done;
    
    err = OSALoad( gScriptingComponent, &aScriptDesc,
                                    kOSAModeNull, theScriptID );
    
done:
    if ( aHandle )
        ReleaseResource( aHandle );
    UseResFile( saveRefNum );           // reset back to resource previously set
    
    (void)AEDisposeDesc( &aScriptDesc );
    
    return err;
}
 
// Store the scipt ID as a 'scpt' resource, in file given, with the ID given.
 
OSErr   StoreScriptToResFile(FSSpec *theSpec, short theResID, OSAID theScriptID, StringPtr theResName )
{
    short       aFileRef;
    OSErr       err;
    
    if ( theScriptID == kOSANullScript )
        return errAENoSuchObject;
 
    aFileRef = FSpOpenResFile( theSpec, fsWrPerm );
    err = ResError( );
    
    if ( noErr == err && aFileRef > 0 )
    {
        err = StoreScriptToResFileRef( aFileRef, theResID, theScriptID, theResName );
        
        CloseResFile( aFileRef );
    }
    
    return err;
}
 
 
OSErr   StoreScriptToResFileRef( short theFileRef, short theResID, OSAID theScriptID, StringPtr theResName )
{
    short       saveRefNum = CurResFile();  // save current resource file
    AEDesc      scriptData = { typeNull,NULL };
    Handle      aHandle;
    OSErr       err;
    
    if ( kOSANullScript == theScriptID )
        return noErr;   // No script to store so that's okay
    
    if ( ! theFileRef )
        return fnfErr;
    
    UseResFile( theFileRef );       // set this resource to be current
    
    err = (OSErr)OSAStore( gScriptingComponent, theScriptID,
                        typeOSAGenericStorage, kOSAModeNull, &scriptData );
    if ( noErr != err ) goto done;
    
            // Write over current script
    aHandle = Get1Resource( kOSAScriptResourceType, theResID );
    if ( aHandle )
    {
        RemoveResource( aHandle );  // Disposed of in current resource file
        err = ResError( );
        if ( noErr != err ) goto done;
    }
 
    aHandle = scriptData.dataHandle;
    HLock( aHandle );
    HandToHand( &aHandle );                              
    AddResource( aHandle, typeOSAGenericStorage, theResID, theResName );
    HUnlock( aHandle );
    err = ResError( );
 
done:       
    UpdateResFile( saveRefNum );
 
    (void)AEDisposeDesc( &scriptData );
 
    return err;
}
 
// Quit the script application, store changes to scripts and dispose of scripts.
 
OSErr CleanUpAEScripts(void)
{
    AppleEvent      myAppleEvent = {typeNull,NULL},
                    ignoreReply = {typeNull,NULL};
    FSSpec          fileSpec;
    OSErr           err;
    
                // Quit the script application we launched
    if (gScriptAppAddress.dataHandle)
    {
        err = AECreateAppleEvent(kCoreEventClass, kAEQuitApplication, &gScriptAppAddress,
                                                                        0, 0, &myAppleEvent);
        
        if (err == noErr)
            err = AESend(&myAppleEvent, &ignoreReply, kAENoReply + kAEAlwaysInteract,
                                    kAENormalPriority, kAEDefaultTimeout, NULL, NULL);
        
        AEDisposeDesc(&myAppleEvent);
    }
 
        // Store the scripts used - this saves any changed properties and aliases
    fileSpec.vRefNum = gAppRec.theSpec.vRefNum;
    fileSpec.parID = gAppRec.theSpec.parID;
 
    PLstrcpy( fileSpec.name, gScript1Name );
    StoreScriptToResFile( &fileSpec, kSEScriptID, gScript1ID, gScript1Name );
 
    PLstrcpy( fileSpec.name, gScript2Name );
    StoreScriptToResFile( &fileSpec, kSEScriptID, gScript2ID, gScript2Name );
 
    PLstrcpy( fileSpec.name, gScript4Name );
    StoreScriptToResFile( &fileSpec, kSEScriptID, gScript4ID, gScript4Name );
 
    PLstrcpy( fileSpec.name, gScript5Name );
    StoreScriptToResFile( &fileSpec, kSEScriptID, gScript5ID, gScript5Name );
 
    (void)OSADispose( gScriptingComponent, gScript1ID );
    (void)OSADispose( gScriptingComponent, gScript2ID );
    (void)OSADispose( gScriptingComponent, gScript4ID );
    (void)OSADispose( gScriptingComponent, gScript5ID );
    
    return(err);
}
 
 
//  Calls a script subroutine that takes two window names or numbers.
//  If a window doesn't exist then it is created, one window is then
//  moved below the other.
//  Uses predefined label parameters.
//      on shift around winMove below winStay
 
OSErr ExecuteScript1(DPtr theDoc)
{
#ifdef __MWERKS__
    #pragma unused (theDoc)
#endif
 
    AppleEvent      myAppleEvent = {typeNull,NULL},
                    ignoreReply = {typeNull,NULL};
    AEDesc          selfAddress;
    OSErr           myErr;
    Str255          handlerName = "\pshift",
                    below = "\pBelow",
                    above = "\pAbove";
    
    if (gScript1ID == kOSANullScript)
        return(noErr);
    
    myErr = MakeSelfAddress(&selfAddress);
    if (myErr != noErr) return noErr; 
 
            // Set up a subroutine AppleEvent
    myErr = AECreateAppleEvent(kASAppleScriptSuite, kASSubroutineEvent, &selfAddress,
                                                                 kAutoGenerateReturnID, kAnyTransactionID, &myAppleEvent);
   
        // Add the name of the subroutine
    if (myErr == noErr)
        myErr = AEPutParamPtr(&myAppleEvent, keyASSubroutineName, typeChar, 
                                                                (Ptr)&handlerName[1], handlerName[0]);
        
        // Add the first predefined label parameter                                             
    if (myErr == noErr)
        myErr = AEPutParamPtr(&myAppleEvent, keyASPrepositionAround, typeChar,  
                                                                (Ptr)&below[1], below[0]);
                                                                    
        // Add the second predefined label parameter                                                
    if (myErr == noErr)
        myErr = AEPutParamPtr(&myAppleEvent, keyASPrepositionBelow, typeChar,   
                                                                (Ptr)&above[1], above[0]);
        
    if (myErr == noErr)
        myErr = OSADoEvent(gScriptingComponent, &myAppleEvent, gScript1ID,
                                        kOSAModeAlwaysInteract, &ignoreReply);
                                        
    (void)AEDisposeDesc( &selfAddress );
    (void)AEDisposeDesc( &myAppleEvent );
    (void)AEDisposeDesc( &ignoreReply );
    
    return(myErr);
}
    
//  Calls a subroutine that returns the date and/or time depending
//  on parameters sent.
//  Uses subroutine defined label parameters.
//      on datestring given wDate:fDate, wTime:fTime
 
OSErr ExecuteScript2(DPtr theDoc)
{
#ifdef __MWERKS__
    #pragma unused (theDoc)
#endif
 
    AppleEvent      myAppleEvent = {typeNull,NULL},
                    myReply = {typeNull,NULL};      // Need to NULL for OSADoEvent() routine
    AEDesc          selfAddress,
                    textDesc;
    AEDescList      paramList = {typeNull,NULL};
    OSErr           myErr;
    Str255          pStr = "\pdatestring";
    
    if (gScript2ID == kOSANullScript)
        return(noErr);
 
    myErr = MakeSelfAddress(&selfAddress);
    if (myErr != noErr) return noErr;
 
                // Create an AppleScript subroutine AppleEvent
    myErr = AECreateAppleEvent(kASAppleScriptSuite, kASSubroutineEvent, &selfAddress,
                                                                 kAutoGenerateReturnID, kAnyTransactionID, &myAppleEvent);
   
            // Specify the routine by it's name
    if (myErr == noErr)
        myErr = AEPutParamPtr(&myAppleEvent, keyASSubroutineName, typeChar, 
                                                                (Ptr)&pStr[1], pStr[0]);
 
            // Create list for user defined label parameters
    if (myErr == noErr)
        myErr = AECreateList(NULL, 0 , false, &paramList);
 
    if (myErr == noErr)
    {           // Make sure label name is in lower case
        PLstrcpy(pStr,"\pwdate");
        myErr = AEPutPtr(&paramList, 0 ,typeChar ,(Ptr)&pStr[1],  pStr[0]);
        if (myErr == noErr) myErr = AEPutPtr(&paramList, 0, typeTrue , NULL,  0);
    }
 
    if (myErr == noErr)
    {
        PLstrcpy(pStr,"\pwtime");
        myErr = AEPutPtr(&paramList, 0 ,typeChar ,(Ptr)&pStr[1],  pStr[0]);
        if (myErr == noErr)  AEPutPtr(&paramList, 0, typeFalse , NULL,  0);
    }
 
            // Add list to AppleEvent
    if (myErr == noErr)
        myErr = AEPutParamDesc(&myAppleEvent, keyASUserRecordFields, &paramList);
 
    if (myErr == noErr)
        myErr = OSADoEvent(gScriptingComponent, &myAppleEvent, gScript2ID,
                                                    kOSAModeAlwaysInteract, &myReply);
      
    AEDisposeDesc(&selfAddress);
    AEDisposeDesc(&myAppleEvent);
    AEDisposeDesc(&paramList);
    
    if (myErr == noErr)
        myErr = GetTextDescFromReply(&myReply, &textDesc);
 
    if (myErr == noErr)
        myErr = SetSelection(&textDesc);
        
    AEDisposeDesc(&textDesc);
    AEDisposeDesc(&myReply);
        
    return(myErr);
}
 
//  First of all gets the current selection. Then calls a script application
//  subroutine to twist the text in the selection. Finally it sets the selection
//  with the result.
//  Uses a positional parameter.
//      on topseeturvee(someText)
 
OSErr ExecuteScript3(DPtr theDoc)
{
#ifdef __MWERKS__
    #pragma unused (theDoc)
#endif
 
    AppleEvent      myAppleEvent = {typeNull,NULL},
                    myReply = {typeNull,NULL};
    AEDescList      paramList = {typeNull,NULL},
                    textDesc;
    OSErr           myErr;
    Str255          pStr = "\ptopseeturvee";
    
    if (! gScriptAppAddress.dataHandle)
        return(noErr);
        
        // Get the current selection of the front window
    myErr = GetSelection(&textDesc);
    
        // Create a subroutine AppleEvent
    if (myErr == noErr)
        myErr = AECreateAppleEvent(kASAppleScriptSuite, kASSubroutineEvent, &gScriptAppAddress,
                                                                 kAutoGenerateReturnID, kAnyTransactionID, &myAppleEvent);
   
        // Add the name of the subroutine
    if (myErr == noErr)
        myErr = AEPutParamPtr(&myAppleEvent, keyASSubroutineName, typeChar, 
                                                                (Ptr)&pStr[1], pStr[0]);
 
        // Create positional parameter list
    if (myErr == noErr)
        myErr = AECreateList(NULL, 0 ,false, &paramList);
        
        // Add textDesc as positional parameter
    if (myErr == noErr)
        myErr = AEPutDesc(&paramList, 0, &textDesc);
 
        // Add positional parameter list
    if (myErr == noErr)
        myErr = AEPutParamDesc(&myAppleEvent, keyDirectObject, &paramList);
        
    if (myErr == noErr)
        myErr = AESend(&myAppleEvent, &myReply, kAEWaitReply + kAEAlwaysInteract,
                                    kAENormalPriority, kAEDefaultTimeout, gAEIdleUPP, NULL);
      
    AEDisposeDesc(&myAppleEvent);
    AEDisposeDesc(&paramList);
        // Dispose of textDesc before we get one in reply
    AEDisposeDesc(&textDesc);
    
    if (myErr == noErr)
        myErr = GetTextDescFromReply(&myReply, &textDesc);
 
    if (myErr == noErr)
    {
        myErr = SetSelection(&textDesc);
        AEDisposeDesc(&textDesc);
    }
        
    AEDisposeDesc(&myReply);
        
    return(myErr);
}
 
//  Calls script subroutine that changes a given file's creator
//  to the creator specified.
//  Uses direct and predefined label parameters.
//      on changecreator of aSpec into aCreator
 
OSErr ExecuteScript4(DPtr theDoc)
{
    AppleEvent      myAppleEvent = {typeNull,NULL},
                    ignoreReply = {typeNull,NULL};
    AEDesc          selfAddress;
    AEDescList      paramList = {typeNull,NULL};
    OSErr           myErr;
    Str255          pStr = "\pchangecreator";
    
    if (gScript4ID == kOSANullScript)
        return(noErr);
 
    myErr = MakeSelfAddress(&selfAddress);
    if (myErr != noErr) return myErr;
        
        // Create a subroutine AppleEvent
    myErr = AECreateAppleEvent(kASAppleScriptSuite, kASSubroutineEvent, &selfAddress,
                                                kAutoGenerateReturnID, kAnyTransactionID, &myAppleEvent);
   
        // Add the name of the subroutine
    if (myErr == noErr)
        myErr = AEPutParamPtr(&myAppleEvent, keyASSubroutineName, typeChar, 
                                                (Ptr)&pStr[1], pStr[0]);
        
        // Add the direct parameter                                             
    if (myErr == noErr)
        myErr = AEPutParamPtr(&myAppleEvent, keyDirectObject, typeFSS,  
                                                (Ptr)&(theDoc->theFSSpec), sizeof(FSSpec));
 
        // Add the subroutine parameter label                                           
    if (myErr == noErr)
    {
        PLstrcpy(pStr, "\pToyS");
        myErr = AEPutParamPtr(&myAppleEvent, keyASPrepositionInto, typeChar,    
                                                        (Ptr)&pStr[1], pStr[0]);
    }
 
        
    if (myErr == noErr)
        myErr = OSADoEvent(gScriptingComponent, &myAppleEvent, gScript4ID,
                                                kOSAModeAlwaysInteract, &ignoreReply);
      
    AEDisposeDesc(&selfAddress);
    AEDisposeDesc(&myAppleEvent);
    AEDisposeDesc(&ignoreReply);
    
    return(myErr);
}
 
 
//  Calls script subroutine to get this application's selection.
//  Uses no parameters except for subroutine name.
//      on getselection()
 
OSErr   GetSelection(AEDesc *textDesc)
{
    AppleEvent      myAppleEvent = {typeNull,NULL},
                    myReply = {typeNull,NULL};
    AEDesc          selfAddress;
    OSErr           myErr;
    Str255          pStr = "\pgetselection";
    
    if (gScript5ID == kOSANullScript)
        return(noErr);
 
    myErr = MakeSelfAddress(&selfAddress);
    if (myErr != noErr) return myErr;
        
                // Create an AppleScript subroutine AppleEvent
    myErr = AECreateAppleEvent(kASAppleScriptSuite, kASSubroutineEvent, &selfAddress,
                                                                 kAutoGenerateReturnID, kAnyTransactionID, &myAppleEvent);
   
            // Specify the routine by it's name
    if (myErr == noErr)
        myErr = AEPutParamPtr(&myAppleEvent, keyASSubroutineName, typeChar, 
                                                                (Ptr)&pStr[1], pStr[0]);
 
    if (myErr == noErr)
        myErr = OSADoEvent(gScriptingComponent, &myAppleEvent, gScript5ID,
                                                    kOSAModeAlwaysInteract, &myReply);
 
    myErr = GetTextDescFromReply(&myReply, textDesc);
    
    AEDisposeDesc(&selfAddress);
    AEDisposeDesc(&myAppleEvent);
    AEDisposeDesc(&myReply);
        
    return(myErr);
}
 
 
//  Calls script subroutine to set this application's selection.
//  Uses a positional parameter.
//      setselection(aString)
 
OSErr   SetSelection(AEDesc *textDesc)
{
    AppleEvent      myAppleEvent = {typeNull,NULL},
                    myReply = {typeNull,NULL};
    AEDesc          selfAddress;
    AEDescList      paramList = {typeNull,NULL};
    OSErr           myErr;
    Str255          pStr = "\psetselection";
    
    if (gScript5ID == kOSANullScript)
        return(noErr);
 
    myErr = MakeSelfAddress(&selfAddress);
    if (myErr != noErr) return myErr;
    
            // Create an AppleScript subroutine AppleEvent
    myErr = AECreateAppleEvent(kASAppleScriptSuite, kASSubroutineEvent, &selfAddress,
                                                                 kAutoGenerateReturnID, kAnyTransactionID, &myAppleEvent);
   
            // Specify the routine by it's name
    if (myErr == noErr)
        myErr = AEPutParamPtr(&myAppleEvent, keyASSubroutineName, typeChar, 
                                                                (Ptr)&pStr[1], pStr[0]);
 
            // Create positional parameter list
    if (myErr == noErr)
        myErr = AECreateList(NULL, 0 ,false, &paramList);
        
            // Add textDesc as positional parameter
    if (myErr == noErr)
        myErr = AEPutDesc(&paramList, 0, textDesc);
 
            // Add positional parameter list
    if (myErr == noErr)
        myErr = AEPutParamDesc(&myAppleEvent, keyDirectObject, &paramList);
 
    if (myErr == noErr)
        myErr = OSADoEvent(gScriptingComponent, &myAppleEvent, gScript5ID,
                                                    kOSAModeAlwaysInteract, &myReply);
    
    AEDisposeDesc(&selfAddress);
    AEDisposeDesc(&myAppleEvent);
    AEDisposeDesc(&paramList);
    AEDisposeDesc(&myReply);
        
    return(myErr);
}
 
// Takes a reply and tries to get a text descriptor from it.
 
OSErr   GetTextDescFromReply(AEDesc *aReply, AEDesc *textDesc)
{
    OSErr       myErr;
 
    textDesc->descriptorType = typeNull;
    textDesc->dataHandle = NULL;
 
    myErr = AEGetParamDesc(aReply, keyAEResult, typeChar, textDesc);
    
    return(myErr);
}
 
 
void    EnableAEScriptItems(Boolean fEnable)
{
    SetMenuItemState ( (fEnable && kOSANullScript != gScript1ID), myMenus[subroutineM], cScript1);
    SetMenuItemState ( (fEnable && kOSANullScript != gScript2ID), myMenus[subroutineM], cScript2);
    SetMenuItemState ( (fEnable && gScriptAppAddress.dataHandle), myMenus[subroutineM], cScript3);
    SetMenuItemState ( (fEnable && kOSANullScript != gScript4ID), myMenus[subroutineM], cScript4);
}
 
    // IdleProc for AESend 
pascal Boolean IdleProc(EventRecord *myEvent, long *sleep, RgnHandle *mouseRgn)
{
    WindowPtr   theWindow;
    Boolean     activate;
 
    switch ( myEvent->what )
    {
        case nullEvent:
            *sleep = 0;                     // no null processing in this sample
            mouseRgn = nil;
            break;
                
        case activateEvt:
            activate = ((myEvent->modifiers & activeFlag) != 0);
            theWindow = (WindowPtr)myEvent->message;
            DoActivate(theWindow, activate);
            break;
 
        case updateEvt:
            DoUpdate( (WindowPtr)myEvent->message );
            break;
 
        case osEvt:
            if ( ( myEvent->message >> 24 ) & suspendResumeMessage )    // suspend or resume
            {
                gInBackground = ((myEvent->message & resumeFlag) == 0);
                DoActivate(FrontWindow(), ! gInBackground);
            }
            break;
            
        default:
            SysBeep( 1 );
            break;
    }
    
    return false;   // I'll wait forever
}