Sources/Application.cp

/*
    File:       Application.cp
 
    Contains:   TApplication is an abstract base class, TBKApplication is a background only application class,
                TGUIApplication is a window-based user application class. TQTApplication is a QuickTime aware application
                class, TGXApplication is a Quick GX aware application class. Finally TQTAndGXApplication 
                handles both QX and QT application work.
                Application.cp contains the needed member functions for the classes defined above.
 
    Written by:     
 
    Copyright:  Copyright © 1992-1999 by Apple Computer, Inc., All Rights Reserved.
 
                You may incorporate this Apple sample source code into your program(s) without
                restriction. This Apple sample source code has been provided "AS IS" and the
                responsibility for its operation is yours. You are not permitted to redistribute
                this Apple sample source code as "Apple sample source code" after having made
                changes. If you're going to re-distribute the source, we require that you make
                it clear in the source that the code was descended from Apple sample source
                code, but that you've made changes.
 
    Change History (most recent first):
                8/18/1999   Karl Groethe    Updated for Metrowerks Codewarror Pro 2.1
                
 
*/
// INCLUDE FILES
#ifndef _APPLICATION_
#include "Application.h"
#endif
 
 
// GLOBAL DATA AND FUNCTIONS
TApplication* gApplication;                     // universal ptr to the framework
 
const short kDefaultNumMasters = 8;             // number of master pointers
const OSType kAEResource = 'aedt';              // our AE binding resource
 
pascal OSErr AEDispatcher(AppleEvent* in,
                          AppleEvent* out,
                          long Command);        // AE Dispatcher
 
 
// Global Functions
pascal OSErr AEDispatcher(AppleEvent* in,
                          AppleEvent* out,
                          long Command)
{
    gApplication->DispatchAppleEvents(in, out, Command);// dispatch event back to the framework
 
    return noErr;
}
 
 
// _________________________________________________________________________________________________________ //
// TApplication class member function implementations.
 
//  CONSTRUCTORS & DESTRUCTORS
 
#pragma segment Application 
TApplication::TApplication()
// Default constructor -- put together the basic environment (non-UI model).
{
    gApplication = this;                        // hook ptr to the global pointer
 
    // Default base class constructor, define fields to known initial values.
    this->SetState(TApplication::kInit);        // set out first state
    fSleepRegion = ::NewRgn();                  // define default sleep region for WaitNextEvent (NULL)
    fSleepValue = TApplication::kBackgroundSleepValue;// default sleep value (ticks)
    fEventMask = everyEvent;                    // default setting
    fMoreMasters = kDefaultNumMasters;          // amount of more master calls
 
    this->InitializeMemory();                   // initialize memory
    this->InstallAEHandler();                   // install our AE handler
}
 
 
#pragma segment Application 
TApplication::~TApplication()
// Default destructor -- empty for the time being.
{
}
 
 
#pragma segment Application
void TApplication::DoNextEvent()
// Handle incoming events
{
    if (!::WaitNextEvent(fEventMask, &fEventRecord, fSleepValue, fSleepRegion))
        fEventRecord.what = nullEvent;          // security issue
}
 
 
#pragma segment Application
void TApplication::DoHighLevelEvent()
// Handle high level events.
{
    fError = ::AEProcessAppleEvent(&fEventRecord);// handle primarly AEs
    VASSERT(fError == noErr, ("Problems with AEProcessAppleEvent", fError));
}
 
#pragma segment Application
void TApplication::Start()
// Start the application and let it run until we change the state to Quit.
{
    this->SetState(TApplication::kRun);         // set TApplication to run state
 
    while (fState != TApplication::kQuit)
        this->DoEventLoop();
    this->Quit();                               // out from the state, we will quit
}
 
 
#pragma segment Application
void TApplication::Quit()
// Quit the application, do any possible cleanup.
{
}
 
 
//  CORE AE HANDLER METHODS
#pragma segment Application
void TApplication::InstallAEHandler()
// Install our core Apple Event Handler that will dispatch the AEs back to the framework.
{
    Handle aedtResource = NULL;
    SignedByte state;
    Size tableSize;
    AEEventTablePtr tablePtr;
 
    short numEntries = ::Count1Resources(kAEResource);// find out how many entries in the table
    fError = ResError();
    VASSERT(fError == noErr, ("Problems with Count1Resources = %d", fError));
    
    if(numEntries != 0)                             // we had 'aedt' resources
    {
        for (short i = 1; i <= numEntries; ++i)     // loop through the resources
        {
            aedtResource = ::Get1IndResource('aedt', i);// get resource
            fError = ResError();
            VASSERT(fError == noErr, ("Problems with Get1IndResource = %d", fError));
            
            state = ::HGetState(aedtResource);      // get the flags from the handle
            ::HLockHi(aedtResource);                // and lock the handle high in memory
 
            tableSize = GetHandleSize(aedtResource);// get the size of the resource
            fError = MemError();
            VASSERT(fError == noErr, ("Problems with GetHandleSize = %d", fError));
            
            short elements = (short)(tableSize / sizeof(AEEventTable));// get N of elements in resource
            tablePtr = (AEEventTablePtr) * aedtResource;// get ptr to the element
 
            // ¥¥¥ Add the information into our fCommandTable as well for future lookups
            for (short j = 0; j < elements; j++)    // go through the elements
            {
                fError  = ::AEInstallEventHandler(tablePtr->theClass, tablePtr->theID, NewAEEventHandlerProc(AEDispatcher), tablePtr->theValue, false);
                VASSERT(fError == noErr, ("Problems with AEInstallEventHandler = %d", fError));
                ++tablePtr;
            }
            ::HSetState(aedtResource, state);       // restore state
            ::ReleaseResource(aedtResource);        // release the resource
        }
    }
}
 
 
#pragma segment Application
void TApplication::DispatchAppleEvents(AppleEvent* in,
                                       AppleEvent* out,
                                       long command)
// Dispatch to the right Handler based on the command in the AE (refCon).
{
    switch (command)
    {
        case cQuitCommand:                      // a quit AE
            this->HandleQuit(in, out, command);
            break;
        case cNewCommand:                           // a  new AE
            this->HandleOpen(in, out, command);
            break;
        case cOpenCommand:                      // an open document AE
            this->HandleOpenDocuments(in, out, command);
            break;
        case cPrintCommand:                     // a print AE
            this->HandlePrint(in, out, command);
            break;
        default:                                        // hmm, something else thenÉ
            ASSERT(false, "\pProblem: We are dealing with an AE command that we have no handler for");
            break;
    }
}
 
 
#pragma segment Application
OSErr TApplication::HandleQuit(AppleEvent*      /*in*/,
                               AppleEvent*      /*out*/,
                               long             /*refCon*/)
// Our General Quit Handler. Set state to Quit and return.
{
    this->SetState(TApplication::kQuit);        // set state to Quit
    return noErr;                               // need to have this due to AEHandler prototype
}
 
 
#pragma segment Application
OSErr TApplication::HandleOpen(AppleEvent*      /*in*/,
                               AppleEvent*      /*out*/,
                               long             /*refCon*/)
{
    return errAEEventNotHandled;                // need to have this due to AEHandler prototype
}
 
#pragma segment Application
OSErr TApplication::HandleOpenDocuments(AppleEvent*/*in*/ ,
                                        AppleEvent*/*out*/ ,
                                        long    /*refCon*/)
{
    return errAEEventNotHandled;                // need to have this due to AEHandler prototype
}
 
#pragma segment Application
OSErr TApplication::HandlePrint(AppleEvent*     /*in*/,
                                AppleEvent*     /*out*/,
                                long            /*refCon*/)
{
    return errAEEventNotHandled;                // need to have this due to AEHandler prototype
}
 
 
//  STATE CHANGE METHODS
#pragma segment Application
void TApplication::SetState(TApplication::EState theState)
{
    fState = theState;
}
 
 
//  MAIN INTERFACE
#pragma segment Application
void TApplication::InitializeMemory()
// Initialize issues dealing with memory use.
{
    ::MaxApplZone();                            // increase the zone
    for (short i = 0; i < fMoreMasters; i++)    // bounce up the amount of master pointers
        ::MoreMasters();
    fError = MemError();
    VASSERT(fError == noErr, ("Problems with MoreMasters = %d", fError));
}
 
 
// _________________________________________________________________________________________________________ //
// TBKApplication class member function implementations
 
//  CONSTRUCTORS & DESTRUCTORS
 
#pragma segment Application 
TBKApplication::TBKApplication()
{
}
 
 
#pragma segment Application 
TBKApplication::~TBKApplication()
{
}
 
 
// _________________________________________________________________________________________________________ //
// TGUIApplication class member function implementations
 
//  CONSTRUCTORS & DESTRUCTORS
 
#pragma segment Application 
TGUIApplication::TGUIApplication()
// Initialize the UI toolbox side.
{
    this->InitializeToolbox();                  // Initialize toolbox
    TMenubar myMenubar;                         // handle my menu bar initialization
 
    fCursHandle = ::GetCursor(watchCursor);     // get the watch cursor resource
    ::SetCursor(*fCursHandle);                  // change the cursor to the watch one
}
 
 
#pragma segment Application 
TGUIApplication::~TGUIApplication()
// Default destructor -- empty for the time being.
{
}
 
 
#pragma segment Application
void TGUIApplication::InitializeToolbox()
// Initialize the toolbox requirements for a standard window based application.
{
    ::InitGraf(&qd.thePort);
    ::InitFonts();
    ::InitWindows();
 
    // _DON'T_ flush disk-inserted or os events or you'll be sorry! 
    ::FlushEvents(everyEvent - diskMask - osMask, 0);
 
    // The following toolbox init calls are slightly overhead, but that's fine 
    ::InitMenus();
    ::TEInit();
    ::InitDialogs(NULL);
 
    ::InitCursor();
}
 
//  MAIN INTERFACE
#pragma segment Application
void TGUIApplication::Start()
// Start the event loop handling.
{
    ::SetCursor(&qd.arrow);                     // OK, scursor back to to the arrow one
    TApplication::Start();                      // call the inherited Start
}
 
 
//  EVENT HANDLING MEMBER FUNCTIONS
#pragma segment Application
void TGUIApplication::DoEventLoop()
// This is the big event loop swith statement function.
{
    this->DoNextEvent();                        // get the next event record
 
    // This is the big switch statement, switch on events received
    switch (fEventRecord.what)
    {
        case nullEvent:                         // we got a periodic null event
            this->DoIdle();                     // call idle handler
            break;
 
        case updateEvt:                         // we got an update event
            this->DoWindowUpdate();             // call the window update member function
            break;
            
        case diskEvt:                           // we got a disk insertion event (most likely formatting)
            this->DoDiskEvent();
            break;
 
        case mouseDown:                         // we got a mouse down event
            // get the window where the mouse down happened, and do an action based on this
            switch (::FindWindow(fEventRecord.where, &fCurrentWindow))
            {
                case inSysWindow:               // It's a DA window, provide time for that.
                    this->DoSystemTime();
                    break;
 
                case inDrag:                    // Move the window to a specific target.
                    this->DoDragWindow();
                    break;
    
                case inGoAway:                  // Close the window.
                    this->DoGoAwayWindow();     // do window close events
                    // ¥¥¥ Future, Send a Close command
                    break;
 
                case inContent:                 // handle click inside the window
                    this->DoInWindowContent();
                    break;
 
                case inMenuBar:                 // handle menu bar clicks
                    this->DoMenuCommand();
                    break;
                                    
                default:                        // ignore any other sane events for the time being
                    break;
            }
 
        case app4Evt:                           // we got a Multifinder event
            switch ((unsigned long)fEventRecord.message >> TApplication::kHighByte)
            {
                case TApplication::kSuspendResumeMessage:// suspend or deactivate message
                    if ((fEventRecord.message & TApplication::kResumeMask) == 0)
                        fSleepValue = TApplication::kBackgroundSleepValue;
                    else                        // resume or activate message
                        fSleepValue = TApplication::kForgroundSleepValue;
                    break;
            }
            break;
 
        case kHighLevelEvent:
            this->DoHighLevelEvent();           // handle our high level events (as AEs)
            break;
 
        default:
            break;
    }
}
 
 
#pragma segment Application
void TGUIApplication::DoIdle()
// Handle idle time
{
}
 
 
#pragma segment Application
void TGUIApplication::DoSystemTime()
// Handle DAs
{
    ::SystemClick(&fEventRecord, fCurrentWindow);
}
 
 
#pragma segment Application
void TGUIApplication::DoDiskEvent()
// Handle disk events (formatting, floppies inserted and so on).
{
    Point dlogPoint;
    const short kDILeft = 40;
    const short kDITop = 40;
 
    if (HiWrd(fEventRecord.message) != noErr)   // if no error indication
    {
        ::SetPt(&dlogPoint, kDILeft, kDITop);       // define the dlog box rect
        fError = ::DIBadMount(dlogPoint, fEventRecord.message);// turn it over to the system
        VASSERT(fError == noErr, ("Problems with DIBadMount = %d ", fError));
    }
}
 
 
#pragma segment Application
void TGUIApplication::DispatchAppleEvents(AppleEvent* in, AppleEvent* out, long command)
// Take care of the GUI related Apple Events, and dispatch the rest to the TApplication level
{
    switch(command)
    {
        case cAboutCommand:     // Send an AE telling the system to display the About box
            this->DoAboutBox();
            break;
        default:
            TApplication::DispatchAppleEvents(in, out, command);
            break;
    }
}
 
 
#pragma segment Application
void TGUIApplication::DoWindowUpdate()
// Handle window update events.
{
    ::BeginUpdate((WindowPtr)fEventRecord.message);
    ::SetPort((WindowPtr)fEventRecord.message); // set port to the right grafport
    this->Draw();                                           // call the window drawing part
    ::EndUpdate((WindowPtr)fEventRecord.message);
}
 
 
 
#pragma segment Application
void TGUIApplication::DoDragWindow()
// Handle the inDrag event.
{
    ::DragWindow(fCurrentWindow, fEventRecord.where, &qd.screenBits.bounds);
}
 
 
#pragma segment Application
void TGUIApplication::DoGoAwayWindow()
// Handle dogoAway events.
{
    ::TrackGoAway(fCurrentWindow, fEventRecord.where);
}
 
 
#pragma segment Application
void TGUIApplication::DoInWindowContent()
// Handle inWindow events.
{
    if (fCurrentWindow != ::FrontWindow())
        ::SelectWindow(fCurrentWindow);         // make the window the foremost one
    else
        this->DoClick();                        // handle the click inside the foremost window
}
 
 
 
//  MENU EVENTS
#pragma segment Application
void TGUIApplication::DoMenuCommand()
// Handle menu mouse events.
{
    long aCommandNumber;
 
    this->AdjustUserInterface();                // first, make sure the menu entries are enabled/disabled
 
    fMenuResult = ::MenuSelect(fEventRecord.where);// get the selected menu (longword)  
 
    aCommandNumber = this->CalculateMenuCommand(fMenuResult);   // calculate our command number
 
    // We will handle the most typical menu entries here (Apple, Edit, File)
    // and call DoCommand() with the rest of the possible commands.
 
    this->DoInternalMenus(aCommandNumber);      // handle internal menus (Apple)
    this->DoCommand(aCommandNumber);            // handle external commands/menus
 
    ::HiliteMenu(0);                            // unhighlight what MenuSelect hilited and return
}
 
 
#pragma segment Application
long TGUIApplication::CalculateMenuCommand(long menuEntry)
// Calculate the command number based on the algorithm where the menu entry
// is either a two-digit or a three-digit number, and where the first number
// is the menu number.
{
    long command;
    if (LoWrd(menuEntry) > 9)                   // use algorithm 2
    {
        command = (HiWrd(menuEntry) * 100) + LoWrd(menuEntry);
    }
    else                                                // use algorithm 1
        command = (HiWrd(menuEntry) * 10) + LoWrd(menuEntry);
    
    return command;
}
 
 
#pragma segment Application
void TGUIApplication::DoInternalMenus(long command)
// Handle our internal menus, Apple, DAs and so on.
{
    Str255 daName;                              // our DA name
    short daRefNum;                             // our DA refnum 
 
    switch (command)
    {
        case cAboutCommand:                     // our About box?
            fMessenger.Send(kCoreEventClass, kAEAbout);
            break;
 
        default:
            if( HiWrd(fMenuResult) == mApple) // we have a DA?
            {
                ::GetMenuItemText(GetMenuHandle(mApple), (short)LoWrd(fMenuResult), daName);
                daRefNum = ::OpenDeskAcc(daName);
            }
            break;
    }
}
 
 
#pragma segment Application
void TGUIApplication::DoCommand(long command)
// Handle the default framework menus, post AE commands.
{
    switch(command)
    {
        case cQuitCommand:
            fMessenger.Send(kCoreEventClass, kAEQuitApplication);
            break;
        
        default:
            TApplication::DoCommand(command);                   // let the underlying framework take a short
            break;
    }
}
 
 
//  AE HANDLING OVERRIDES
#pragma segment Application
OSErr TGUIApplication::HandleOpen(AppleEvent*       /*in*/,
                               AppleEvent*          /*out*/,
                               long                 /*refCon*/)
{
    this->DoCreateDocument();                   // Default behavior -- open one window ('document').
    return noErr;                               // need to have this due to AEHandler prototype
}
 
 
//  DOCUMENT CREATION MEMBER FUNCTIONS
#pragma segment Application
void TGUIApplication::DoCreateDocument()
// Create initially one window.
{
    TWindow * aWindow = new TWindow;
    ASSERT(aWindow != NULL, "\pWe didn't create a TWindow");
    
    if(aWindow != NULL)
        this->AddDocument(aWindow);
}
 
 
#pragma segment Application
void TGUIApplication::AddDocument(TWindow* theDocument)
// Add the document (Window) to an internal list of documents.
{
    fDocument = theDocument;
}
 
 
#pragma segment Application
void TGUIApplication::DoCreateDocument(short windowID)
// Create initially one window.
{
    TWindow * aWindow = new TWindow(windowID);
    ASSERT(aWindow != NULL, "\pWe didn't create a TWindow");
 
    if(aWindow != NULL)
        this->AddDocument(aWindow);
}
 
 
#pragma segment Application
void TGUIApplication::Draw()
// Define drawing instructions inside this method. Default dispatch
// and draw the currently active document (window).
{
    fDocument->Draw();
}
 
 
#pragma segment Application
void TGUIApplication::DoClick()
// Handle a click inside a window, empty for the moment (need to override this one)
{
}
 
 
#pragma segment Application
void TGUIApplication::DoAboutBox()
// Present our default about box, override for a better one!
{
    ASSERT(false, "\pThis is the Gamura framework! Override for a better about box");
}
 
 
#pragma segment Application
void TGUIApplication::DoHelp()
// This is the hook for a possible help system (something outside the help balloons)
{
    ASSERT(false, "\pYou triggered the help system, override for a real one!");
}
 
 
// _________________________________________________________________________________________________________ //
// TQTApplication class member function implementations
 
//  CONSTRUCTORS & DESTRUCTORS
 
#pragma segment Application 
TQTApplication::TQTApplication()
// Initialize the QT specific parts.
{
}
 
 
#pragma segment Application 
TQTApplication::~TQTApplication()
// Do any possible epilogue cleanup.
{
}
 
 
// _________________________________________________________________________________________________________ //
// TGXApplication class member function implementations
 
//  CONSTRUCTORS & DESTRUCTORS
 
#pragma segment Application 
TGXApplication::TGXApplication()
// Initialize the GX specific parts
{
}
 
 
#pragma segment Application 
TGXApplication::~TGXApplication()
// Do any possible epilogue cleanup.
{
}
 
 
// _________________________________________________________________________________________________________ //
// TQTAndGXApplication class member function implementations
 
//  CONSTRUCTORS & DESTRUCTORS
 
#pragma segment Application 
TQTAndGXApplication::TQTAndGXApplication()
// Initialize the QT and GX specific parts.
{
}
 
 
#pragma segment Application 
TQTAndGXApplication::~TQTAndGXApplication()
// Do any possible epilogue cleanup.
{
}
 
 
// _________________________________________________________________________________________________________ //
 
 
/*  Change History (most recent last):
  No        Init.   Date        Comment
  1     khs 11/6/92 New file
  2     khs 1/14/93 Cleanup
*/