TumblerSource/Tumbler_event.c

//      Tumbler)event.c
//
//      Event handling routines.
//
//      Author:     Nick Thompson & Pablo Fernicola, with thanks to the QuickDraw 3D team
//      Date:       Monday, Janurary 20, 1992
//
//      Copyright © 1992-95 Apple Computer, Inc., All Rights Reserved
//
//  Modification History:
//      12/2/94     nick        
 
//
//  Debugging Aids :
//
//  Turn this flag on to see where the current hilite region is.
//
//      #define _FLASH_HILITE_RGN_
//
//
 
#include <Drag.h>
#include "QD3D.h"
 
#include "Tumbler_globals.h"
#include "Tumbler_prototypes.h"
#include "Tumbler_resources.h"
#include "AppleEvents.h"
#include "Events.h"
 
#include "QD3DView.h"
#include "QD3DDrawContext.h"
#include "QD3DDrawContext.h"
#include "QD3DPick.h"
#include "QD3DTransform.h"
#include "QD3DLight.h"
#include "QD3DMath.h"
#include "QD3DGeometry.h"
 
#define SleepDuration       20      /* WaitNextEvent sleep constant     */
 
#include "Tumbler_event.h"
#include "Tumbler_camera.h"
#include "Tumbler_drag.h"
#include "Tumbler_offscreen.h"
#include "Tumbler_windows.h"
#include "Tumbler_menus.h"
#include "Tumbler_drag.h"
#include "Tumbler_document.h"
#include "Tumbler_cursor.h"
 
#ifdef PODIUM_APP
    #include "Tumbler_Podium.h"
#endif
 
static void DoContentClick(WindowPtr theWindow, EventRecord *theEvent);
static void DoBackgroundContentClick(WindowPtr theWindow, EventRecord *theEvent) ;
static void DoMouseDown(EventRecord *theEvent);
static void DoKey(char theChar);
static void DoKeyDown(EventRecord *theEvent);
static void DoActivate(EventRecord *theEvent);
static void DoUpdate(EventRecord *theEvent);
static void DoOSEvent(EventRecord *theEvent);
static void DoHighLevelEvent(EventRecord *theEvent);
static void DoEvent(EventRecord *theEvent);
static void DoIdleHitTest(void);
static void DoIdle(void);
 
 
 
 
 
//-------------------------------------------------------------------------------------
//  DoContentClick handles mouseDown events in the content region of a document window.
//
//  (1) If the mouseDown is on a draggable object (the document's hiliteRgn) and a
//      successful drag occurs, no further processing is necessary.
//
//  (2) If the mouseDown is not on a draggable object and within the viewRect of the
//      TextEdit field, call TEClick to handle the mouseDown.
 
static void DoContentClick( WindowPtr theWindow, EventRecord *theEvent)
 
{   short           thePart;
    Point           thePoint;
    ControlHandle   theControl;
    DocumentPtr     theDocument = GetDocumentFromWindow(theWindow);
    GrafPtr         savedPort ;
 
    if( theWindow == nil )
        return ;
 
    GetPort( &savedPort) ;
    SetPort( theWindow ) ;
    
    thePoint = theEvent->where;
    GlobalToLocal(&thePoint);
 
    if( theDocument->documentGroup ) {
    
        
        // we can only do a drag if the option key is held down
    
        if (theEvent->modifiers & optionKey) {
            RgnHandle   tempRgn = NewRgn();  // Nick this needs to be the BBox of the group
            
            RectRgn(tempRgn, &theWindow->portRect);
            DoDragObjects(theDocument, theEvent, tempRgn ) ;
            
            DisposeRgn(tempRgn);
        } else {
            Point   oldMouse;
            Point   newMouse;
            long    mouseChanged = 0;
            long    dx, dy, x, y, oldX, oldY;
            float   width, height;
            float   xRot, yRot;
                    
            GetMouse(&newMouse);        // get the mouse in local co-ordinates, 
                                            // local to the  current GrafPort
    
            oldX = newMouse.h;
            oldY = newMouse.v;
            
            width = theWindow->portRect.left - theWindow->portRect.right;
            height = theWindow->portRect.bottom - theWindow->portRect.top;
 
            while (WaitMouseUp()) {
            
                GetMouse(&newMouse);        // get the mouse in local co-ordinates, 
                                            // local to the  current GrafPort
 
                dx = oldY - newMouse.v;
                dy = newMouse.h - oldX;
                
                if ((dx != 0) || (dy != 0)) {
                    TQ3Matrix4x4        tempMatrix;
                    
                    xRot = ((float) dx * kQ3Pi) / width;
                    yRot = ((float) dy * kQ3Pi) / height;
        
                    if ((xRot != 0.0) || (yRot != 0.0)) {
                        Q3Matrix4x4_SetRotate_XYZ(&tempMatrix, xRot, yRot, 0.0);
                        Q3Matrix4x4_Multiply(&theDocument->modelRotation, &tempMatrix, &theDocument->modelRotation);
 
                        mouseChanged = 1;
                        DrawOffscreen(theDocument);
                    }
                }
                oldX = newMouse.h; oldY = newMouse.v;
            }
            
            if( mouseChanged ) {
                theDocument->rotationDir.x = xRot;
                theDocument->rotationDir.y = yRot;
                DrawOffscreen(theDocument);
                
                DoDrawGrowIcon(theDocument->theWindow);
            }
        }
    }
 
    SetPort( savedPort ) ;
 
}
 
 
//
//  DoBackgroundContentClick handles mouseDown events in the content region of a document window
//  when the window is not frontmost. The following bullet items describe how this background
//  mouseDown event is handled:
//
//  (1) If the mouseDown is not in a draggable object (not in the document's hiliteRgn) call
//      SelectWindow to bring the window to the front as usual.
//
//  (2) If the mouseDown is in a draggable object and the mouse is released without
//      dragging, call SelectWindow when the mouse is released.
//
//  (3) If the mouseDown is in a draggable object and a successful drag occurs, SelectWindow
//      should only be called if the drop occurred in the same window (the DoDragObjects function
//      calls SelectWindow in this case).
//
 
static void DoBackgroundContentClick(WindowPtr theWindow, EventRecord *theEvent)
{
    short           thePart;
    Point           thePoint;
    ControlHandle   theControl;
    DocumentPtr     theDocument ;
    RgnHandle       tempRgn = NewRgn(); // Nick will fix later, needs to be the bbox of the group
    
    if( theWindow == nil )
        return ;
 
    SetPort( theWindow );
    theDocument = GetDocumentFromWindow(theWindow);
 
    thePoint = theEvent->where;
    GlobalToLocal(&thePoint);
 
    RectRgn(tempRgn, &theWindow->portRect);
    
    if (PtInRgn(thePoint, tempRgn)) {
 
        if (! DoDragObjects(theDocument, theEvent, tempRgn)) {
            SelectWindow(theDocument->theWindow);
        }
 
    } else {
 
        SelectWindow(theDocument->theWindow);
 
    }
    DisposeRgn( tempRgn );
}
 
 
//
//  DoMouseDown is called to handle mouseDown events.
//
 
static void DoMouseDown(EventRecord *theEvent)
 
{   short       thePart;
    WindowPtr   theWindow;
    Rect        dragRect;
    DocumentPtr theDocument;
    
 
    thePart = FindWindow(theEvent->where, &theWindow);
    switch(thePart) {
    
        case inMenuBar:
            PrepareMenus();
            DoMenuCommand(MenuSelect(theEvent->where));
            break;
            
        case inSysWindow:
            SystemClick(theEvent, theWindow);
            break;
        case inContent:
        
            if (theWindow == FrontWindow()) {
#ifndef PODIUM_APP
                DoContentClick(theWindow, theEvent);
#else
                Podium_DoContent( GetDocumentFromWindow(theWindow), theEvent ) ;
#endif
            } else {
#ifndef PODIUM_APP
                DoBackgroundContentClick(theWindow, theEvent);
#else
                Podium_DoBackgroundContent( GetDocumentFromWindow(theWindow), theEvent ) ;
#endif
            }
            break;
            
        case inDrag:
            if (theWindow != FrontWindow())
                SelectWindow(theWindow);
            dragRect = qd.screenBits.bounds;
            DragWindow(theWindow, theEvent->where, &dragRect);
            break;
            
        case inGrow:
            
            if ((theDocument = GetDocumentFromWindow(theWindow)) != nil) {
                GrowDocumentWindow(theWindow, theEvent->where);
            }
            break;
            
        case inGoAway:
            if ((theDocument = GetDocumentFromWindow(theWindow)) != nil)
                if (TrackGoAway(theWindow, theEvent->where)) {
                    (void)CloseDocument(theDocument);       // it's not important what the result is here
                }
            break;
    }
}
 
 
//
//  DoKey is called each time a character is typed on the keyboard to
//  be entered into a document window.
//
 
static void DoKey(char theChar)
 
{   WindowPtr       theWindow;
    DocumentPtr theDocument;
 
    if ((theWindow = FrontWindow()) != nil) {
        if ((theDocument = GetDocumentFromWindow(theWindow)) != nil) {
            // what d'ya want to do here?
        }
    }
}
 
 
//
//  DoKeyDown is called to handle keyDown or autoKey events.
//
 
static void DoKeyDown(EventRecord *theEvent)
 
{   char        theChar;
 
    theChar = theEvent->message & charCodeMask;
 
    if (theEvent->modifiers & cmdKey) {
        PrepareMenus();
        DoMenuCommand(MenuKey(theChar));
    }
}
 
 
//
//  DoActivate is called in response to activate/deactivate events.
//
 
static void DoActivate(EventRecord *theEvent)
 
{   WindowPtr       theWindow;
    DocumentPtr theDocument;
 
    if ((theWindow = (WindowPtr) theEvent->message) != nil) {
        if ((theDocument = GetDocumentFromWindow(theWindow)) != nil) {
            DoActivateDocument(theDocument, (theEvent->modifiers & activeFlag));
        }
    }
}
 
 
//
//  DoUpdate is called in response to update events.
//
 
static void DoUpdate(EventRecord *theEvent)
 
{   DocumentPtr theDocument;
 
    if ( ((WindowPtr) theEvent->message) != nil)
        UpdateWindow((WindowPtr) theEvent->message);
}
 
 
//
//  DoOSEvent is called in response to Operating System events.
//
 
static void DoOSEvent(EventRecord *theEvent)
 
{   DocumentPtr theDocument;
 
    switch ((theEvent->message >> 24) & 0x0FF) {
        case suspendResumeMessage:
            gInBackground = (theEvent->message & resumeFlag) == 0;
            if ((theDocument = GetDocumentFromWindow(FrontWindow())) != nil)
                DoActivateDocument(theDocument, !gInBackground);
            break;
    }
}
 
 
 
 
static void DoHighLevelEvent(EventRecord *theEvent)
{
    AEProcessAppleEvent(theEvent);
}
 
 
//
//  Each time WaitNextEvent returns an event to this application, DoEvent
//  is called to handle the event.
//
 
static void DoEvent(EventRecord *theEvent)
 
{   WindowPtr       theWindow;
    DocumentPtr theDocument;
 
    if ((theWindow = FrontWindow()) != nil) {
        if ((theDocument = GetDocumentFromWindow(theWindow)) != nil) {
            gFrontDocument = theDocument;
        }
    }
 
    switch(theEvent->what) {
        case mouseDown:
            DoMouseDown(theEvent);
            break;
        case mouseUp:
            break;
        case keyDown:
        case autoKey:
            DoKeyDown(theEvent);
            break;
        case activateEvt:
            DoActivate(theEvent);
            break;
        case updateEvt:
            DoUpdate(theEvent);
            break;
        case osEvt:
            DoOSEvent(theEvent);
            break;
        case kHighLevelEvent:
            DoHighLevelEvent(theEvent);
            break;
    }
}
 
 
//
//  DoIdle get called repetitively while the application is not doing
//  anything.
//
 
static void DoIdleHitTest(void)
 
{
 
    WindowPtr       theWindow;
    DocumentPtr theDocument;
 
    if ((theWindow = FrontWindow()) != nil) {
        if ((theDocument = GetDocumentFromWindow(theWindow)) != nil) {
            SetPort(theDocument->theWindow);
 
            if( theDocument->documentGroup ) {
                Point       screenPoint;
                TQ3WindowPointPickData  withData;
                unsigned long       numPicked;
                TQ3PickObject       pickObject;
                
                withData.data.sort          =   kQ3PickSortNone;
                withData.data.numHitsToReturn=  1;
                withData.data.mask          =   kQ3PickDetailMaskObject;
                                            
                GetMouse( &screenPoint );
                withData.point.x = screenPoint.h;
                withData.point.y = screenPoint.v;
            
                withData.vertexTolerance = withData.edgeTolerance = 3;
// gone and sadly missed in b1c1, delete this line at b2c1!!
//              withData.view = theDocument->theView;
            
                pickObject = Q3WindowPointPick_New(&withData);
        
                Q3View_StartPicking(theDocument->theView, pickObject);
                do {
                    Q3DisplayGroup_Submit(theDocument->documentGroup, theDocument->theView);
                } while (Q3View_EndPicking(theDocument->theView) == kQ3ViewStatusRetraverse);
                
                if (Q3Pick_GetNumHits(pickObject, &numPicked) && (numPicked != 0)) {
                    SetCursor(*GetCursor(plusCursor));
                } else {
                    SetCursor(&qd.arrow);
                }
                
                Q3Object_Dispose(pickObject);
            }
 
#ifdef _FLASH_HILITE_RGN_
 
            {   long            dTime;
 
                InvertRgn(theDocument->hiliteRgn);
                Delay(10, &dTime);
                InvertRgn(theDocument->hiliteRgn);
            }
#endif
 
        }
    }
}
 
//
//  DoIdle get called repetitively while the application is not doing
//  anything.
//
 
static void DoIdle(void)
 
{
 
    WindowPtr       theWindow;
    DocumentPtr theDocument;
 
    if ((theWindow = FrontWindow()) != nil) {
        if ((theDocument = GetDocumentFromWindow(theWindow)) != nil) {
            Boolean update = false;
            
            SetPort(theDocument->theWindow);
 
            if( theDocument->documentGroup && theDocument->animateLights && theDocument->light) {
#if defined(ESCHER_VER_15) && ESCHER_VER_15
 
                float angle;
                TQ3GroupPosition        lightPosition, position;
                TQ3LightObject      theLight;
                TQ3GroupObject      lightGroup;
                TQ3Matrix4x4            matrix;
                TQ3Point3D          location;
                TQ3Object           ellipsoid;
                TQ3Point3D          origin;
                
                theDocument->light = kQ3True;
                
                Q3View_GetLightGroup(theDocument->theView, &lightGroup);
                Q3Group_GetFirstPosition(lightGroup, &lightPosition);
                        
                /* First one is always on, skip */
                Q3Group_GetNextPosition(lightGroup, &lightPosition);
 
                /* Adjust the transform for the Red sphere */
                Q3RotateTransform_GetAngle(theDocument->lightXform1, &angle);
                angle += 0.8;
                Q3RotateTransform_SetAngle(theDocument->lightXform1, angle);
                Q3Transform_GetMatrix(theDocument->lightXform1, &matrix);
                
                /* Get the Red light */
                Q3Group_GetPositionObject(lightGroup, lightPosition, &theLight);
                /* Get the Red sphere */
                Q3Group_GetFirstPositionOfType(theDocument->light1,
                    kQ3ShapeTypeGeometry, &position);
            
                Q3Group_GetPositionObject(theDocument->light1, position, &ellipsoid);
                Q3Ellipsoid_GetOrigin( ellipsoid, &origin);
 
                Q3Point3D_Transform(&origin, &matrix, &location);
                Q3PointLight_SetLocation(theLight, &location);
                Q3Object_Dispose(theLight);                     
                Q3Object_Dispose(ellipsoid);                        
                
                /* Adjust the transform for the Green sphere */
                Q3Group_GetNextPosition(lightGroup, &lightPosition);
                Q3RotateTransform_GetAngle(theDocument->lightXform2, &angle);
                angle += 0.5;
                Q3RotateTransform_SetAngle(theDocument->lightXform2, angle);
                Q3Transform_GetMatrix(theDocument->lightXform2, &matrix);
                        
                /* Get the Green light */
                Q3Group_GetPositionObject(lightGroup, lightPosition, &theLight);
                /* Get the Green sphere */
                Q3Group_GetFirstPositionOfType(theDocument->light2,
                    kQ3ShapeTypeGeometry, &position);
            
                Q3Group_GetPositionObject(theDocument->light2, position, &ellipsoid);
                Q3Ellipsoid_GetOrigin( ellipsoid, &origin);
                Q3Point3D_Transform(&origin, &matrix, &location);
                Q3PointLight_SetLocation(theLight, &location);
                Q3Object_Dispose(theLight);                     
                Q3Object_Dispose(ellipsoid);                        
                        
                /* Adjust the transform for the Blue sphere */
                Q3Group_GetNextPosition(lightGroup, &lightPosition);
                Q3RotateTransform_GetAngle(theDocument->lightXform3, &angle);
                angle += 0.3;
                Q3RotateTransform_SetAngle(theDocument->lightXform3, angle);
                Q3Transform_GetMatrix(theDocument->lightXform3, &matrix);
                        
                /* Get the Blue light */
                Q3Group_GetPositionObject(lightGroup, lightPosition, &theLight);
                /* Get the Blue sphere */
                Q3Group_GetFirstPositionOfType(theDocument->light3,
                    kQ3ShapeTypeGeometry, &position);
            
                Q3Group_GetPositionObject(theDocument->light3, position, &ellipsoid);
                Q3Ellipsoid_GetOrigin( ellipsoid, &origin);
                Q3Point3D_Transform(&origin, &matrix, &location);
                Q3PointLight_SetLocation(theLight, &location);
                Q3Object_Dispose(theLight);                     
                Q3Object_Dispose(ellipsoid);                        
 
                Q3Object_Dispose(lightGroup);
                update = true;
#endif
            }
 
            if( theDocument->documentGroup && theDocument->animateModel) {
                TQ3Matrix4x4    tmp;
                
                if( theDocument->rotationDir.x == 0.0 && theDocument->rotationDir.y == 0.0 ) {
                    Q3Matrix4x4_SetRotate_XYZ(&tmp, 0.03, 0.08, 0.0);
                    Q3Matrix4x4_Multiply(&theDocument->modelRotation, &tmp, &theDocument->modelRotation);
                } else {
                    Q3Matrix4x4_SetRotate_XYZ(&tmp, theDocument->rotationDir.x, theDocument->rotationDir.y, 0.0);
                    Q3Matrix4x4_Multiply(&theDocument->modelRotation, &tmp, &theDocument->modelRotation);
                }
                update = true;
            }
 
            if( update ) {
                DrawOffscreen(theDocument);
                DrawOnscreen(theDocument);
                DoDrawGrowIcon(theDocument->theWindow);
            }
        }
    }
}
 
 
//
//  This is the main event loop of the program. It calls WaitNextEvent
//  and dispatches the returned event until gQuit is true.
//
 
void EventLoop(void)
 
{   short           gotEvent;
    EventRecord     theEvent;
    RgnHandle       theMouseRgn;
    Point           theLoc;
 
    theMouseRgn = NewRgn();
 
    do {
    
        gotEvent = WaitNextEvent(everyEvent, &theEvent, 0L, theMouseRgn);
        if (gotEvent) {
            DoEvent(&theEvent);
        } else {
#ifndef PODIUM_APP
            DoIdle();
#else
            Podium_DoIdle(&theEvent) ;
#endif
        }
    } while (! gQuit);
 
    DisposeRgn(theMouseRgn);
}