TumblerSource/Tumbler_podium.c

// tumbler_podium.c -- this contains the podium specific routines for the tumbler app
//
//
 
 
#ifdef PODIUM_APP
 
#include "QD3D.h"
 
#include <Drag.h>
#include <Events.h>
#include <Controls.h>
#include <ToolUtils.h>
#include <Windows.h>
 
#include "QD3DView.h"
#include "QD3DDrawContext.h"
#include "QD3DDrawContext.h"
#include "QD3DPick.h"
#include "QD3DGroup.h"
#include "QD3DTransform.h"
#include "QD3DLight.h"
#include "QD3DMath.h"
 
 
#include <QuickDraw.h>
#include <QDOffscreen.h>
 
 
#include "tumbler_Document.h"
#include "tumbler_Camera.h"
#include "tumbler_podium.h"
#include "tumbler_offscreen.h"
#include "tumbler_drag.h"
 
 
static CursHandle       objectCursor, zoomInCursor, zoomOutCursor, handCursor;
 
 
static TQ3Object DoHitTest(DocumentPtr  theDocument);
static short PointInRect(Point *point, Rect *rect);
 
// Used in Tumber_AEVT.c
void Podium_UpdateDrawContextFromDropRect( DocumentPtr theDocument ) ;
 
//------------------------------------------------------------------------------------
void Podium_UpdateDrawContextFromDropRect( DocumentPtr theDocument )
{
 
    Rect                    frameRect ;
    
    frameRect = theDocument->dropArea ;
     
    // if the drop area for the doc is the same as the GWorld's rect
    // then we don't need to do anything, if they have changes we need
    // to update the drawcontext
    
    if(!EqualRect(&frameRect,&theDocument->geometriesOffscreen->portRect)) {
        TQ3DrawContextObject    theDrawContext ;
        OSErr               theErr ;
        
        if((theErr = UpdateGWorld( &theDocument->geometriesOffscreen, 32, &frameRect, nil, nil, 0L )) == noErr
//          && (theErr = UpdateGWorld( &theDocument->maskOffscreen, 1, &frameRect, nil, nil, 0L )) == noErr
            ) { 
                        
            theDrawContext = TumblerDocument_NewPixmapDrawContext( theDocument ) ;
            Q3View_SetDrawContext(theDocument->theView, theDrawContext);
            Q3Object_Dispose(theDrawContext);
            
            AdjustCamera(theDocument,
                theDocument->geometriesOffscreen->portRect.right - theDocument->geometriesOffscreen->portRect.left,
                theDocument->geometriesOffscreen->portRect.bottom - theDocument->geometriesOffscreen->portRect.top);
        }
    }
}
 
        
 
 
 
static void MouseMoved(
    DocumentPtr theDocument,
    long    oldX, 
    long    oldY,
    long    newX, 
    long    newY)
{
    float               scaleFactor = 256.0;
    unsigned long       winWidth, winHeight;
    TQ3Matrix4x4        tmp;
    TQ3BoundingBox      viewBBox;
    TQ3Point3D          modelCenter;
    
    /*
     *  Find the bounding box for the group.
     *  We'll use the center of the box as the point about which to rotate
     *  (the old method always rotated about the world-space origin).
     */
 
    /*
     *  Get the width and height of the *current* window.
     *  This is done so that mouse motion in a window has an effect
     *  relative to the size of the window. 
     */
    winWidth  = theDocument->theWindow->portRect.right - theDocument->theWindow->portRect.left;
    winHeight = theDocument->theWindow->portRect.bottom - theDocument->theWindow->portRect.top;
 
    GetGroupBBox(theDocument->theView, theDocument->documentGroup, NULL, &viewBBox);
    
    /*
     *  The "to" point is the center of the view bounding box
     */
    {
        float       weights[2] = { 0.5, 0.5 };
        TQ3Point3D  points[2];
        
        points[0] = viewBBox.min;
        points[1] = viewBBox.max;
        
        Q3Point3D_AffineComb(points, weights, 2, &modelCenter);
    }
            
    /*
     *  Make a new rotation matrix, rotating about the center of the view
     *  bbox.
     */         
    {
        float   xRot, yRot, zRot;
        
        xRot = yRot = zRot = 0.0;
        
        xRot = Q3Math_DegreesToRadians((float)(newY - oldY) / winHeight * scaleFactor);
        yRot = Q3Math_DegreesToRadians((float)(newX - oldX) / winWidth * scaleFactor);
 
        if ((xRot != 0.0) || (yRot != 0.0)){
            Q3Matrix4x4_SetRotateAboutPoint(&tmp, &modelCenter, xRot, yRot, zRot);
            Q3Matrix4x4_Multiply(&theDocument->modelRotation, &tmp, &theDocument->modelRotation);
        }
    }
 
}
 
 
static short PointInRect(Point *point, Rect *rect)
{
    if( point->h > rect->left && point->h < rect->right ) {
        if( point->v > rect->top && point->v < rect->bottom ) {
            return(1);
        } else {
            return(0);
        }
    } else {
        return(0);
    }
}
 
 
static Boolean PtOnHandles(Point *newMouse, Rect *location, short *whichHandle)
{
    Rect    handlesRect;
    
    handlesRect.left = location->left - 4;
    handlesRect.top = location->top - 4;
    handlesRect.right = location->left;
    handlesRect.bottom = location->top;
    
    if( PointInRect(newMouse, &handlesRect) ) {
        *whichHandle = LeftTopHandle;
        return(true);
    }
    
    handlesRect.left = location->right;
    handlesRect.right = location->right + 4;
    
    if( PointInRect(newMouse, &handlesRect) ) {
        *whichHandle = RightTopHandle;
        return(true);
    }
    
    handlesRect.left = location->right;
    handlesRect.right = location->right + 4;
    handlesRect.top = location->bottom;
    handlesRect.bottom = location->bottom + 4;
    
    if( PointInRect(newMouse, &handlesRect) ) {
        *whichHandle = RightBottomHandle;
        return(true);
    }
    
    handlesRect.left = location->left - 4;
    handlesRect.right = location->left;
    
    if( PointInRect(newMouse, &handlesRect) ) {
        *whichHandle = LeftBottomHandle;
        return(true);
    }
    return(false);
}
 
static void DrawHandles(Rect *location)
{
    Rect    handlesRect;
    
    handlesRect.left = location->left - 4;
    handlesRect.top = location->top - 4;
    handlesRect.right = location->left;
    handlesRect.bottom = location->top;
    
    PaintRect(&handlesRect);
    
    handlesRect.left = location->right;
    handlesRect.right = location->right + 4;
    
    PaintRect(&handlesRect);
    
    handlesRect.left = location->right;
    handlesRect.right = location->right + 4;
    handlesRect.top = location->bottom;
    handlesRect.bottom = location->bottom + 4;
    
    PaintRect(&handlesRect);
    
    handlesRect.left = location->left - 4;
    handlesRect.right = location->left;
    
    PaintRect(&handlesRect);
    
    PenSize(2,2);
    MoveTo(location->left - 2, location->top - 2);
    LineTo(location->right, location->top - 2);
    LineTo(location->right, location->bottom);
    LineTo(location->left - 2, location->bottom);
    LineTo(location->left - 2, location->top - 2);
    //FrameRect(location);
}
 
/*
 *  hit test a click in the document.
 */
 
static TQ3Object DoHitTest(DocumentPtr  theDocument)
{
    TQ3WindowPointPickData  withData;
    unsigned long       numPicked;
    TQ3PickObject       pickObject;
 
    withData.data.sort          =   kQ3PickSortNone;
    withData.data.numHitsToReturn=  1;
    withData.data.mask          =   kQ3PickDetailMaskObject;
                                
    withData.point.x = theDocument->mouseLocation.h - theDocument->dropArea.left;
    withData.point.y = theDocument->mouseLocation.v - theDocument->dropArea.top;
 
    withData.vertexTolerance = withData.edgeTolerance = 3;
 
    pickObject = Q3WindowPointPick_New(&withData);
    if (pickObject == NULL) {
        return NULL;
    }
 
    Q3View_StartPicking(theDocument->theView, pickObject );
    do {
        Q3DisplayGroup_Submit(theDocument->documentGroup, theDocument->theView);
    } while (Q3View_EndPicking(theDocument->theView) == kQ3ViewStatusRetraverse);
    
    
    if (Q3Pick_GetNumHits(pickObject, &numPicked) == kQ3Success && (numPicked != 0)) {
        TQ3Object           object;
        TQ3PickDetail       validMask;
        unsigned long       hitIndex = 1;
 
        object = NULL;
/*      TQ3HitData hitData;
        
        // used to be: ErPick_GetFirstHit(pickObject, &hitData);
        Q3Pick_GetHitData( pickObject, 1, &hitData );
        Q3Object_Dispose(pickObject);
        return(hitData.object);
*/
        if (Q3Pick_GetPickDetailValidMask(pickObject, hitIndex, &validMask) == kQ3Success) {
 
            /* Quick check to see if the hit contains an object and shape part */
            if (validMask & kQ3PickDetailMaskObject) {
 
                /* Get reference to geometry object hit */
                Q3Pick_GetPickDetailData(   pickObject,
                                            hitIndex,
                                            kQ3PickDetailMaskObject,
                                            &object);
            }
        }
 
        Q3Object_Dispose(pickObject);
        return(object);
 
    } else {
        Q3Object_Dispose(pickObject);
        return(NULL);
    }
}
 
/*
 *  DoContent handles mouseDown events in the content region of a document window.
 *
 *  (1) If the mouseDown is on a control, handle the click by calling TrackControl.
 *
 *  (2) If the mouseDown is on a draggable object (the document's hiliteRgn) and a
 *      successful drag occurs, no further processing is necessary.
 *
 *  (3) If the mouseDown is on a draggable object and the mouse is released without
 *      dragging, set the insertion point to the original mouseDown location by calling
 *      TEClick with the mouseDown information.
 *
 *  (4) If the mouseDown is not on a draggable object and within the viewRect of the
 *      TextEdit field, call TEClick to handle the mouseDown.
 */
 
void Podium_DoContent(DocumentPtr   theDocument, EventRecord *theEvent)
 
{
    Point           thePoint;
 
    SetPort(theDocument->theWindow);
    thePoint = theEvent->where;
 
    if( theDocument->documentGroup ) {
        Point   newMouse, offset;
        short   whichHandle;
        
        GetMouse(&newMouse);
        theDocument->mouseLocation = newMouse;
        
        /* First check for resize */
        if( theDocument->currentlySelectedObject && PtOnHandles(&newMouse, &theDocument->dropArea, &whichHandle) ) {
                Rect    previousRect;
                Rect    frameRect;
                Point   oldMouse;
                TQ3Boolean  needRedraw;
                
                PenMode(srcXor);
                PenSize(2,2);
                frameRect = previousRect = theDocument->dropArea;
                
                oldMouse = theDocument->mouseLocation;
                switch( whichHandle ){
                    case LeftTopHandle:
                        
                        offset.h = newMouse.h - frameRect.left;
                        offset.v = newMouse.v - frameRect.top;
 
                        while (WaitMouseUp()) {
                            GetMouse(&newMouse);
                            if (newMouse.h >= oldMouse.h +  2 || newMouse.h <= oldMouse.h - 2 ||
                                newMouse.v >= oldMouse.v +  2 || newMouse.v <= oldMouse.v - 2) {
                                    needRedraw = kQ3True;
                                    frameRect.left = newMouse.h - offset.h;
                                    frameRect.top = newMouse.v - offset.v;
                                    DrawHandles(&previousRect);
                                    DrawHandles(&frameRect);
                                    previousRect = frameRect;
                            }
                        }
                    break;
                    case RightTopHandle:
                        
                        offset.h = newMouse.h - frameRect.right;
                        offset.v = newMouse.v - frameRect.top;
 
                        while (WaitMouseUp()) {
                            GetMouse(&newMouse);
                            if (newMouse.h >= oldMouse.h +  2 || newMouse.h <= oldMouse.h - 2 ||
                                newMouse.v >= oldMouse.v +  2 || newMouse.v <= oldMouse.v - 2) {
                                    needRedraw = kQ3True;
                                    frameRect.right = newMouse.h - offset.h;
                                    frameRect.top = newMouse.v - offset.v;
                                    DrawHandles(&previousRect);
                                    DrawHandles(&frameRect);
                                    previousRect = frameRect;
                            }
                        }
                    break;
                    case LeftBottomHandle:
                        
                        offset.h = newMouse.h - frameRect.left;
                        offset.v = newMouse.v - frameRect.bottom;
 
                        while (WaitMouseUp()) {
                            GetMouse(&newMouse);
                            if (newMouse.h >= oldMouse.h +  2 || newMouse.h <= oldMouse.h - 2 ||
                                newMouse.v >= oldMouse.v +  2 || newMouse.v <= oldMouse.v - 2) {
                                    needRedraw = kQ3True;
                                    frameRect.left = newMouse.h - offset.h;
                                    frameRect.bottom = newMouse.v - offset.v;
                                    DrawHandles(&previousRect);
                                    DrawHandles(&frameRect);
                                    previousRect = frameRect;
                            }
                        }
                    break;
                    case RightBottomHandle:
                        
                        offset.h = newMouse.h - frameRect.right;
                        offset.v = newMouse.v - frameRect.bottom;
 
                        while (WaitMouseUp()) {
                            GetMouse(&newMouse);
                            if (newMouse.h >= oldMouse.h +  2 || newMouse.h <= oldMouse.h - 2 ||
                                newMouse.v >= oldMouse.v +  2 || newMouse.v <= oldMouse.v - 2) {
                                    needRedraw = kQ3True;
                                    frameRect.right = newMouse.h - offset.h;
                                    frameRect.bottom = newMouse.v - offset.v;
                                    DrawHandles(&previousRect);
                                    DrawHandles(&frameRect);
                                    previousRect = frameRect;
                            }
                        }
                    break;
                }
    
                theDocument->dropArea = frameRect;
                
                if( needRedraw == kQ3True ) {
                    Podium_UpdateDrawContextFromDropRect( theDocument ) ;
                    DrawOffscreen(theDocument);
                    DrawOnscreen(theDocument);
                    
                }
                        
                PenMode(srcXor);
                PenSize(2,2);
                PenPat(&qd.black);
                DrawHandles(&frameRect);
        /* is it in content */
        } else if( PtInRect(newMouse, &theDocument->dropArea) ) {
//          TQ3Object   newPick;
//      if(( newPick = DoHitTest(theDocument)) != NULL) {
            if( theDocument->selected == kQ3False) {
                PenMode(srcXor);
                PenSize(2,2);
                PenPat(&qd.black);
                DrawHandles(&theDocument->dropArea);
            }
            
            theDocument->currentlySelectedObject = theDocument->documentGroup ;
            
            if( theEvent->modifiers & shiftKey )
                SetCursor(*zoomInCursor);
            else if( theEvent->modifiers & cmdKey )
                SetCursor(*zoomOutCursor);
            else
                SetCursor(*objectCursor);
        
            if( theEvent->modifiers & shiftKey ) {
                TQ3Matrix4x4    tmp;
                
                Q3Matrix4x4_SetScale(&tmp, 1.2, 1.2, 1.2);
                while (WaitMouseUp()) {
                    Q3Matrix4x4_Multiply(&tmp, &theDocument->modelRotation, &theDocument->modelRotation);
                    DrawOffscreen( theDocument ) ;
                    DrawOnscreen( theDocument ) ;
                }
            } else if( theEvent->modifiers & cmdKey ) {
                TQ3Matrix4x4    tmp;
                
                Q3Matrix4x4_SetScale(&tmp, 0.83333, 0.83333, 0.83333);
                while (WaitMouseUp()) {
                    Q3Matrix4x4_Multiply(&tmp, &theDocument->modelRotation, &theDocument->modelRotation);
                    DrawOffscreen(theDocument);
                    DrawOnscreen( theDocument ) ;
                }
            } else if( theEvent->modifiers & controlKey ) {
            
                Point   newMouse;
                long    mouseChanged = 0;
                long    dx, dy, oldX, oldY;
                float   width, height;
                float   xRot, yRot;
                        
                SetCursor(*handCursor);
                GetMouse(&newMouse);        // get the mouse in local co-ordinates, 
                                                // local to the  current GrafPort
        
                oldX = newMouse.h;
                oldY = newMouse.v;
                
                width = theDocument->geometriesOffscreen->portRect.right - theDocument->geometriesOffscreen->portRect.left;
                height =  theDocument->geometriesOffscreen->portRect.bottom - theDocument->geometriesOffscreen->portRect.top;
    
                while (WaitMouseUp()) {
                
                    GetMouse(&newMouse);        // get the mouse in local co-ordinates, 
                                                // local to the  current GrafPort
    
                    dx = newMouse.v - oldY;
                    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);
                            DrawOnscreen( theDocument ) ;
                        }
                    }
                    oldX = newMouse.h; oldY = newMouse.v;
                }
                
                if( mouseChanged ) {
                    theDocument->rotationDir.x = xRot;
                    theDocument->rotationDir.y = yRot;
                }
            } else {
                Rect    previousRect;
                Rect    frameRect;
                Point   oldMouse;
                TQ3Boolean  needRedraw;
                long    height, width;
                
                PenMode(srcXor);
                PenSize(2,2);
                frameRect = previousRect = theDocument->dropArea;
                height = frameRect.bottom - frameRect.top;
                width = frameRect.right - frameRect.left;
                
                offset.h = newMouse.h - frameRect.left;
                offset.v = newMouse.v - frameRect.top;
                
                oldMouse = theDocument->mouseLocation;
                while (WaitMouseUp()) {
                    GetMouse(&newMouse);
                    if (newMouse.h >= oldMouse.h +  2 || newMouse.h <= oldMouse.h - 2 ||
                        newMouse.v >= oldMouse.v +  2 || newMouse.v <= oldMouse.v - 2) {
                            needRedraw = kQ3True;
                            frameRect.left = newMouse.h - offset.h;
                            frameRect.top = newMouse.v - offset.v;
                            frameRect.right = frameRect.left + width;
                            frameRect.bottom = frameRect.top + height;
                            DrawHandles(&previousRect);
                            DrawHandles(&frameRect);
                            previousRect = frameRect;
                    }
                }
    
                theDocument->dropArea = frameRect;
                
                if( needRedraw == kQ3True ) {
                    Podium_UpdateDrawContextFromDropRect( theDocument ) ;
                    DrawOffscreen(theDocument);
                    DrawOnscreen(theDocument);
                        
                }
                PenMode(srcXor);
                PenSize(2,2);
                PenPat(&qd.black);
                DrawHandles(&frameRect);
            }
        } else {
            if( theDocument->selected == kQ3True) {
                theDocument->currentlySelectedObject = NULL;
                theDocument->selected = kQ3False;
                PenMode(srcXor);
                PenSize(2,2);
                PenPat(&qd.black);
                DrawHandles(&theDocument->dropArea);
            }
            SetCursor(&qd.arrow);
        }
    } 
    else  {
    
        if( theDocument->currentlySelectedObject ) {
            theDocument->currentlySelectedObject = NULL;
            PenMode(srcXor);
            PenSize(2,2);
            DrawHandles(&theDocument->dropArea);
            SetCursor(&qd.arrow);
        }
    }
}
 
 
/*
 *  DoBackgroundContent 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 DragText function
 *      calls SelectWindow in this case).
 */
 void Podium_DoBackgroundContent(DocumentPtr    theDocument, EventRecord *theEvent)
 
{
    Point           thePoint;
    RgnHandle       tempRgn = NewRgn();  // Nick this needs to be the BBox of the group
 
    SetPort(theDocument->theWindow);
    thePoint = theEvent->where;
    GlobalToLocal(&thePoint);
 
    RectRgn(tempRgn, &theDocument->geometriesOffscreen->portRect);
 
    if (PtInRgn(thePoint,tempRgn)) {
        if (! DoDragObjects(theDocument, theEvent, tempRgn)) {
            SelectWindow(theDocument->theWindow);
        }
    } 
    else {
        SelectWindow(theDocument->theWindow);
    }
    
    DisposeRgn(tempRgn);
}
 
 
 
 
/*
 *  DoIdle get called repetitively while the application is not doing
 *  anything.
 */
 
void Podium_DoIdle(EventRecord *theEvent)
{
    WindowPtr       theWindow;
    DocumentPtr theDocument;
 
    if ((theWindow = FrontWindow()) != nil) {
        if ((theDocument = GetDocumentFromWindow(theWindow)) != nil) {
            SetPort(theDocument->theWindow);
 
            if( theDocument->documentGroup ) {
                Point   mouseLocation;
                
                GetMouse(&mouseLocation);
 
                 theDocument->mouseLocation = mouseLocation;
                 
                 if( PointInRect(&theDocument->mouseLocation, &theDocument->dropArea) ) {
                    if(/*DoHitTest(theDocument)*/1) {
                        if( theEvent->modifiers & shiftKey) {
                            SetCursor(*zoomInCursor);
                        } else if( theEvent->modifiers & cmdKey) {
                            SetCursor(*zoomOutCursor);
                        } else if( theEvent->modifiers & controlKey) {
                            SetCursor(*handCursor);
                        } else {
                            SetCursor(*objectCursor);
                        }
                    } else {
                        SetCursor(&qd.arrow);
                    }
                } else {
                    SetCursor(&qd.arrow);
                }
                
                if( theDocument->animateModel == kQ3True) {
                    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);
                    }
                    DrawOffscreen(theDocument);
                    DrawOnscreen(theDocument);
                }
            }
        }
    }
}
 
void Podium_Init(void)
{
    objectCursor = GetCursor(132);
    zoomInCursor = GetCursor(133);
    zoomOutCursor = GetCursor(134);
    handCursor = GetCursor(130);
}
 
 
 
#endif /* PODIUM_APP */