MovieBrowser.c

/*
    File:       MovieBrowser.c
 
    Written by: Peter Hoddie
 
    Copyright:  © 1992-1994 by Apple Computer, Inc., all rights reserved.
 
    Change History (most recent first):
  <1>       12/8/94     khs     changed the format of the file to the new look and feel
 
*/
 
 
// INCLUDES
#include <Aliases.h>
#include <AppleEvents.h>
#include <Dialogs.h>
#include <Errors.h>
#include <Events.h>
#include <Memory.h>
#include <Movies.h>
#include <ToolUtils.h>
#include <LowMem.h>
#include <SegLoad.h>
#include <Fonts.h>
#include <GestaltEqu.h>
#include <Devices.h>
#include <Resources.h>
 
 
// STRUCTURES
typedef struct Document {
    WindowPtr           w;
    Movie               m;
    MovieController     mc;
 
    short               page;
    short               pageCount;
 
    short               curResID;
    short               resRef;
 
    Track               firstTextTrack;
 
    FSSpec              fss;
 
    Rect                movieArea;
    Rect                scrollArea;
 
    Boolean             noButtons;
    Boolean             showController;
 
    Rect                sprocketsRectTop;
    Rect                sprocketsRectBottom;
 
    Rect                scrollAndSprocketsArea;
 
    long                scrollOffset;
    long                scrollPhaseShift;
 
    RGBColor            backgroundColor;
 
    Rect                thumbnailSize;
 
    TimeValue           **lastMovieTime;
    Handle              names;
 
    PicHandle           titleBackground;
    Rect                titleRect;
    Rect                titleArea;
 
    Str255              movieName;
    Point               titlePlace;
 
    short               lastPageClick;
    unsigned long       lastPageClickTime;
} Document, *DocumentPtr;
 
typedef long (*DocFunction)(DocumentPtr d, void *refCon);
 
 
// FUNCTION PROTOTYPES
void doAnAlert(StringPtr s, short err);
void colorMenus(void);
Boolean doMenuItem(long selection);
void DoContentClick(DocumentPtr d, Point where, unsigned long clickTime);
 
 
DocumentPtr OpenDocument(FSSpec *fss);
void CloseDocument(DocumentPtr d);
void GoToPage(DocumentPtr d, short pageNum);
DocumentPtr GetWindowDocument(WindowPtr w);
long ForEachDocument(DocFunction df, void *refCon);
long TestOneEvent(DocumentPtr d, EventRecord *e);
long IdleOneDocument(DocumentPtr d, void *refCon);
PicHandle GetArrow(DocumentPtr d, Boolean right, Rect *bounds);
void GetScrollArea(DocumentPtr d, Rect *bounds);
void ActivateDocument(DocumentPtr d);
void UpdateDocument(DocumentPtr d);
void OpenFinderDocs(void);
OSErr MissedAEParameters (AppleEvent *message);
pascal OSErr OpenDocMessage(AppleEvent *message, AppleEvent *reply, long refcon);
pascal OSErr QuitAppMessage(AppleEvent *message, AppleEvent *reply, long refcon);
 
 
// GLOBALS
short gAppResFile;
Boolean gDone = false;
GWorldPtr gSprockets = 0;
Str255 gLastSearchText;
unsigned long gLastWNETime;
Boolean gInBackground = false;
long gSysVersion = 0;
 
 
// DEFINES
// don't give the system too much time...
#define kWNEInterval (90)
 
#define kBetweenThumbSize (2)
 
 
// FUNCTIONS
void doAnAlert(StringPtr s, short err)
{
    Str31 s1;
 
    NumToString(err, s1);
    ParamText(s, err ? s1 : 0, 0, 0);
    Alert(128, 0);
}
 
void colorMenus(void)
{
    MenuHandle mh;
    short count, i;
    DocumentPtr d = GetWindowDocument(FrontWindow());
 
    mh = GetMHandle(129);
    DisableItem(mh, 2);
    if (FrontWindow()) EnableItem(mh, 2);
 
    mh = GetMHandle(130);
    count = CountMItems(mh);
    for (i = 1; i <= count; i++)
        CheckItem(mh, i, d && (i == (d->page + 1)));
 
    mh = GetMHandle(131);
    DisableItem(mh, 1);
    DisableItem(mh, 3);
    DisableItem(mh, 4);
    DisableItem(mh, 5);
    DisableItem(mh, 6);
    DisableItem(mh, 8);
    DisableItem(mh, 9);
    DisableItem(mh, 11);
    if (d) {
        if (d->firstTextTrack) {
            EnableItem(mh, 8);
            if (gLastSearchText[0])
                EnableItem(mh, 9);
        }
        if (d->m)
            EnableItem(mh, 4);
 
        EnableItem(mh, 11);
        CheckItem(mh, 11, d->showController);
    }
    
}
 
Boolean doMenuItem(long selection)
{
    short menu = HiWord(selection);
    short item = LoWord(selection);
    Boolean done = false;
    DocumentPtr d = GetWindowDocument(FrontWindow());
 
    switch (menu) {
        case 128:
            if (item > 2) {
                Str255 daName;
 
                GetItem(GetMHandle(128), item, daName);
                OpenDeskAcc(daName);
            }
            else {
                short item = 0;
                short saveRes = CurResFile();
                DialogPtr d;
                short kind;
                Handle h;
                Rect r;
                
                UseResFile(gAppResFile);
                d = GetNewDialog(129, nil, (WindowPtr)-1);
 
                SetPort(d);
                GetDItem(d, 1, &kind, &h, &r);
                PenSize(3,3);
                InsetRect(&r,-4,-4);
                FrameRoundRect(&r,16,16);
 
                while (d && (item != 1))
                    ModalDialog(nil, &item);
                UseResFile(saveRes);
 
                if (d) DisposDialog(d);
            }
            break;
        case 129:
            switch (item) {
                case 1: {
                        StandardFileReply reply;
                        OSType docType = 'Dal’';
                        
                        StandardGetFilePreview(nil, 1, &docType, &reply);
                        if (reply.sfGood)
                            OpenDocument(&reply.sfFile);
                        }
                        break;
                case 2: CloseDocument(d);
                        break;
                case 4: done = true;
                        break;
            }
            break;
        case 130:
            GoToPage(GetWindowDocument(FrontWindow()), item - 1);
            break;
        case 131:
            switch (item) {
                case 4: if (d && d->m)
                            PutMovieOnScrap(d->m, DoTheRightThing);
                        break;
                case 8: {
                        DialogPtr dlg = GetNewDialog(130, nil, (WindowPtr)-1);
                        short kind;
                        Handle h;
                        Rect r;
                        short item = 0;
                        Str255 str;
                        ModalFilterProcPtr filter = nil;
 
                        if (!dlg) break;
 
                        // set up some dialog manager slime if not System 7
                        if (gSysVersion < 7 ) {
                            SetDialogDefaultItem(dlg, 1);
                            SetDialogCancelItem(dlg, 2);
                            SetDialogTracksCursor(dlg, true);
                            GetStdFilterProc(&filter);
                        }
 
                        GetDItem(dlg, 4, &kind, &h, &r);
                        SetIText(h, gLastSearchText);
                        SelIText(dlg, 4, 0, 32767);
 
                        while ((item != 1) && (item != 2))
                            ModalDialog(filter, &item);
 
                        GetIText(h, str);
                        DisposDialog(dlg);
 
                        if (item == 1 && str[0]) {
                            BlockMove(str, gLastSearchText, sizeof(gLastSearchText));
                            goto doSearch;
                        }
                        break;
                    }
                    case 9: {
                        TimeValue newTime;
                        OSErr err;
                        MediaHandler textHandler;
                        long textOffset;
doSearch:
                        // text media handler really wants lower case letters, so make sure it gets 'em
                        if (gSysVersion >= 7)
                            LowerText((Ptr)&gLastSearchText[1], gLastSearchText[0]);
                        else {
                            short i = 0;
                            while (i++ <= gLastSearchText[0]) {
                                char c = gLastSearchText[i];
                                if ((c >= 'A') && (c <= 'Z'))
                                    gLastSearchText[i] -= 32;
                            }
                        }
 
                        textHandler = GetMediaHandler(GetTrackMedia(d->firstTextTrack));
                        err = FindNextText(textHandler, (Ptr)&gLastSearchText[1], gLastSearchText[0],
                                findTextWrapAround, GetMovieTime(d->m, nil),
                                &newTime, nil, &textOffset);
                        if (!err && (newTime != -1)) {
                            TimeRecord tr;
                            RGBColor rgb;
                            
                            tr.value.lo = newTime;
                            tr.value.hi = 0;
                            tr.scale = GetMovieTimeScale(d->m);
                            tr.base = 0;
                            MCDoAction(d->mc, mcActionGoToTime, &tr);
                            rgb = (**((GrafVars **)((CGrafPtr)d->w)->grafVars)).rgbHiliteColor;
 
                            HiliteTextSample(textHandler, newTime, textOffset,
                                textOffset + gLastSearchText[0], &rgb);
                        }
                        else
                            SysBeep(1);
                    }
                    break;
                    case 11: {
                        d->showController = !d->showController;
                        MCSetVisible(d->mc, d->showController);
                        break;
                    }
                    break;
            }
    }
    HiliteMenu(0);
 
    return done;
}
 
 
DocumentPtr OpenDocument(FSSpec *fss)
{
    OSErr err;
    DocumentPtr d;
    Rect r;
    Point center;
    short resID;
    Handle h;
 
    d = (DocumentPtr)NewPtrClear(sizeof(Document));
    err = MemError();
    if (err != noErr) goto bail;
 
    d->fss = *fss;
 
    d->resRef = FSpOpenResFile(fss, fsRdPerm);
    if ((d->resRef == -1) || ResError()) {
        err = resNotFound;
        d->resRef = 0;
        goto bail;
    }
 
    d->lastPageClick = -1;
    d->names = NewHandle(0);
 
    // count the pages, and rememeber their names
    resID = 128;
    SetResLoad(false);
    do {
        Handle r;
        short id;
        OSType rt;
        Str255 name;
 
        r = Get1Resource(rAliasType, resID++);
        if (r != NULL)
            d->pageCount++;
        else
            break;
 
        // remember the name
        GetResInfo(r, &id, &rt, name);
        PtrAndHand(name, d->names, name[0] + 1);
        err = MemError();
        if (err != noErr) goto bail;
    } while (true);
    SetResLoad(true);
 
    // make the window
    d->w = GetNewCWindow(128, nil, (WindowPtr)-1);
    if (!d->w) {
        err = memFullErr;
        goto bail;
    }
    SetWTitle(d->w, fss->name);
    TextFont(GetSysFont());
 
    // get back color
    h = Get1Resource('RGB ', 128);
    if (h != NULL) {
        d->backgroundColor = **(RGBColor **)h;
        ReleaseResource(h);
    }
    else {
        short saveRes = CurResFile();
 
        UseResFile(gAppResFile);
            d->backgroundColor = **(RGBColor **)Get1Resource('RGB ', 128);
        UseResFile(saveRes);
    }
 
    // pick up first thumbnail pict for measurement
    h = Get1Resource('PICT', 128);
    if (!h) {
        err = resNotFound;
        goto bail;
    }
    d->thumbnailSize = (**(PicHandle)h).picFrame;
    OffsetRect(&d->thumbnailSize, -d->thumbnailSize.left, -d->thumbnailSize.top);
    d->thumbnailSize.right += kBetweenThumbSize;
 
    // show a controller?
    d->showController = false;
    h = Get1Resource('MCmc', 128);
    if (h) {
        d->showController = **h;
        ReleaseResource(h);
    }
 
    // make page time table
    d->lastMovieTime = (TimeValue **)NewHandleClear(d->pageCount * sizeof(TimeValue));
    err = MemError();
    if (err != noErr) goto bail;
 
    d->movieArea = **(Rect **)GetResource('RECT', 128);
    ReleaseResource(GetResource('RECT', 128));
    OffsetRect(&d->movieArea, -d->movieArea.left, -d->movieArea.top);
 
    // figure out what to use for title background
    d->titleBackground = (PicHandle)Get1Resource('PICT', 1048);
    if (!d->titleBackground) {
        short saveRes = CurResFile();
 
        UseResFile(gAppResFile);
            d->titleBackground = (PicHandle)GetPicture(1048);
        UseResFile(saveRes);
    }
    LoadResource((Handle)d->titleBackground);
    r = (**d->titleBackground).picFrame;
    OffsetRect(&r, -r.left, -r.top);
    OffsetRect(&r, (d->movieArea.right / 2) - (r.right / 2), 0);
    d->titleRect = r;
    OffsetRect(&d->titleRect, 0, (d->titleRect.bottom - d->titleRect.top) / 3);
    InsetRect(&r, 0, -((d->titleRect.bottom - d->titleRect.top) / 3));
    d->titleArea = r;
 
    OffsetRect(&d->movieArea, 0, d->titleArea.bottom);
    SizeWindow(d->w, d->movieArea.right,
        d->movieArea.bottom + d->thumbnailSize.bottom + (gSprockets->portRect.bottom * 2), false);
 
    // figure out how to keep things centered
    GetScrollArea(d, &r);
    d->scrollArea = r;
    OffsetRect(&r, -r.left, -r.top);
    d->scrollPhaseShift = (d->thumbnailSize.right) - (r.right % d->thumbnailSize.right)/2;
 
    // calculate sprockets rect
    d->sprocketsRectTop = d->scrollArea;
    d->sprocketsRectTop.bottom = d->scrollArea.top;
    d->sprocketsRectTop.top -= gSprockets->portRect.bottom;
 
    d->sprocketsRectBottom = d->scrollArea;
    d->sprocketsRectBottom.top = d->scrollArea.bottom;
    d->sprocketsRectBottom.bottom += gSprockets->portRect.bottom;
 
    UnionRect(&d->scrollArea, &d->sprocketsRectTop, &d->scrollAndSprocketsArea);
    UnionRect(&d->sprocketsRectBottom, &d->scrollAndSprocketsArea, &d->scrollAndSprocketsArea);
 
    SetWRefCon(d->w, (long)d);
 
    SetPort(d->w);
 
    r = d->w->portRect;
    GetBestDeviceRect(nil, &r);
    if (r.top == 0) r.top += GetMBarHeight();
    center.h = (r.left + r.right) >> 1;
    center.v = (r.top + r.bottom) >> 1;
    r.left = center.h - (d->w->portRect.right >> 1);
    r.right = r.left + d->w->portRect.right;
    r.top = center.v - (d->w->portRect.bottom >> 1);
    r.bottom = r.top + d->w->portRect.bottom;
    MoveWindow(d->w, r.left, r.top, false);
    AlignWindow(d->w, false, &d->movieArea, nil);
    ShowWindow(d->w);
 
    GoToPage(d, d->page);
 
bail:
    if (err) CloseDocument(d);
 
    return d;
}
 
void CloseDocument(DocumentPtr d)
{
    if (!d) return;
 
    if (d->resRef) CloseResFile(d->resRef);
    if (d->mc) DisposeMovieController(d->mc);
    if (d->lastMovieTime) DisposHandle((Handle)d->lastMovieTime);
    if (d->names) DisposHandle(d->names);
    DisposeMovie(d->m);
    if (d->w) DisposeWindow(d->w);
    DisposPtr((Ptr)d);
 
    ActivateDocument(0);                // clear out the movies menu
}
 
DocumentPtr GetWindowDocument(WindowPtr w)
{
    if (!w) return nil;
 
    if (((WindowPeek)w)->windowKind == userKind)
        return (DocumentPtr)GetWRefCon(w);
    else
        return nil;
}
 
void GoToPage(DocumentPtr d, short pageNum)
{
    OSErr err;
    short resID;
    Movie newMovie = nil;
    Point where = {0,0};
    Rect r;
    short saveRes;
    FSSpec movieFile;
    Boolean whoCares;
    short movieResRef;
    AliasHandle alias;
    RgnHandle rgn;
    short trashID;
    OSType trackType;
 
    if (pageNum < 0)
        pageNum = d->pageCount - 1;
    else
        pageNum = pageNum % d->pageCount; 
    resID = pageNum + 128;
    if (resID == d->curResID) return;
 
    // resolve alias to the file
    saveRes = CurResFile();
    UseResFile(d->resRef);
        alias = (AliasHandle)Get1Resource(rAliasType, resID);
    UseResFile(saveRes);
    if (!alias) return;
    err = ResolveAlias((gSysVersion >= 7) ? &d->fss : 0, alias, &movieFile, &whoCares);
    GetResInfo((Handle)alias, &trashID, &trackType, d->movieName);
    ReleaseResource((Handle)alias);
    if (err) return;
 
    // get the movie
    SetCursor(*GetCursor(watchCursor));
    OpenMovieFile(&movieFile, &movieResRef, fsRdPerm);
        SetPort(d->w);
        RGBBackColor(&d->backgroundColor);              // for erasing
        err = NewMovieFromFile(&newMovie, movieResRef, nil, nil,
                newMovieActive, nil);
        BackColor(whiteColor);
    SetCursor(&qd.arrow);
    CloseMovieFile(movieResRef);
    if (err) return;
 
    // position new movie a little
    GetMovieBox(newMovie, &r);
    OffsetRect(&r, -r.left, -r.top);
    SetMovieBox(newMovie, &r);
 
    // restore movie's previous time
    SetMovieTimeValue(newMovie, (*d->lastMovieTime)[pageNum]);
 
    // position movie in window
    if (d->mc) {
        TimeValue saveTime;
 
        // invalidate the old one
        rgn = MCGetWindowRgn(d->mc, d->w);
        if (rgn) {
            InvalRgn(rgn);
            DisposeRgn(rgn);
        }
 
        // store the old movie's time
        saveTime = GetMovieTime(d->m, nil);
        (*d->lastMovieTime)[d->page] = saveTime;
 
        SetMovieVolume(newMovie, GetMovieVolume(d->m));         // don't lose the volume setting
 
        // change the movie on the controller
        MCSetVisible(d->mc, false);
            MCSetMovie(d->mc, newMovie, d->w, where);
            MCPositionController(d->mc, &d->movieArea, nil, 0);
        if (d->showController)
            MCSetVisible(d->mc, true);
    }
    else {
        // create a new controller
        d->mc = NewMovieController(newMovie, &d->movieArea, d->showController ? 0 : mcNotVisible);
        if (!d->mc) return;
        MCDoAction(d->mc, mcActionSetKeysEnabled, (void *)true);
    }
 
    // update state
    DisposeMovie(d->m);
    d->m = newMovie;
    d->curResID = resID;
    d->page = pageNum;
 
    // look for text
    {
    long trackCount, i;
 
    d->firstTextTrack = nil;
    trackCount = GetMovieTrackCount(d->m);
    for (i=1; i<=trackCount; i++) {
        Track t = GetMovieIndTrack(d->m, i);
        OSType mediaType;
 
        GetMediaHandlerDescription(GetTrackMedia(t), &mediaType, nil, nil);
        if (mediaType == 'text') {
            d->firstTextTrack = t;
            break;
        }
    }
    }
 
    // invalidate the new one
    rgn = MCGetWindowRgn(d->mc, d->w);
    if (rgn) {
        InvalRgn(rgn);
        DisposeRgn(rgn);
    }
 
    // update the title variables
    {
    FontInfo fi;
    short width;    
 
    InvalRect(&d->titleRect);
    GetFontInfo(&fi);
    width = StringWidth(d->movieName);
    d->titlePlace.h = ((d->titleRect.right + d->titleRect.left) / 2) - (width / 2);
    d->titlePlace.v = ((d->titleRect.top + d->titleRect.bottom) / 2) + (fi.ascent / 2);
    }
}
 
void DoContentClick(DocumentPtr d, Point where, unsigned long clickTime)
{
    Rect r;
    short scrollValue = 0;
    unsigned long lastTicks = TickCount();
    long delayTime = 15;
    Rect mustRect = d->w->portRect;
    PicHandle showPict = nil, restorePict = nil;
 
    if (!d) return;
 
    SetPort(d->w);
    GlobalToLocal(&where);
 
tryAgain:
    // try left arrow
    GetArrow(d, false, &r);
    if (PtInRect(where, &r) && PtInRect(where, &mustRect)) {
        // go left
        if (d->scrollOffset != 0)
            d->scrollOffset--;
        else
            d->scrollOffset = d->pageCount - 1;
        scrollValue = +1;
        mustRect = r;
        restorePict = GetPicture(1024);
        showPict = GetPicture(1026);
 
        d->lastPageClick = -1;
 
        goto gotHit;
    }
 
    // try right arrow
    GetArrow(d, true, &r);
    if (PtInRect(where, &r) && PtInRect(where, &mustRect)) {
        // go right
        if (d->scrollOffset != (d->pageCount - 1))
            d->scrollOffset++;
        else
            d->scrollOffset = 0;
        scrollValue = -1;
        mustRect = r;
        restorePict = GetPicture(1025);
        showPict = GetPicture(1027);
        goto gotHit;
    }
 
    r = d->scrollArea;
    if (PtInRect(where, &r) && PtInRect(where, &mustRect)) {
        // clicked on an image
        short internalOffset = (where.h - r.left + d->scrollPhaseShift) / d->thumbnailSize.right;
        Boolean wasIn, inRect;
        Rect clipRect;
 
        r = d->scrollArea;
        ClipRect(&r);
        clipRect = r;
 
        r.right = r.left + d->thumbnailSize.right;
        OffsetRect(&r, internalOffset * d->thumbnailSize.right, 0);
        OffsetRect(&r, -d->scrollPhaseShift, 0);
        PenMode(patXor);
        PenPat((ConstPatternParam)&qd.gray);
        PenSize(3, 3);
        FrameRect(&r);
        wasIn = true;
        while (StillDown()) {
            GetMouse(&where);
            inRect = PtInRect(where, &r);
            if (inRect != wasIn) {
                FrameRect(&r);
                wasIn = inRect;
            }
            ClipRect(&d->w->portRect);
                ForEachDocument(IdleOneDocument, nil);
            ClipRect(&clipRect);
        }
        if (wasIn) FrameRect(&r);
        PenNormal();
        ClipRect(&d->w->portRect);
        if (wasIn) {
            short newPage = (d->scrollOffset + internalOffset) % d->pageCount;
            SInt32 dTime = LMGetDoubleTime();
            
            if (    (  d->lastPageClick == newPage) && 
                    ( (d->lastPageClickTime + dTime) >= clickTime) 
               ) {
                Fixed curRate;
 
                MCDoAction(d->mc, mcActionGetPlayRate, &curRate);
                if (!curRate) {
                    EventRecord whoCares;
                    TimeValue t = GetMovieTime(d->m, nil);
                    TimeValue movieDur = GetMovieDuration(d->m);
                    Fixed preferredRate = GetMoviePreferredRate(d->m);
 
                    if (t == movieDur) t = 0;
                    PrerollMovie(d->m, t, preferredRate);
                    EventAvail(0, &whoCares);
                    EventAvail(0, &whoCares);
                    EventAvail(0, &whoCares);
                    UpdateDocument(d);
                    MCIdle(d->mc);
                    MCIdle(d->mc);
                    MCIdle(d->mc);
                    MCDoAction(d->mc, mcActionPlay, (void *)preferredRate);
                }
                d->lastPageClick = -1;
                d->lastPageClickTime += 500;
            }
            else {
                if (newPage == d->page) {
                    MCDoAction(d->mc, mcActionPlay, (void *)0);
                    d->lastPageClick = newPage;
                }
                else {
                    GoToPage(d, newPage);
                    d->lastPageClick = newPage;
                }
            }
            d->lastPageClickTime = clickTime;
        }
        r = mustRect;
        goto gotHit;
    }
 
gotHit:
    if (scrollValue) {
        Rect r;
        RgnHandle rgn;
 
        if (showPict) DrawPicture(showPict, &mustRect);
        r = d->scrollArea;
        rgn = NewRgn();
        if (rgn != NULL) {
            RGBBackColor(&d->backgroundColor);
                ScrollRect(&r, d->thumbnailSize.right * scrollValue, 0, rgn);
            BackColor(whiteColor);
            InvalRgn(rgn);
            DisposeRgn(rgn);
            UpdateDocument(d);
            ClipRect(&d->w->portRect);
            do {
                ForEachDocument(IdleOneDocument, nil);
            } while (StillDown() && ((unsigned long)TickCount()) < (lastTicks + delayTime));
            if (StillDown()) {
                lastTicks = TickCount();
                GetMouse(&where);
                delayTime -= 3;
                if (delayTime < 0) delayTime = 0;
                scrollValue = 0;
                goto tryAgain;
            }
        }
    }
    if (restorePict) DrawPicture(restorePict, &mustRect);
}
 
PicHandle GetArrow(DocumentPtr d, Boolean right, Rect *bounds)
{
    PicHandle aPict;
    Rect r;
 
    if (d->noButtons) {
        SetRect(bounds, 0, 0, 0, 0);
        return GetPicture(1024);
    }
 
    if (!right) {
        // left arrow
        aPict = GetPicture(1024);
        r = (**aPict).picFrame;
        OffsetRect(&r, -r.left + 5, -r.top);
        OffsetRect(&r, 0, d->w->portRect.bottom - r.bottom);
    }
    else {
        // right arrow
        aPict = GetPicture(1025);
        r = (**aPict).picFrame;
        OffsetRect(&r, -r.left, -r.top);
        OffsetRect(&r, d->w->portRect.right - r.right - 5, d->w->portRect.bottom - r.bottom);
    }
    OffsetRect(&r, 0, -(d->thumbnailSize.bottom/2) + (r.bottom - r.top)/2);
    OffsetRect(&r, 0, -(gSprockets->portRect.bottom * 3)/2);
    if (bounds) *bounds = r;
 
    return aPict;
}
 
void GetScrollArea(DocumentPtr d, Rect *bounds)
{
    Rect r, r1, r2;
    short bottom;
    long maxWidth = (d->pageCount * d->thumbnailSize.right);
 
    GetArrow(d, false, &r1);
    GetArrow(d, true, &r2);
    bottom = d->w->portRect.bottom - ((gSprockets->portRect.bottom * 3)/2);
    SetRect(&r, r1.right, bottom - d->thumbnailSize.bottom, r2.left, bottom);
    InsetRect(&r, 4, 0);
 
    if ((r.right - r.left) > maxWidth) {
        // don't wrap
        short center = (r1.right + r2.left) / 2;
        r.right = r.left + maxWidth;
        OffsetRect(&r, -r.left + (center - (r.right - r.left)/2), 0);
        d->noButtons = true;
    }
 
    *bounds = r;
}
 
void UpdateDocument(DocumentPtr d)
{
    PicHandle aPict;
    Rect r;
    RgnHandle rgn = 0;
    Rect scrollArea;
 
    if (!d) return;
 
    if (d->mc)
        rgn = MCGetWindowRgn(d->mc, d->w);
    scrollArea = d->scrollArea;
    BeginUpdate(d->w);
        SetPort(d->w);
 
        ClipRect(&d->w->portRect);
        if (rgn) {
            DiffRgn(d->w->clipRgn, rgn, d->w->clipRgn);
            RectRgn(rgn, &d->scrollAndSprocketsArea);
            DiffRgn(d->w->clipRgn, rgn, d->w->clipRgn);
            RectRgn(rgn, &d->titleRect);
            DiffRgn(d->w->clipRgn, rgn, d->w->clipRgn);
        }
 
        RGBForeColor(&d->backgroundColor);
            PaintRect(&d->w->portRect);
        ForeColor(blackColor);
 
        if (rgn)
            UnionRgn(d->w->clipRgn, rgn, d->w->clipRgn);
 
        // draw the title
        LoadResource((Handle)d->titleBackground);
        HNoPurge((Handle)d->titleBackground);
            DrawPicture(d->titleBackground, &d->titleRect);
        HPurge((Handle)d->titleBackground);
        MoveTo(d->titlePlace.h, d->titlePlace.v);
        TextFont(GetSysFont());
        DrawString(d->movieName);
 
        // left arrow
        aPict = GetArrow(d, false, &r);
        DrawPicture(aPict, &r);
 
        // right arrow
        aPict = GetArrow(d, true, &r);
        DrawPicture(aPict, &r);
 
        // sprockets
        {
        Rect r1 = d->sprocketsRectTop;
        Rect r2 = d->sprocketsRectBottom;
        Rect clip;
        short pageOffset;
 
        UnionRect(&r1, &r2, &clip);
        ClipRect(&clip);
 
        pageOffset = d->scrollPhaseShift;
        r1.left -= pageOffset;
        r2.left = r1.left;
 
        r1.right = r1.left + gSprockets->portRect.right;
        r2.right = r1.right;
        while (r1.left < d->scrollArea.right) {
            CopyBits((BitMap *)*gSprockets->portPixMap, &d->w->portBits, &gSprockets->portRect,
                    &r1, srcCopy, nil);
            CopyBits((BitMap *)*gSprockets->portPixMap, &d->w->portBits, &gSprockets->portRect,
                    &r2, srcCopy, nil);
            OffsetRect(&r1, gSprockets->portRect.right, 0);
            OffsetRect(&r2, gSprockets->portRect.right, 0);
        }
        }
 
        // thumbnails
        {
        short firstPage;
        short resID;
        Rect thumbRect;
        short saveRes = CurResFile();
 
        r = scrollArea;
        ClipRect(&r);
 
        firstPage = d->scrollOffset;
        resID = firstPage + 128;
        thumbRect = r;
        thumbRect.right = thumbRect.left + d->thumbnailSize.right;
        OffsetRect(&thumbRect, -d->scrollPhaseShift, 0);
        UseResFile(d->resRef);
        while (thumbRect.left < r.right) {
            Rect whoCares;
 
            if ((resID - 128) >= d->pageCount)
                resID = 128;
 
            if (SectRect(&(**d->w->visRgn).rgnBBox, &thumbRect, &whoCares)) {
                PicHandle p = GetPicture(resID);
 
                MoveTo(thumbRect.left, thumbRect.top);
                LineTo(thumbRect.left, thumbRect.bottom);
 
                MoveTo(thumbRect.right - 1, thumbRect.top);
                LineTo(thumbRect.right - 1, thumbRect.bottom);
                if (p) {
                    Rect r = thumbRect;
 
                    r.right--;
                    r.left++;
 
                    LoadResource((Handle)p);
                    HNoPurge((Handle)p);
                        DrawPicture(p, &r);
                    HPurge((Handle)p);
                }
            }
 
            OffsetRect(&thumbRect, d->thumbnailSize.right, 0);
            resID++;
        }
        UseResFile(saveRes);
        }
 
        ClipRect(&d->w->portRect);
        
        // dividing line
        {
        Rect r = d->scrollAndSprocketsArea;
        RGBColor aColor;
 
        r.left = 0;
        r.right = d->w->portRect.right;
        r.top -= (gSprockets->portRect.bottom/2) + 2;
 
        aColor = d->backgroundColor;
        aColor.red -= (aColor.red /6);
        aColor.green -= (aColor.green /6);
        aColor.blue -= (aColor.blue /6);
        RGBForeColor(&aColor);
        MoveTo(r.left, r.top);
        LineTo(r.right, r.top);
 
        aColor = d->backgroundColor;
        aColor.red += (aColor.red /6);
        aColor.green += (aColor.green /6);
        aColor.blue += (aColor.blue /6);
        RGBForeColor(&aColor);
        MoveTo(r.left, r.top + 1);
        LineTo(r.right, r.top + 1);
        }
 
    EndUpdate(d->w);
    if (rgn) DisposeRgn(rgn);
}
 
void ActivateDocument(DocumentPtr d)
{
    MenuHandle mh = GetMHandle(130);
    short count, index = 0;
    Ptr p;
    char c[2];
 
    count = CountMItems(mh);
    while (count--)
        DelMenuItem(mh, count + 1);
 
    if (!d) return;
 
    count = d->pageCount;
    HLock(d->names);
    p = *d->names;
    c[0] = 1;
    c[1] = 'e';
    while (count--) {
        AppendMenu(mh, (StringPtr)&c);
        SetItem(mh, ++index, (StringPtr)p);
        p += (*p + 1);
    }
    HUnlock(d->names);
}
 
long IdleOneDocument(DocumentPtr d, void *refCon)
{
    if (d->mc)
        MCIdle(d->mc);
    return 0;
}
 
long ForEachDocument(DocFunction df, void *refCon)
{
    WindowPtr w = FrontWindow();
    long result = 0;
 
    while (w) {
        DocumentPtr d = GetWindowDocument(w);
        if (d) {
            result = (df)(d, refCon);
            if (result) break;
        }
        w = (WindowPtr)((WindowPeek)w)->nextWindow;
    }
 
    return result;
}
 
long TestOneEvent(DocumentPtr d, EventRecord *e)
{
    if (d->mc && d->m)
        return MCIsPlayerEvent(d->mc, e);
 
    return 0;
}
 
void OpenFinderDocs(void)
{
    short printIt, docCount;                // no printing
 
    CountAppFiles(&printIt, &docCount);
    
    if (docCount > 0) {
        short j;
 
        for(j = 1; j <= docCount; j++){
            FSSpec fss;
            AppFile apfile;
 
            GetAppFiles(j,&apfile);
            FSMakeFSSpec(apfile.vRefNum, 0, apfile.fName, &fss);
 
            OpenDocument(&fss);
            ClrAppFiles(j);
        }
    }
}
 
OSErr MissedAEParameters (AppleEvent *message)
{
    DescType typeCode;
    Size actualSize;
    OSErr err;
 
    err = AEGetAttributePtr(message, keyMissedKeywordAttr, typeWildCard,
            &typeCode, nil, 0L, &actualSize);
    if (err == errAEDescNotFound)
        return(noErr);
    return(err = noErr ? errAEEventNotHandled : err);
}
 
pascal OSErr OpenDocMessage(AppleEvent *message, AppleEvent *reply, long refcon)
{
    FSSpec fss;
    AEDescList docList;
    long index, itemsInList;
    Size actualSize;
    AEKeyword keywd;
    DescType typeCode;
    OSErr err;
 
    if ((err = AEGetParamDesc(message, keyDirectObject, typeAEList, &docList)) != noErr)
        return(err);
    if ((err = MissedAEParameters(message)) != noErr)
        return(err);
    if ((err = AECountItems(&docList, &itemsInList)) != noErr)
        return(err);
 
    for (index = 1; index <= itemsInList; index++)
        {
        if ((err = AEGetNthPtr(&docList, index, typeFSS, &keywd, &typeCode,
                    (Ptr)&fss, sizeof(FSSpec), &actualSize)) != noErr)
            break;
            OpenDocument(&fss);
        }
 
    return(AEDisposeDesc(&docList));
}
 
pascal OSErr QuitAppMessage(AppleEvent *message, AppleEvent *reply, long refcon)
{
    OSErr err;
 
    if ((err = MissedAEParameters(message)) != noErr)
        return(err);
 
    gDone = true;
 
    return(noErr);
}
 
void main(void)
{
    OSErr err;
    long response;
 
    // initialize the world
    InitGraf(&qd.thePort);
    InitFonts();
    InitWindows();
    InitMenus();
    TEInit();
    InitDialogs(0L);
    InitCursor();
    MaxApplZone();
    
    // find the system version (used later)
    Gestalt(gestaltSystemVersion, &gSysVersion);
    gSysVersion = (gSysVersion >> 8) & 0x0F;
 
    // must have QuickTime around
    if (Gestalt(gestaltQuickTime, &response)) {
        doAnAlert("\pPlease install QuickTimeª.", 0);
        return;
    }
 
    if (response < 0x01508000) {
        doAnAlert("\pMovieBrowserª required QuickTime 1.5 or later.", 0);
        return;
    }
 
    err = EnterMovies();
    if (err) {
        doAnAlert("\pEnterMovies failed.", err);
        return;
    }
 
    SetMenuBar(GetNewMBar(128));
    DrawMenuBar();
 
    AddResMenu(GetMHandle(128), 'DRVR');
 
    gAppResFile = CurResFile();
    gLastSearchText[0] = 0;
    gLastWNETime = TickCount();
 
    // load up the sprockets picture
    {
    PicHandle p = GetPicture(1028);
    Rect r;
    CGrafPtr savePort;
    GDHandle saveGD;
 
    if (!p) return;
    r = (**p).picFrame;
    if (NewGWorld(&gSprockets, 1, &r, 0, 0, 0) != noErr) {
        doAnAlert("\pOut of memory", -108);
        return;
    }
    LockPixels(gSprockets->portPixMap);
 
    GetGWorld(&savePort, &saveGD);
    SetGWorld(gSprockets, nil);
        EraseRect(&r);
        DrawPicture(p, &r);
    SetGWorld(savePort, saveGD);
 
    ReleaseResource((Handle)p);
    }
 
    if((Gestalt(gestaltAppleEventsAttr, &response) ? false : response != 0)) {
        AEInstallEventHandler(kCoreEventClass, kAEOpenDocuments,
                (AEEventHandlerProcPtr)OpenDocMessage, 0, false);
        AEInstallEventHandler(kCoreEventClass, kAEQuitApplication,
                (AEEventHandlerProcPtr)QuitAppMessage, 0, false);
    }
 
    OpenFinderDocs();
 
    do {
        EventRecord e;
        long result;
        DocumentPtr d;
        unsigned long ticksNow = TickCount();
        Boolean mustGetEvent = gInBackground;
 
        if (!mustGetEvent) {
            // see if we have a pending update
            WindowRef w = LMGetWindowList();
            while (w) {
                if (!EmptyRect(&(**((WindowPeek)w)->updateRgn).rgnBBox)) {
                    mustGetEvent = true;
                    break;
                }
                w = (WindowPtr)(((WindowPeek)w)->nextWindow);
            }
        }
 
        if (mustGetEvent || ((gLastWNETime + kWNEInterval) < ticksNow)) {
            WaitNextEvent(everyEvent, &e, 0, nil);
            gLastWNETime = ticksNow;
        }
        else {
            if (OSEventAvail(mDownMask | keyDownMask, &e))
                WaitNextEvent(everyEvent, &e, 0, nil);
            else
                e.what = nullEvent;
        }
 
#ifdef THINK_C      
        result = ForEachDocument((DocFunction)TestOneEvent, &e);
#else
        result = ForEachDocument(TestOneEvent, &e);
#endif
        if (result) continue;
        switch (e.what) {
            case mouseDown:
                {
                short part;
                WindowPtr whichWindow;
 
                part = FindWindow(e.where, &whichWindow);
                if (part) {
                    switch (part) {
                        case inMenuBar:
                                    colorMenus();
                                    gDone = doMenuItem(MenuSelect(e.where));
                                    break;
                        case inSysWindow:
                                    SystemClick(&e, whichWindow);
                                    break;
                        case inContent:
                                    if (whichWindow != FrontWindow()) {
                                        SelectWindow(whichWindow);
                                        break;
                                    }
                                    DoContentClick(GetWindowDocument(whichWindow), e.where, e.when);
                                    break;
                        case inDrag:
                                    d = GetWindowDocument(whichWindow); 
                                    if (d != NULL) {
                                        Rect r;
 
                                        GetMovieBox(d->m, &r);
                                        DragAlignedWindow(whichWindow, e.where, &qd.screenBits.bounds, &r, nil);
                                    }
                                    else
                                        DragWindow(whichWindow, e.where, &qd.screenBits.bounds);
                                    break;
                        case inGrow:
                                    break;
                        case inGoAway:
                                    if (TrackGoAway(whichWindow, e.where))
                                        CloseDocument(GetWindowDocument(whichWindow));
                                    break;
                    }
                }
                }
                break;
 
            case updateEvt:
                UpdateDocument(GetWindowDocument((WindowPtr)e.message));
                break;
            case keyDown:
                {
                char c;
 
                c = e.message & charCodeMask;
                if (e.modifiers & cmdKey) {
                    colorMenus();
                    gDone = doMenuItem(MenuKey(c));
                }
                }
                break;
            case app4Evt:   
                if ((e.message & 0xFF000000L) == 0x01000000L) {
                    if (e.message & 1) {
                        InitCursor();
                        ActivateDocument(GetWindowDocument(FrontWindow()));
                        gInBackground = false;
                    }
                    else
                        gInBackground = true;
                }
                break;
            case activateEvt:
                ActivateDocument(GetWindowDocument((WindowPtr)e.message));
                break;
            case kHighLevelEvent:
                AEProcessAppleEvent(&e);
                break;
        }
 
    } while (!gDone);
 
    // QuickTime will take care of cleaning up after itself...
}