The R Project SVN R

Rev

Rev 15168 | Blame | Compare with Previous | Last modification | View Log | Download | RSS feed

/*
 *  R : A Computer Language for Statistical Data Analysis
 *  File R_Event.c
 *  Copyright (C) 1998-1999  Ross Ihaka
 *                2000-2001  Stefano M. Iacus the R core team
 *
 *  This program is free software; you can redistribute it and/or modify
 *  it under the terms of the GNU General Public License as published by
 *  the Free Software Foundation; either version 2 of the License, or
 *  (at your option) any later version.
 *
 *  This program is distributed in the hope that it will be useful,
 *  but WITHOUT ANY WARRANTY; without even the implied warranty of
 *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 *  GNU General Public License for more details.
 *
 *  You should have received a copy of the GNU General Public License
 *  along with this program; if not, write to the Free Software
 *  Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
 *
 *
 *  This file is adapted from the public demos coming with the Waste library
 *  distribution:  WASTE Text Engine © 1993-2000 Marco Piovanelli.
 *
 *  This file was originally written by: Wing Kwong (Tiki), WAN 3/2/99
 *  Updated to last version of WasteLib library: Stefano M. Iacus, 2001
 *
 *  Original file was:
 *
 *  WASTE Demo Project:
 *  Dialog Utilities
 *
 *  Copyright © 1993-1998 Marco Piovanelli
 *  All Rights Reserved
 *
 *  C port by John C. Daub
 */
/*

    R_Event
    by wing kwong (Tiki), WAN 3/2/99

    Description

    This file is based on the WASTE and WASTE demo, I had do some
    modification to make it function as the way R want. The routine in
    here is used to handle event (high or low level event.)
*/

#include <AppleEvents.h>


#ifndef __AEREGISTRY__
#include <AERegistry.h>
#endif

#ifndef __DISKINIT__
#include <DiskInit.h>
#endif

#ifndef __TEXTSERVICES__
#include <TextServices.h>
#endif

#ifndef __WEDEMOAPP__
#include "RIntf.h"
#endif

#ifdef HAVE_CONFIG_H
#include <config.h>
#endif
#include "Defn.h"
#include "Graphics.h"
#include "Startup.h" /* Jago */
#include <Rdevices.h>

Boolean EventsInit = false;


SEXP R_LoadFromFile(FILE*, int);

UInt32 sSleepTime = 0; // sleep time for WaitNextEvent()
RgnHandle sMouseRgn = nil; // mouse region for WaitNextEvent()
PicHandle WinPic;
Boolean fstart = true;
SInt16 Help_Window = 1;
WindowPtr Help_Windows[MAX_NUM_H_WIN + 1];

AEIdleUPP gAEIdleUPP;
EventRecord gERecord;
Boolean gQuit, gInBackground;


#define kResumeMask             1       /* bit of message field for resume vs. suspend */


extern Graphic_Ref                 gGReference[MAX_NUM_G_WIN + 1];
extern SInt16                      gExpose;
extern Boolean                     gfinishedInput,finished;
extern WindowPtr                   Console_Window;
extern SInt16                      Edit_Window;
extern WindowPtr                   Edit_Windows[MAX_NUM_E_WIN + 1];
extern SInt32                      gChangeAble, gbuflen;
extern char*                       gbuf;
extern int                         isGraphicWindow (WindowPtr);
extern void                        doActivate(EventRecord*);
extern WindowPtr                   Working_Window;
extern char                        InitFile[256];
extern SInt16                      gTextSize;
extern RGBColor                    gTypeColour;

#define kCMDEventClass  'DCMD'  /* Event class command for MacZip */
#define kCMDEvent       'DCMD'  /* Event command                  */

void DoGenKeyDown ( const EventRecord *event, Boolean Console);
extern pascal   OSErr   FSpGetFullPath (const FSSpec*, short*, Handle*);
extern int R_SetOptionWidth(int w);
extern int R_ChooseFile(int isNewFile, char *fileBuf, int buflen);
extern OSErr   OpenSelection                    (FSSpecPtr theDoc);

pascal Boolean idleProc(EventRecord *eventIn, long *sleep, RgnHandle *mouseRgn);

/* AdjustCursor routine :
   Description :
   Used to adjust Cursor
 */
void AdjustCursor ( Point mouseLoc, RgnHandle mouseRgn )
{
    Cursor      arrow ;
    WindowRef   window ;

    // by default, set mouseRgn to the whole QuickDraw coordinate plane,
    // so that we never get mouse moved events

    SetRectRgn ( mouseRgn, -0x7FFF, -0x7FFF, 0x7FFF, 0x7FFF ) ;

#if ! TARGET_API_MAC_CARBON
    // give text services a chance to set the cursor shape
    // (this call is not needed for Carbon clients)
    if ( SetTSMCursor( mouseLoc ) )
    {
        return ;
    }
#endif

    // if there is a window open, give WEAdjustCursor an opportunity to set the cursor
    // WEAdjustCursor intersects mouseRgn (if supplied) with a region within which
    // the cursor is to retain its shape
    // (if the cursor is outside the view region, this is subtracted from mouseRgn )

    if ( ( window = FrontWindow ( ) ) != nil )
    {
        if ( WEAdjustCursor ( mouseLoc, mouseRgn, GetWindowWE ( window ) ) )
        {
            return ;
        }
    }

    // set the cursor to the arrow cursor

    SetCursor( GetQDGlobalsArrow ( & arrow ) ) ;
}

/* DoMouseDown routine :
   Description :
   Used to dispatch the mouse down event
 */

void DoPathSelect ( WindowRef window );
OSStatus SwitchToFinder ( );
OSStatus FindProcess(OSType,OSType,ProcessSerialNumber *);

OSStatus FindProcess
    (
        OSType                  inProcessType,
        OSType                  inProcessSignature,
        ProcessSerialNumber *   outPSN
    )
{
    ProcessSerialNumber     psn ;
    ProcessInfoRec          info ;

    // start at beginning of process list
    psn . lowLongOfPSN = kNoProcess ;
    psn . highLongOfPSN = kNoProcess ;

    // init process info record
    BlockZero ( & info, sizeof ( info ) ) ;
    info . processInfoLength = sizeof ( info ) ;

    // walk the process list, looking for the given creator
    while ( ( GetNextProcess ( & psn ) == noErr ) &&
            ( GetProcessInformation ( & psn, & info ) == noErr ) )
    {
        if ( info . processSignature == inProcessSignature )
        {
            if ( ( inProcessType == typeWildCard ) || ( inProcessType == info . processType ) )
            {
                if ( outPSN )
                {
                    * outPSN = psn ;
                }
                return noErr ;
            }
        }
    }

    return procNotFound ;
}

OSStatus SwitchToFinder ( )
{
    ProcessSerialNumber     finderPSN ;
    OSStatus                err ;

    //  get Finder PSN
    if ( ( err = FindProcess ( FOUR_CHAR_CODE ( 'FNDR' ), FOUR_CHAR_CODE ( 'MACS' ), & finderPSN ) ) != noErr )
    {
        goto cleanup ;
    }

    //  make it the frontmost process
    err = SetFrontProcess ( & finderPSN ) ;

cleanup :
    //  return result code
    return err ;
}

void DoPathSelect ( WindowRef window )
{
    SInt32  menuChoice = 0 ;

    if ( ( WindowPathSelect ( window, nil, & menuChoice ) == noErr ) &&
         ( ( menuChoice & 0x0000FFFF ) > 1 ) )
    {
        //  bring the Finder to the foreground
        SwitchToFinder ( ) ;
    }
}

void DoMouseDown ( const EventRecord * event )
{
    //WindowRef we;
    WEReference we;
    WindowPtr   window ;
    SInt16      partCode ;
    SInt32      selStart, selEnd;
    // find out where this click when down in

    partCode = FindWindow ( event -> where, & window ) ;   
    SelectWindow(window);

    // dispatch on partCode

    switch ( partCode )
    {
        case inMenuBar :
            PrepareMenus ( ) ;
            DoMenuChoice ( MenuSelect ( event -> where ), event -> modifiers , window) ;
        break;

        case inContent :
            DoContent ( event -> where, event, window );
            SelectWindow ( window ) ;
            we = GetWindowWE(window);
                
            if(window == Console_Window){
                WEGetSelection ( & selStart, & selEnd, we );
                if(selStart == selEnd)
                    WESetSelection(WEGetTextLength(we), WEGetTextLength(we), we);
            }
        break;
        
        case inDrag :
        {
            if ( window != FrontWindow ( ) )
            {
                if ( ( event->modifiers & cmdKey ) == 0 )
                {
                    SelectWindow ( window ) ;
                }
            }
            else if ( IsWindowPathSelectClick ( window, ( EventRecord * ) event ) )
            {
                DoPathSelect ( window ) ;
                break ;
            }

            DoDrag ( event -> where, window ) ;
            break;
        }

        case inGrow :
        {
            DoGrow ( event -> where, window ) ;
            break;
        }

        case inGoAway :
        {
            if (FrontWindow() == Console_Window)
             DoQuit( savingAsk );
    
            if ( TrackGoAway ( window, event -> where ) )
            {
                if(!finished)
                DoClose ( closingWindow /*kNavSaveChangesClosingDocument*/, savingYes, window ) ;
                else
                 DoClose ( closingWindow /*kNavSaveChangesClosingDocument*/, savingAsk, window ) ;
                
            }
            break ;
        }

        case inZoomIn :
        case inZoomOut :
        {
            if ( TrackBox ( window, event -> where, partCode ) )
            {
                DoZoom ( partCode, window ) ;
            }
            break ;
        }

        case inProxyIcon:
        {
            //  in order to start a proxy icon drag, the window must be active
            //  and the proxy icon must be enabled
            if ( window == FrontWindow ( ) )
            {
                if ( ! IsWindowModified ( window ) )
                {
                    //  if the mouse isn't held down on the proxy icon for a certain delay,
                    //  this gesture should be interpreted as an attempt to drag the window
                    if ( TrackWindowProxyDrag ( window, event->where ) != errUserWantsToDragWindow )
                    {
                        break ;
                    }
                }
            }
            else
            {
                //  clicking the proxy icon of an inactive window should select
                //  the window unless the command key is held down
                if ( ( event->modifiers & cmdKey ) == 0 )
                {
                    SelectWindow ( window ) ;
                }
            }

            DoDrag ( event->where, window ) ;
            break ;
        }

#if !TARGET_API_MAC_CARBON
        case inSysWindow :
        {
            SystemClick ( event, window ) ;
            break ;
        }
#endif
    }   // switch
}




/* DoKeyDown routine :
   Description :
 */


void DoKeyDown ( const EventRecord *event )
{
    WEReference we ;
    SInt32   StartPos1, EndPos1, StartPos2, EndPos2;

    if (FrontWindow() == Console_Window){
        we = GetWindowWE ( Console_Window ) ;
        WEGetSelection ( &StartPos1, &EndPos1, we );
        DoGenKeyDown ( event, false);
        WEGetSelection(&StartPos2, &EndPos2, we);
        Change_Color_Range(StartPos1, StartPos2, gTypeColour.red,
              gTypeColour.green, gTypeColour.blue, we);

        WESetSelection ( StartPos2, EndPos2, we );
        
    } 
    else 
        DoGenKeyDown ( event, false);
    
}




/* DoGenKeyDown routine :
   Description :
   It will be called by the DoKeyDown event, and sometime we need to
   directly insert a "\r" event of the console window.
 */
void DoGenKeyDown ( const EventRecord *event, Boolean Console)
{
    SInt16 key;
    Boolean isCmdKey ;
    pascal Handle  TextHdl=NULL;
    SInt32 textLength;
    WEReference we ;
    Ptr   TextPtr;
    SInt32 selStart, selEnd ;
    Point               where = {-1, -1};
    char     cha;
    char     testbuf[256];
    // extract character code from event message
    if (Console){
    we = GetWindowWE ( Console_Window);
    }else{
    we = GetWindowWE ( FrontWindow());
    }
    key = ( event -> message & charCodeMask );


    // is this a command+key combo?

    isCmdKey = ( ( event -> modifiers & cmdKey ) != 0 );

    // map function keys to the equivalent command+key combos
    // note that all fuction keys generate the same code, i.e. 0x10

    if ( key == 0x10 )
    {
    isCmdKey = true ;

    switch ( ( event -> message & keyCodeMask ) >> 8 )
    {
    case keyF1:
    {
        //  R_ShowFile("Macintosh HD:Desktop Folder:R copy 3:R:Projects:R help", "Happy");
        key = 'c';
        break ;
    }

    case keyF2:
    {
        RnWrite(testbuf, R_ChooseFile(true, testbuf, 256));
        // key = 0 ;
                //key = 'v';
        break ;
    }

    case keyF3:
    {
        RnWrite(testbuf ,R_ChooseFile(false, testbuf, 256));
        key = 0;
        break ;
    }

    case keyF4:
    {
        ErrorDia("No warning is the best");
        key = 0 ;
        break ;
    }

    default:
    {

        key = 0 ;
        break ;
    }
    }   // switch
    }   // if

    // command + printable character combos are routed to MenuKey( )
    // but be sure to pass command + arrow key combos to WEKey( )

    if ( isCmdKey && (key >= 0x20 ) )
    {
    PrepareMenus ( );
    DoMenuChoice ( MenuKey ( key ), event -> modifiers ,FrontWindow()) ;
    }
    else
    {

    if ((FrontWindow () == Console_Window) || (Console)){
        WEGetSelection ( & selStart, & selEnd, we );
        if ((selEnd < gChangeAble) && (key >=0x20))
        SysBeep(10);
        else{
        if(key == kDel){
            if (selEnd < gChangeAble)
            SysBeep(10);
            else
            DoKey ( key, event ) ;
        }else
            if(key == kBs){
            if (selEnd <= gChangeAble)
                SysBeep(10);
            else
                DoKey ( key, event ) ;
            }else{
            if ((key == 0x1E) && ( gChangeAble <= selStart))
                do_Down_Array();

            else{
                if ((key == 0x1F) && ( gChangeAble <= selStart))
                do_Up_Array();
                else{
                if(key == kReturn) WESetSelection ( WEGetTextLength(we), WEGetTextLength(we), we );
                DoKey ( key, event ) ;
                }
            }
            if(key == kReturn){
                TextHdl = WEGetText(we);
                HLock(TextHdl);
                TextPtr = *TextHdl;
                TextPtr = TextPtr + gChangeAble;
                strncpy(gbuf, TextPtr,gbuflen);
                HUnlock(TextHdl);
                textLength = WEGetTextLength(we) - gChangeAble;
                if (gbuflen < textLength)
                gbuf = gbuf + gbuflen;
                else
                gbuf = gbuf + textLength;
                *gbuf = '\0';
                gfinishedInput = !gfinishedInput;
            }
            }
        }
        }
        if ((isEditWindow(FrontWindow()) != 0) || (isHelpWindow(FrontWindow()) != 0)){
        DoKey ( key, event ) ;
        }

    }
}


/* DoDiskEvent routine :
   Description :
   Handle Disk event
 */

void DoDiskEvent ( const EventRecord * event )
{
#if !TARGET_API_MAC_CARBON
    Point dialogCorner ;

    if ( ( event -> message >> 16 ) != noErr )
    {
        SetPt ( & dialogCorner, 112, 80 ) ;
        DIBadMount ( dialogCorner, event -> message ) ;
    }
#else
    #pragma unused ( event )
#endif
}


/* DoOSEvent routine :
   Description :
   Handle OS event
 */

void DoOSEvent ( const EventRecord * event )
{
    SInt16      osMessage ;
    WindowPtr   window ;

    // extract the OS message field from the event record

    osMessage = ( event -> message & osEvtMessageMask ) >> 24 ;

    // dispatch on osMessage

    switch ( osMessage )
    {
    case suspendResumeMessage:
    {
    if ( ( window = FrontWindow( ) ) != nil )
    {
        DoActivate( (event->message & resumeFlag) != 0, window );
    }
    break;
    }

    case mouseMovedMessage:
    {
    // nothing
    break;
    }
    }
}


/* DoHighLevelEvent routine :
   Description :
   Handle High Level event
 */
void DoHighLevelEvent( const EventRecord *event )
{
    AEProcessAppleEvent( event );
}

void DoNullEvent( const EventRecord *event )
{
#pragma unused (event)

    WindowPtr window;

    if ( ( window = FrontWindow( ) ) != nil )
    {
    WEIdle( &sSleepTime, GetWindowWE(window) );
    }
    else
    {
    sSleepTime = LONG_MAX;
    }
}


/* DoWindowEvent routine :
   Description :
   Handle High Level window event
 */
void DoWindowEvent( const EventRecord *event )
{
    WindowPtr window;

    // the message field of the event record contains the window reference

    window = (WindowPtr) event->message;

    // make sure this window is an application window; check the windowKind field

    if ( GetWindowKind( window ) != userKind )
    return;

    switch ( event->what )
    {
    case updateEvt:
    {
    DoUpdate( window );
    break;
    }

    case activateEvt:
    {
    DoActivate( ( event->modifiers & activeFlag) != 0, window );
    break;
    }
    }
}


/* ProcessEvent routine :
   Desciption :

   It is used in the Event  loop, when you call Process Event, it will
   capture  the next  event that  available,  and dispatch  it to  the
   corresponding routine.
 */
 
       

    
 void ProcessEvent( void )
{
    EventRecord event;
    Boolean gotEvent,  haveResize = false;
    WindowPtr windowPtr;
    DevDesc  *dd;
    SInt16 Console_Width, NumofChar;
    GrafPtr savePort;
    RgnHandle cursorRgn;
    Rect portRect ;
    CGrafPtr thePort;
  
    gotEvent = WaitNextEvent( everyEvent, &event, sSleepTime, sMouseRgn );

#if ! TARGET_API_MAC_CARBON
    // give text services a chance to intercept this event
    // if TSMEvent( ) handles the event, it will set event.what to nullEvent
    // this call is not needed for Carbon clients
    TSMEvent ( & event ) ;
#endif

    if (gExpose) {
    dd = (DevDesc*)gGReference[gExpose].devdesc;
    dd-> dp.resize(dd);
    playDisplayList(dd);
    haveResize = true;
    gExpose = false;
    }
    if (fstart) {
    GetPortBounds ( GetWindowPort(Console_Window), & portRect ) ;
    Console_Width = portRect.right - portRect.left ;
    GetPort(&savePort);
#if TARGET_API_MAC_CARBON
    SetPortWindowPort(Console_Window);
#else
    SetPort(Console_Window);
#endif
    TextFont(4);
    TextSize(gTextSize);
    NumofChar = (int)(((Console_Width - 15) / CharWidth('M')) -0.5) ;
        R_SetOptionWidth(NumofChar);
    SetPort(savePort);
    fstart = false;
    }

    // adjust cursor shape and set mouse region
    // (we assume event.where is the current mouse position in global coordinates
    // if event.what <= osEvt; high-level events store the event ID there)

    if ( event.what <= osEvt ) {
    AdjustCursor( event.where, sMouseRgn );
    }

    // dispatch on event.what

    switch( event.what ) {

    case nullEvent:
    DoNullEvent( &event );
    break;

    case mouseDown:
    DoMouseDown( &event);
    break;

    case keyDown:
    case autoKey:

        DoKeyDown( &event );

    break;

    case updateEvt:
    windowPtr = (WindowPtr) (&event)->message;
    if (isGraphicWindow(windowPtr)) { 
    CGrafPtr thePort;
    
    
    thePort = GetWindowPort(windowPtr);
    
        /* flush the entire port */
        QDFlushPortBuffer(thePort, NULL);

    
    }
    if (!haveResize)
        DoWindowEvent(&event);
    break;

    case activateEvt:
    windowPtr = (WindowPtr) (&event)->message;
    if (isGraphicWindow(windowPtr)) {
        doActivate(&event);
    }
    DoWindowEvent(&event);
    break;

    case diskEvt:
    DoDiskEvent( &event );
    break;

    case osEvt:
    DoOSEvent( &event );
    break;

    case kHighLevelEvent:
    DoHighLevelEvent( &event );
    break;


    } // switch

    if (gotEvent) {
    sSleepTime = 0;  // force early idle after non-idle event
    }
}


/* My IdleProc for AESend */

pascal Boolean idleProc(EventRecord *eventIn, long *sleep, RgnHandle *mouseRgn)
{
    switch (eventIn->what) {
    case nullEvent:
    /* no nul processing in this sample */
    *sleep = 0;
    mouseRgn = nil;
    break;
    case updateEvt:
    case activateEvt:
    //DrawMain((WindowPtr)eventIn->message);          /* draw whatever window needs an update */
    break;
    case app4Evt:
    switch ((gERecord.message >> 24) & 0x0FF) {     /* high byte of message */
    case suspendResumeMessage:                  /* suspend/resume is also an activate/deactivate */
        gInBackground = (gERecord.message & kResumeMask) == 0;
        break;
    }
    break;



    }
    return(false);  /* I'll wait forever */
}


/* GotRequiredParams routine :
 */
OSErr GotRequiredParams( const AppleEvent *ae )
{
    DescType actualType;
    Size actualSize;
    OSErr err;
    
    err = AEGetAttributePtr( ae, keyMissedKeywordAttr, typeWildCard, &actualType, nil, 0, &actualSize );

    return  ( err == errAEDescNotFound ) ? noErr :
    ( err == noErr ) ? errAEParamMissed : err;

}


extern OSErr SourceFile(FSSpec      *myfss);
extern char *mac_getenv(const char *name);


/* HandleOpenDocument routine :
   Description :
   This Event will be generated when you click on a R file icon.
   This event can only be depatched by the ProcessEvent routine.
   Thus, even you click on the file icon of R to start R, this event
   will not be catch until the R_readConsole start.
 */
static pascal OSErr HandleOpenDocument( const AppleEvent *ae,
                    AppleEvent *reply, SInt32 refCon )
{
#pragma unused ( reply, refCon )
    AEDescList      docList;
    AEKeyword       keyword;
    DescType        actualType;
    Size        actualSize;
    SInt32      numberOfDocuments, i;
    FSSpec      fileSpec;
    OSErr       err;
    FInfo       fileInfo;
    SInt16      pathLen;
    Handle      pathName=NULL;
    FILE        *fp;
    SEXP        img, lst;

    docList.descriptorType = typeNull;
    docList.dataHandle = nil;
    // extract direct parameter from the Apple event
    if ( ( err = AEGetParamDesc( ae, keyDirectObject, typeAEList, &docList ) ) != noErr )
    goto cleanup;

    // perform the recommended check for additional required parameters

    if ( ( err = GotRequiredParams( ae ) ) != noErr )
    goto cleanup;

    i = 1;
    if ( ( err = AEGetNthPtr( &docList, i, typeFSS, &keyword, &actualType,
                  &fileSpec, sizeof( fileSpec ), &actualSize ) ) != noErr )
    goto cleanup;

    err = FSpGetFInfo(&fileSpec, &fileInfo);
    if (err != noErr) goto cleanup;

    if (fileInfo.fdType == 'PICT'){
        error("No Picture Can be load in this version of R\n");
    }
    if (fileInfo.fdType == 'TEXT'){
        if( strncmp("TRUE",mac_getenv("OnOpenSource"),4) == 0)
            SourceFile(&fileSpec);
        else {    
        DoNew( false);
        RemWinMenuItem();
        ReadTextFile ( &fileSpec, GetWindowWE ( Edit_Windows[Edit_Window-1] ) );
        UniqueWinTitle();
        }
    }else
    if ((fileInfo.fdType == 'BINA')||(fileInfo.fdType == 'ROBJ')){
        FSpGetFullPath(&fileSpec, &pathLen, &pathName);
        HLock((Handle) pathName);
        strncpy(InitFile, *pathName, pathLen);
        InitFile[pathLen] = '\0';
        HUnlock((Handle) pathName);


        if(!(fp = fopen(InitFile, "rb"))) { /* binary file */
        RWrite("File cannot be opened !");
        /* warning here perhaps */
        return;
        }
        PROTECT(img = R_LoadFromFile(fp, 1));
        switch (TYPEOF(img)) {
        case LISTSXP:
        while (img != R_NilValue) {
            defineVar(TAG(img), CAR(img), R_GlobalEnv);
            img = CDR(img);
        }
        break;
        case VECSXP:
        for (i = 0; i < LENGTH(img); i++) {
            lst = VECTOR_ELT(img,i);
            while (lst != R_NilValue) {
            defineVar(TAG(lst), CAR(lst), R_GlobalEnv);
            lst = CDR(lst);
            }
        }
        break;
        }
        UNPROTECT(1);
    }
 cleanup:
    return err;

}



/* HandleQuitApplication routine :
   Description :
   This event will be execute when the applcation exit normally
 */
static pascal OSErr HandleQuitApplication( const AppleEvent *ae,
                       AppleEvent *reply, SInt32 refCon )
{
#pragma unused (reply, refCon)

    AEKeyword       optKey;
    DescType        actualType;
    Size        actualSize;
    SavingOption    saving;
    OSErr       err;

    // default saving option is savingAsk;

    saving = savingAsk;

    // extract optional save options

    if ( ( err = AEGetParamPtr( ae, keyAESaveOptions, typeEnumerated, &actualType, &optKey, sizeof( optKey ), &actualSize ) ) == noErr )
    {
    if ( optKey == kAEYes )
    {
        saving = savingYes;
    }
    else if (optKey == kAENo )
    {
        saving = savingNo;
    }
    else if ( optKey != kAEAsk )
    {
        err = paramErr; // for want of a better code
        goto cleanup;
    }
    }

    // perform the recommended check for additional required parameters

    if ( ( err = GotRequiredParams( ae ) ) != noErr )
    goto cleanup;

    // actually do the quit stuff

    err = DoQuit( saving );

 cleanup:
    return err;
}


/* InitializeEvents: modified to let R interact with other processes
                     such as UnZip tools, Browsers, etc.
                     AppleEvents handlers are now installed only if
                     the System allow this (i.e. System v. > 7.0)
                     Stefano M.Iacus, 2/2/2001
*/

OSErr InitializeEvents( void )
{
    OSErr   err;
    long aLong = 0;

    // allocate space for the mouse region

    sMouseRgn = NewRgn( );

    if ( ( err = AEInstallEventHandler( kCoreEventClass, kAEOpenDocuments, NewAEEventHandlerUPP( HandleOpenDocument ), kDoOpen, false ) ) != noErr )
        goto cleanup;

    if ( ( err = AEInstallEventHandler( kCoreEventClass, kAEPrintDocuments, NewAEEventHandlerUPP( HandleOpenDocument ), kDoPrint, false ) ) != noErr )
        goto cleanup;

    if ( ( err = AEInstallEventHandler( kCoreEventClass, kAEQuitApplication, NewAEEventHandlerUPP( HandleQuitApplication ), 0, false ) ) != noErr )
        goto cleanup;

    // install Apple event handlers for a subset of the Core suite

    if ( ( err = InstallCoreHandlers( ) ) != noErr )
        goto cleanup;

    // install Apple event handlers for inline input
    if ( ( err = WEInstallTSMHandlers( ) ) != noErr )
        goto cleanup;

    gAEIdleUPP = NewAEIdleUPP(idleProc);
  
 cleanup:
    if(err == noErr)
     EventsInit = true;

    return err;
}



void R_startBrowser(char *fileName)
{
    FSSpec HelpFile;
    Str255 name;

    name[0] = strlen(fileName);
    strncpy((char *)(&name[1]), fileName, name[0]);
    FSMakeFSSpec(0,0,name,&HelpFile);
    OpenSelection(&HelpFile);
}