home *** CD-ROM | disk | FTP | other *** search
/ Developer CD Series 2000 January: Mac OS SDK / Dev.CD Jan 00 SDK1.toast / Development Kits / Mac OS / Apple Guide / Engineering / APISample / APISampleCW / Source / UAppMo.cp < prev    next >
Encoding:
Text File  |  1994-06-01  |  29.6 KB  |  1,090 lines  |  [TEXT/MPS ]

  1. // Copyright ©1994 Apple Computer, Inc.
  2. // Author: John Powers
  3. // Date:   13-Mar-94
  4.  
  5. // UAppMo.cp
  6. // The derived application class for MoGuide.
  7.  
  8. #ifndef __UAPPMO__
  9.     #include "UAppMo.h"
  10. #endif
  11.  
  12. #include <Dialogs.h>
  13.  
  14. extern Boolean gAGuideAvailable;
  15.  
  16.         // Segment
  17.  
  18. #pragma segment Main
  19.  
  20. // ------------------------------------------------------------------------
  21. // TAppMo::HandleAECustom
  22. // Handles the custom events.
  23. // The refCon contains our application object.
  24. // Should be in a locked, unpurgeable segment.
  25. //
  26. // Unfortunately, the DTS sample TApplication does not process
  27. // high level events.  We fix that by overriding the EventLoop
  28. // and adding high-level event processing.
  29. //
  30. pascal OSErr
  31. TAppMo::HandleAECustom(AppleEvent& theAppleEvent,
  32.                         AppleEvent& /*theReply*/, long refCon)
  33. {
  34.     OSType        eventId;
  35.     Size        actualSize;
  36.     DescType    returnedType;
  37.     OSErr        err=noErr;
  38.             // Useless without our application object.
  39.     TAppMo* ourApp=(TAppMo*)refCon;
  40.     if(!ourApp)
  41.         return kErrNoAppObject;
  42.             // Get a potential document collaboarator.
  43.     TDocArt* docArt = ourApp->GetDocArt();
  44.             // Get event id.
  45.     err = AEGetAttributePtr(&theAppleEvent, keyEventIDAttr,
  46.                                 typeType, &returnedType,
  47.                                 (Ptr) &eventId, sizeof(eventId),
  48.                                 &actualSize);
  49.     switch (eventId)
  50.     {
  51.         case kAEIdCloseDocArt:
  52.             ourApp->CloseDoc(ourApp->fDocArt);
  53.             break;
  54.         case kAEIdShowDocArt:
  55.             ourApp->ShowArt();
  56.             ourApp->DoAdvance();
  57.             break;
  58.         case kAEIdCloseDocFB:
  59.             ourApp->CloseDoc(ourApp->fDocFB);
  60.             break;
  61.         case kAEIdShowDocFB:
  62.             ourApp->ShowFeedback();
  63.             break;
  64.         case kAEIdReset:
  65.             if(docArt)
  66.                 docArt->SetWantReset();
  67.             break;
  68.         case kAEIdShuffle:
  69.             if(docArt)
  70.                 docArt->SetWantShuffle();
  71.             break;
  72.         case kAEIdQuitApp:
  73.             ourApp->ExitLoop();
  74.             break;
  75.         case kAEIdWaitCollision:
  76.                 // The context check will set the event.
  77.                 // If the event is constant, we could set it
  78.                 // right here and skip the context check.
  79.                 // Otherwise, we need the context check to
  80.                 // send us the event that the author desires.
  81.             if(docArt)
  82.                 docArt->SetWantCollision(true);
  83.             break;
  84.         case kAEIdIgnoreCollision:
  85.                 // Ignore any collision,
  86.                 // turn off flag and clear event.
  87.             if(docArt)
  88.             {
  89.                 docArt->SetWantCollision(false);
  90.                 docArt->SetCollisionEvent(0);
  91.             }
  92.             break;
  93.         case kAEIdAdvance:
  94.                 // Advance on the next open-window event.
  95.             ourApp->SetOkayAdvance(true);
  96.             break;
  97.         default:
  98.             err = errAEEventNotHandled;
  99.             break;
  100.     }
  101.     if(ourApp->fFBEvent)
  102.         ourApp->fFBEvent->SetFlash(eventId);
  103.     return err;
  104.     
  105. }
  106.  
  107. // --------------------------------------------------------------------
  108. // TAppMo::ReplyToCoach
  109. //
  110. // This is a callback invoked by the Coach Apple event handler.
  111. // This member function and the refCon
  112. // was provided by us in the Init call.
  113. //
  114. // Handles replies for Coach queries sent to OurApp.
  115. // Called with the item name and refCon.
  116. // Return the item Rect and a result code.
  117. //
  118. // The name is a zero-terminated string (C-string).
  119. // equalstring requires an A5 world.
  120. //
  121. // Caveats:
  122. //
  123. //        Our app resource map is not available.
  124. //        If you need a resource such as a string,
  125. //        read it in advance and save it in memory.
  126. //
  127. // A static function in main segment.
  128. pascal OSErr
  129. TAppMo::ReplyToCoach(Rect* pItemRect, Ptr pName, long refCon)
  130. {
  131.     OSErr    result=kAGErrItemNotFound;
  132.             // We probably need an A5 world.
  133.     long restoreGlobals = SetA5(*(long*)LMGetCurrentA5());
  134.             // Our application object is in the refcon.
  135.     TAppMo* ourApp=(TAppMo*) refCon;
  136.     if(ourApp)
  137.     {
  138.             // Ask our document window for the rect of the named item.
  139.         if(ourApp->fDocArt->GetLocation(pName, pItemRect))
  140.         {
  141.             GrafPtr    savePort;
  142.                 // Return the rectangle in global coordinates
  143.             GetPort(&savePort);
  144.                 // Get the GrafPort.
  145.             SetPort((GrafPtr) ourApp->fDocArt->GetDocWindow());
  146.                 // Top-left
  147.             LocalToGlobal((Point*)&(pItemRect->top));
  148.                 // Bottom-right
  149.             LocalToGlobal((Point*)&(pItemRect->bottom));
  150.             SetPort(savePort);
  151.             result = noErr;
  152.         }
  153.             // Feedback - auditory and visual
  154.         if(ourApp->fFBCoach)
  155.             ourApp->fFBCoach->SetFlash(pName);
  156.     } // if(ourApp…
  157.      SetA5(restoreGlobals);
  158.     return(result);
  159. }
  160.  
  161. // ------------------------------------------------------------------------
  162. // TContext::ReplyToContext
  163. // Handles replies for context checks sent to OurApp.
  164. // Called with the data in the context check database object
  165. // and the event you sent in the Init.
  166. // Return the result (true or false).
  167. // A callback.
  168. // A static function in main segment.
  169. pascal OSErr
  170. TContext::ReplyToContext(Ptr pInput, Size inputDataSize,
  171.                             Ptr *ppOutput, Size *pOutputDataSize,
  172.                             AGAppInfoHdl hAppInfo)
  173. {
  174.     OSErr        err=noErr;
  175.     long        inputValue=0;
  176.     short        result=false;
  177.             // We probably need an A5 world.
  178.     long restoreGlobals = SetA5(*(long*)LMGetCurrentA5());
  179.             // The input value can be used by either event id.
  180.     if(pInput && inputDataSize>0)
  181.     {
  182.         inputValue = *((long*)pInput);
  183.     }
  184.             // Our application object was passed by us in the refCon.
  185.             // It is returned to us in the refcon of hAppInfo.
  186.     TAppMo* ourApp = (TAppMo*) (**hAppInfo).refCon;
  187.             // Which event id?
  188.     switch ((**hAppInfo).eventId)
  189.     {
  190.                     // kAEIdContextCollision
  191.                     // Set the collision event to the inputValue.
  192.                     // When a collision occurs, the event will
  193.                     // be sent to Apple Guide.  In other words,
  194.                     // Apple Guide is saying "Let me know when
  195.                     // a collision occurs by sending me this event."
  196.                     // A zero input value cancels the event.
  197.                     // Always returns a false result.
  198.         case kAEIdContextCollision:
  199.             if(ourApp->fDocArt)
  200.                 if(ourApp->fDocArt->WantCollision())
  201.                     ourApp->fDocArt->SetCollisionEvent(inputValue);
  202.             result = false;
  203.             break;
  204.                     // kAEIdContextBeep
  205.                     // Return the value (true or false) saved in fBeepReturn.
  206.         case kAEIdContextBeep:
  207.             result = ourApp->fBeepReturn;
  208.             break;
  209.                     // kAEIdContextWindow
  210.                     // Return true if a document window is open and showing.
  211.         case kAEIdContextWindow:
  212.             result = (ourApp->fDocArt)?ourApp->fDocArt->IsWindowVisible():false;
  213.             break;
  214.         default:
  215.                     // An unknown event will be ignored.
  216.             break;
  217.     }
  218.         // Create storage for our result.
  219.     *ppOutput = NewPtr(sizeof(short));
  220.     if(*ppOutput)
  221.     {
  222.             // Set the result and its size.
  223.         **ppOutput = result;
  224.         *pOutputDataSize = sizeof(short);
  225.     }
  226.         // Feedback - auditory and visual
  227.     if(ourApp->fFBContext)
  228.         ourApp->fFBContext->SetFlash((**hAppInfo).eventId);
  229.  
  230.      SetA5(restoreGlobals);
  231.     return err;
  232. }
  233.  
  234.         // Segment
  235.  
  236. #pragma segment MoG1
  237.  
  238. // =========================================================================
  239. // TAppMo
  240. // ------------------------------------------------------------------------
  241. TAppMo::TAppMo()
  242. {
  243. };
  244.  
  245. // ------------------------------------------------------------------------
  246. // TAppMo::AddGuideFiles
  247. // Add the guide files to the mMoGuide menu.
  248. //
  249. // If there are any files, a dashed line is added first,
  250. // followed by the menu name for each file.  We save
  251. // the FSSpec's for easy retrieval when the user chooses
  252. // the menu item.  If any guide files already exist on
  253. // the menu, they are deleted first and a fresh list
  254. // is prepared.
  255. //
  256. void
  257. TAppMo::AddGuideFiles()
  258. {
  259.     short        newFileCount;
  260.     Str63        menuName;
  261.     FSSpec        fileSpec;
  262.     MenuHandle    hmMoGuide = GetMHandle(mMoGuide);
  263.     if(hmMoGuide)
  264.     {
  265.             // If we already have a file list, delete it.
  266.         if(this->fhFileList)
  267.         {
  268.                 // We do, dispose of it and its menu items.
  269.             DisposeHandle(this->fhFileList);
  270.             while(this->fFileCount--)
  271.             {
  272.                     // Delete the item after the dash line.
  273.                 DelMenuItem(hmMoGuide, iLastMenuItem+2);
  274.             }
  275.                 // Delete dashed line.
  276.             DelMenuItem(hmMoGuide, iLastMenuItem+1);
  277.         }
  278.             // Set the search directory for guide files.
  279.         short    vRefNum = (-*(short*)LMGetSFSaveDisk());
  280.         long    dirID = (*(long*)LMGetCurDirStore());
  281.         Boolean    wantMixin = false;
  282.         newFileCount = AGFileGetDBCount(vRefNum, dirID,
  283.                                         kAGFileDBTypeAny, wantMixin);
  284.                                     
  285.         if(newFileCount>0)
  286.         {
  287.                 // Create a new list of file FSSpecs.
  288.             this->fhFileList = NewHandle(newFileCount*sizeof(FSSpec));
  289.             if(this->fhFileList)
  290.             {
  291.                     // Add dashed line.
  292.                 AppendMenu(hmMoGuide, (const unsigned char*)"\p-");
  293.                     // Get each file, add to menu and to list.
  294.                 this->fFileCount = newFileCount;
  295.                 for(short i=1; i<=newFileCount; i++)
  296.                 {
  297.                         // Use same vRefNum, dirID, and wantMixin
  298.                         // that was set for the count.
  299.                     if(AGFileGetIndDB(vRefNum, dirID,
  300.                                     kAGFileDBTypeAny, wantMixin,
  301.                                     i, &fileSpec)==noErr)
  302.                     {
  303.                         if(AGFileGetDBMenuName(&fileSpec, menuName)==noErr)
  304.                         {
  305.                                 // We have a guide file,
  306.                                 // get it's menu name and add to menu.
  307.                             AppendMenu(hmMoGuide, menuName);
  308.                             ((FSSpec*)(*this->fhFileList))[i-1] = fileSpec;
  309.                         }
  310.                     }
  311.                 } // for(short…
  312.             } // if(hFileMenu…
  313.         } // if(newFileCount…
  314.     } // if(hmMoGuide…
  315. }
  316.  
  317. // --------------------------------------------------------------------------
  318. // TAppMo::AdjustMenus
  319. //
  320. void
  321. TAppMo::AdjustMenus()            // override
  322. {
  323.             // Inherit first.
  324.     TApp::AdjustMenus();
  325.             // Our menus.
  326.     MenuHandle    hmMoGuide=GetMHandle(mMoGuide);
  327.     MenuHandle    hmWindow=GetMHandle(mWindow);
  328.             // Always enabled
  329.     EnableItem(hmMoGuide, iFiles);
  330.             // Set default case.
  331.     DisableItem(hmMoGuide, iStartGuide);
  332.     DisableItem(hmMoGuide, iQuitGuide);
  333.     DisableItem(hmMoGuide, iOpenDefault);
  334.     DisableItem(hmMoGuide, iClose);
  335.             // If Apple Guide is installed, then we can do lots of things.
  336.     if(gAGuideAvailable)
  337.     {
  338.         AGStatus status = AGGetStatus();
  339.                 // Permit starting it if it's not already started.
  340.         if(status==kAGIsNotRunning)
  341.             EnableItem(hmMoGuide, iStartGuide);
  342.                 // If Apple Guide is sleeping, then we can make it quit.
  343.         else if(status==kAGIsSleeping)
  344.             EnableItem(hmMoGuide, iQuitGuide);
  345.                 // If we have default help available, then we can open it.
  346.         if(AGGetAvailableDBTypes() & kAGDBTypeBitHelp)
  347.             EnableItem(hmMoGuide, iOpenDefault);
  348.                 // If we have a non-nil this->fGuideRefNum,
  349.                 // then a database is open. Permit closing.
  350.                 // this->fGuideRefNum is tracked in TApp::AdjustMenus.
  351.         if(this->fGuideRefNum)
  352.             EnableItem(hmMoGuide, iClose);
  353.                 // Enable any databases that we may have added.
  354.         for(short i=0; i<this->fFileCount; i++)
  355.             EnableItem(hmMoGuide, iLastMenuItem+i+2);
  356.     }
  357.             // Always available.
  358.             // Art-related items.
  359.     EnableItem(hmWindow, iWinArt);
  360.         // Can reset objects if a window is visible.
  361.     if(this->fDocArt)
  362.     {
  363.         EnableItem(hmWindow, iResetArt);
  364.         EnableItem(hmWindow, iShuffleArt);
  365.     }
  366.     else
  367.     {
  368.         DisableItem(hmWindow, iResetArt);
  369.         DisableItem(hmWindow, iShuffleArt);
  370.     }
  371.             // Always available.
  372.     EnableItem(hmWindow, iFeedback);
  373.     EnableItem(hmWindow, iContextTrue);
  374.     CheckItem(hmWindow, iContextTrue, this->fBeepReturn);
  375.     EnableItem(hmWindow, iContextBeep);
  376.     if(this->fFBContext)
  377.         CheckItem(hmWindow, iContextBeep, this->fFBContext->GetBeep());
  378.  
  379. }
  380.  
  381. // ------------------------------------------------------------------------
  382. // TAppMo::CheckFrontWindow
  383. // Check to see if Apple Guide has a window showing.
  384. // Update the miscellaneous feedback with the result.
  385. // We look for the front window of the current database
  386. // which is not necessarily our own. If we wanted just
  387. // our own database, we would pass this->fGuideRefNum
  388. // instead of kAGFrontDatabase.
  389. //
  390. void
  391. TAppMo::CheckFrontWindow()
  392. {
  393.     Str255 string;
  394.     short index=kStrNone;
  395.     if(gAGuideAvailable)
  396.     {
  397.             // If Apple Guide is available, then the choices
  398.             // are nothing (Apple Guide is not running),
  399.             // sleeping, access window, or presentation window.
  400.         if(AGGetStatus()!=kAGIsNotRunning)
  401.         {
  402.             AGWindowKind kind = AGGetFrontWindowKind(kAGFrontDatabase);
  403.                 // Clarity, not conciseness.
  404.             if(kind==kAGNoWindow)
  405.                 index = kStrNoneSleeping;
  406.             else if(kind==kAGAccessWindow)
  407.                 index = kStrAccess;
  408.             else if(kind==kAGPresentationWindow)
  409.                 index = kStrPresentation;
  410.         }
  411.     }
  412.         // Update FBMisc if there is a change.
  413.         // It flashes every time we update it.
  414.         // We force an update every kMaxStatusUpdateCnt cycles.
  415.     this->fLastStatusCnt--;
  416.     if(this->fLastStatusCnt==0 || index!=this->fLastMiscIndex)
  417.     {
  418.         GetIndString(string, kFrontWindowStrId, index);
  419.             // If we are in the idle loop, we must check this->fFBMisc
  420.             // validity in case it hasn't been created yet.
  421.         if(this->fFBMisc)
  422.         {
  423.             this->fFBMisc->SetFlash(string);
  424.             this->fLastMiscIndex = index;
  425.         }
  426.         this->fLastStatusCnt = kMaxStatusUpdateCnt;
  427.     }
  428. }
  429.  
  430. // ------------------------------------------------------------------------
  431. // TAppMo::DoAdvance
  432. // If an advance is wanted, do it.
  433. // This is a one-shot event.
  434. // We send a go-back event because we assume that Apple Guide
  435. // is showing an oops panel.
  436. void
  437. TAppMo::DoAdvance()
  438. {
  439.     if(gAGuideAvailable && this->fOkayAdvance)
  440.     {
  441.         AlertIfError(AGGeneral(this->fGuideRefNum, kAGEventReturnBack));
  442.     }
  443.     this->fOkayAdvance = false;
  444. }
  445.  
  446. // ------------------------------------------------------------------------
  447. // TAppMo::DoGoAway
  448. //
  449. // This is the close side of TAppMo::ShowArt, ShowClipboard, ShowFeedback.
  450. //
  451. void
  452. TAppMo::DoGoAway()
  453. {
  454.             // Clear our local document record if
  455.             // it's object is going away.
  456.     if(this->fCurDoc==this->fDocFB)
  457.     {
  458.         this->fDocFB = nil;
  459.         this->fFBContext->SetDoc(nil);
  460.         this->fFBCoach->SetDoc(nil);
  461.         this->fFBEvent->SetDoc(nil);
  462.         this->fFBMisc->SetDoc(nil);
  463.     }
  464.     else if(this->fCurDoc==this->fDocArt)
  465.         this->fDocArt = nil;
  466.             // Inherit go-away action from TApp and TApplication
  467.     TApp::DoGoAway();
  468. }
  469.  
  470. // ------------------------------------------------------------------------
  471. // TAppMo::DoIdle
  472. // Do our idle and deferred events.
  473. //
  474. void
  475. TAppMo::DoIdle()        // override
  476. {
  477.         // Inherit to do TApp Idle.
  478.     TApp::DoIdle();
  479.         // Document actions.
  480.     if(this->fDocArt)
  481.         this->fDocArt->DoIdle();
  482.         // Feedback about context-checking.
  483.     if(this->fFBContext)
  484.         this->fFBContext->DoIdle();
  485.         // Feedback about Coach objects.
  486.     if(this->fFBCoach)
  487.         this->fFBCoach->DoIdle();
  488.         // Feedback about Apple events.
  489.     if(this->fFBEvent)
  490.         this->fFBEvent->DoIdle();
  491.         // Check for Apple Guide front window.
  492.     this->CheckFrontWindow();
  493.         // Miscellaneous feedback.
  494.     if(this->fFBMisc)
  495.         this->fFBMisc->DoIdle();
  496. }
  497.  
  498. // ------------------------------------------------------------------------
  499. // TAppMo::DoMenuCommand
  500. // This is called when an item is chosen from the menu bar (after calling
  501. // MenuSelect or MenuKey). It does the right thing for each command.
  502. //
  503. // TAppMo::AdjustMenus enables the menu items
  504. // depending on whether or not guide is available.
  505. // Consequently, we don't have to check here to see
  506. // if guide is available because, if it isn't, the item
  507. // won't be choosen.
  508. //
  509. void
  510. TAppMo::DoMenuCommand(short menuID, short menuItem)        //  override
  511. {
  512.     FSSpec    fileSpec;
  513.     OSErr    err=noErr;
  514.     switch (menuID)
  515.     {
  516.         case mMoGuide:
  517.             switch(menuItem)
  518.             {
  519.                 case iStartGuide:
  520.                     AlertIfError(AGStart());
  521.                     break;
  522.                 case iQuitGuide:
  523.                     AlertIfError(AGQuit());
  524.                     break;
  525.                 case iOpenDefault:
  526.                     AlertIfError(this->OpenGuideDatabase((FSSpec*)kAGDefault));
  527.                     break;
  528.                 case iClose:
  529.                     err = AGClose(&this->fGuideRefNum);
  530.                     if(err==kAGErrDatabaseNotOpen)
  531.                         this->fGuideRefNum = nil;
  532.                     break;
  533.                 case iFiles:
  534.                     this->AddGuideFiles();
  535.                     break;
  536.                 default:
  537.                         // Must be one of the guide file items that we added.
  538.                         // Get its FSSpec from the list and open it.
  539.                     if(this->fhFileList)
  540.                     {
  541.                         fileSpec = ((FSSpec*)(*this->fhFileList))[menuItem-iLastMenuItem-2];
  542.                         AlertIfError(this->OpenGuideDatabase(&fileSpec));
  543.                     }
  544.                     break;
  545.             } // switch
  546.             break;
  547.         case mWindow:
  548.             switch(menuItem)
  549.             {
  550.                 case iWinArt:
  551.                     err = this->SendEventToSelf(kAEIdShowDocArt);
  552.                     break;
  553.                 case iFeedback:
  554.                     err = this->SendEventToSelf(kAEIdShowDocFB);
  555.                     break;
  556.                 case iContextTrue:
  557.                     this->fBeepReturn = !this->fBeepReturn;
  558.                     break;
  559.                 case iContextBeep:
  560.                     if(this->fFBContext)
  561.                         this->fFBContext->SetBeep(!this->fFBContext->GetBeep());
  562.                     break;
  563.                 case iResetArt:
  564.                     err = this->SendEventToSelf(kAEIdReset);
  565.                     break;
  566.                 case iShuffleArt:
  567.                     err = this->SendEventToSelf(kAEIdShuffle);
  568.                     break;
  569.                 default:
  570.                     break;
  571.             } // switch
  572.             break;
  573.         default:
  574.             break;
  575.     } // switch
  576.             // Inherit for the Apple, File, and Edit menus.
  577.     TApp::DoMenuCommand(menuID, menuItem);
  578.     AlertIfError(err);
  579. } // DoMenuCommand
  580.  
  581. // ---------------------------------------------------------------------
  582. // TAppMo::Init
  583. // Do the things that our derived application class requires.
  584. // Basic initialization, before we start.
  585. // We store our application object in the core event handler
  586. // refCon so that the handler can use it.  Avoids making it a global.
  587. // Return noErr if successful.
  588. OSErr
  589. TAppMo::Init()
  590. {
  591.             // Inherit
  592.     OSErr err = TApp::Init();
  593.     if(err==noErr)
  594.     {
  595.             // Override the menubar ID.
  596.         this->fMenuBarID = rMenuBarMo;
  597.             // Clear our collaborators because we'll not want to use
  598.             // them until after we get the core startup event.
  599.                 // Clear handler refNums.
  600.         this->fCoachRefNum = nil;
  601.         this->fContext = nil;
  602.         this->fFBContext = nil;
  603.         this->fFBCoach = nil;
  604.         this->fFBEvent = nil;
  605.         this->fFBMisc = nil;
  606.         this->fDocArt = nil;
  607.         this->fDocFB = nil;
  608.             // Also clear our variables or we may get a bus error
  609.             // at the first DoIdle (before we get the start event.)
  610.         this->fhFileList = nil;
  611.         this->fFileCount = 0;
  612.         this->fBeepReturn = false;
  613.         this->fOkayAdvance = false;
  614.         this->fLastMiscIndex = 0;
  615.             // Remove the core event handler installed in TApp.
  616.         (void) AERemoveEventHandler(kCoreEventClass,
  617.                                 typeWildCard,
  618.                                 kHandlerNotRequired,
  619.                                 kIsNotSysHandler);
  620.             // Replace with a core event handler which
  621.             // uses the same handler, but passes TAppMo
  622.             // in the refCon instead of TApp.  This
  623.             // insures that TAppMo::Start is invoked.
  624.         err = AEInstallEventHandler(kCoreEventClass,
  625.                                 typeWildCard,
  626.                             #ifdef __powerc
  627.                                 NewAEEventHandlerProc(TApp::HandleAECore),
  628.                             #else
  629.                                 TApp::HandleAECore,
  630.                             #endif
  631.                                 (long)this,
  632.                                 kIsNotSysHandler);
  633.             // Install our custom event handler.
  634.             // We put the TAppMo object in the refCon.
  635.         err = AEInstallEventHandler(kAEClassCustom,
  636.                                 typeWildCard,
  637.                             #ifdef __powerc
  638.                                 NewAEEventHandlerProc(TAppMo::HandleAECustom),
  639.                             #else
  640.                                 TAppMo::HandleAECustom,
  641.                             #endif
  642.                                 (long)this,
  643.                                 kIsNotSysHandler);
  644.             // Initialize our status update cycle counter.
  645.         this->fLastStatusCnt = kMaxStatusUpdateCnt;
  646.             // Starting is done in TApp::HandleAECore.
  647.             // We wait until that happens before proceeding.
  648.     }
  649.     return err;
  650. }
  651.  
  652. // ------------------------------------------------------------------------
  653. // TAppMo::Quit
  654. // We're quitting, so delete everything.
  655. // All of this probably goes away in the application heap anyway,
  656. // we're just being compulsively tidy.
  657. //
  658. void
  659. TAppMo::Quit()
  660. {
  661.     if(gAGuideAvailable)
  662.     {
  663.                 // Remove Coach and context handlers.
  664.         AlertIfError(AGRemoveCoachHandler(&this->fCoachRefNum));
  665.     }
  666.             // Delete our context object.
  667.     if(this->fContext)
  668.     {
  669.         AlertIfError(this->fContext->Remove());
  670.         delete this->fContext;
  671.     }
  672.             // Delete our flasher objects.
  673.     if(this->fFBContext)
  674.         delete this->fFBContext;
  675.     if(this->fFBCoach)
  676.         delete this->fFBCoach;
  677.     if(this->fFBEvent)
  678.         delete this->fFBEvent;
  679.     if(this->fFBMisc)
  680.         delete this->fFBMisc;
  681.             // Remove list of guide files.
  682.     if(this->fhFileList)
  683.         DisposeHandle(this->fhFileList);
  684.             // Remove custom event handler.
  685.     (void) AERemoveEventHandler(kAEClassCustom,
  686.                                 typeWildCard,
  687.                                 kHandlerNotRequired,
  688.                                 kIsNotSysHandler);
  689.             // Inherit.
  690.     TApp::Quit();
  691. }
  692.  
  693. // ------------------------------------------------------------------------
  694. // TAppMo::ShowArt
  695. // Opens the art window.
  696. // The document window is closed by the go-away box (TAppMo::DoGoAway).
  697. //
  698. // The art is specified in a resource.
  699. // The art resource id matches the id of its window.
  700. // To add another window, specify a new resource id,
  701. // define the new art resource, and instantiate a new TDocArt.
  702. //
  703. void
  704. TAppMo::ShowArt()
  705. {
  706.     if(this->fDocArt==nil)
  707.     {
  708.             // Art window is not present.
  709.             // Create a document and window for the art.
  710.         this->fDocArt = new TDocArt(kArt1WindResID);
  711.         if(this->fDocArt)
  712.         {
  713.                 // Add new document to document list.
  714.             this->fDocList->AddDoc((TDocument*) this->fDocArt);
  715.                 // Update current document and window pointer.
  716.             this->fCurDoc = this->fDocArt;
  717.             this->fWhichWindow = this->fCurDoc->GetDocWindow();
  718.             SetPort(this->fWhichWindow);
  719.                 // Initialize art document window.
  720.             AlertIfError(this->fDocArt->Init(kArt1WindResID));
  721.                 // Set the application object as a collaborator.
  722.             this->fDocArt->SetApp(this);
  723.         }
  724.     }
  725.         // Better show it, or at least bring it to the front.
  726.     this->fDocArt->Show();
  727.             // Invalidate window so that it will be updated and drawn.
  728.     this->fDocArt->Invalidate();
  729. }
  730.  
  731. // ------------------------------------------------------------------------
  732. // TAppMo::ShowFeedback
  733. // Opens the feedback window.
  734. // The document window is closed by the go-away box (TAppMo::DoGoAway).
  735. //
  736. void
  737. TAppMo::ShowFeedback()
  738. {
  739.     if(this->fDocFB==nil)
  740.     {
  741.             // Feedback window is not present.
  742.             // Create a document and window for the feedback.
  743.         this->fDocFB = new TDocFB(kFeedbackWinResID);
  744.         if(this->fDocFB)
  745.         {
  746.                 // Add new document to document list.
  747.             this->fDocList->AddDoc((TDocument*) this->fDocFB);
  748.                 // Update current document and window pointer.
  749.             this->fCurDoc = this->fDocFB;
  750.             this->fWhichWindow = this->fCurDoc->GetDocWindow();
  751.             SetPort(this->fWhichWindow);
  752.                     // Initialize feedback document window.
  753.             AlertIfError(this->fDocFB->Init());
  754.                 // Set the application object as a collaborator.
  755.             this->fDocFB->SetApp(this);
  756.                 // Set document window collaborator for feedback.
  757.             this->fFBContext->SetDoc(this->fDocFB);
  758.             this->fFBCoach->SetDoc(this->fDocFB);
  759.             this->fFBEvent->SetDoc(this->fDocFB);
  760.             this->fFBMisc->SetDoc(this->fDocFB);
  761.         }
  762.     }
  763.         // Better show it, or at least bring it to the front.
  764.     this->fDocFB->Show();
  765.             // Invalidate window so that it will be updated and drawn.
  766.     this->fDocFB->Invalidate();
  767. }
  768.  
  769. // ---------------------------------------------------------------------
  770. // TAppMo::Start
  771. // Startup the application.
  772. // The Init is done in main.  The Start comes with the oapp or odoc event.
  773. // Return noErr if successful.
  774. OSErr
  775. TAppMo::Start()
  776. {
  777.             // Inherit.
  778.     OSErr err = TApp::Start();
  779.     {
  780.                 // Reply to Coach mark queries.
  781.                 // Pass along the TApp object.
  782.                 // Setup our callbacks for context-check queries.
  783.                 // Pass along the TApp object in the refCon.
  784.         if(gAGuideAvailable)
  785.         {
  786.             err = AGInstallCoachHandler(NewCoachReplyProc(TAppMo::ReplyToCoach),
  787.                                         (long)this, &this->fCoachRefNum);
  788.             if(err!=noErr)
  789.                 return err;
  790.             this->fContext = new TContext;
  791.             if(this->fContext)
  792.                 err = this->fContext->Install((long)this);
  793.             else
  794.                 err = kErrNoContextObject;
  795.             if(err!=noErr)
  796.                 return err;
  797.         }
  798.                 // List of guide files.
  799.         this->fhFileList = nil;
  800.         this->fFileCount = 0;
  801.                 // Don't want a panel advance yet.
  802.         this->fOkayAdvance = false;
  803.                 // Return a true or false to the beep context check query?
  804.         this->fBeepReturn = false;
  805.                 // Feedback (the Flasher).
  806.         this->fFBContext = new TFeedback;
  807.         if(this->fFBContext)
  808.             this->fFBContext->Init(this, dataCC);
  809.         this->fFBCoach = new TFeedback;
  810.         if(this->fFBCoach)
  811.             this->fFBCoach->Init(this, dataCH);
  812.         this->fFBEvent = new TFeedback;
  813.         if(this->fFBEvent)
  814.             this->fFBEvent->Init(this, dataEV);
  815.         this->fFBMisc = new TFeedback;
  816.         if(this->fFBMisc)
  817.         {
  818.             this->fFBMisc->Init(this, dataTP);
  819.                 // Set an infinite interval for FBMisc.
  820.             this->fFBMisc->SetInterval(0);
  821.         }
  822.                 // Show the feedback window/document.
  823.         err = this->SendEventToSelf(kAEIdShowDocFB);
  824.     }
  825.     return err;
  826. }
  827.  
  828. // =========================================================================
  829. // TContext
  830. // ------------------------------------------------------------------------
  831. TContext::TContext()
  832. {
  833.     this->fHandlerCnt = 0;
  834. }
  835.  
  836. // ------------------------------------------------------------------------
  837. TContext::~TContext()
  838. {
  839.         // If we still have handlers installed, remove them.
  840.     if(this->fHandlerCnt>0)
  841.         (void) this->Remove();
  842. }
  843.  
  844. // ------------------------------------------------------------------------
  845. // TContext::Install
  846. // Install context-check handlers.
  847. // The appObj is the application object
  848. // used in the reply to access the application.
  849. // If any error occurs, return it.
  850. //
  851. OSErr
  852. TContext::Install(long appObj)
  853. {
  854.     OSErr result=noErr;
  855.     OSErr eachResult;
  856.         // Define and install context-check handlers.
  857.         // If we still have handlers installed, remove them.
  858.     if(this->fHandlerCnt>0)
  859.         result = this->Remove();
  860.     this->fHandlerCnt = kContextHandlerCnt;
  861.     this->fContext[0].eventID = kAEIdContextCollision;
  862.     this->fContext[1].eventID = kAEIdContextBeep;
  863.     this->fContext[2].eventID = kAEIdContextWindow;
  864.     for(short i=0; i<this->fHandlerCnt; i++)
  865.     {
  866.             // Install handler.
  867.             // If an error, the refNum will be set to nil by Apple Guide.
  868.         eachResult = AGInstallContextHandler(
  869.                                         NewContextReplyProc(TContext::ReplyToContext),
  870.                                         this->fContext[i].eventID,
  871.                                         appObj,
  872.                                         &this->fContext[i].refNum);
  873.             // Track only errors in result.
  874.         result = (eachResult!=noErr)?eachResult:result;
  875.     }
  876.     return result;
  877. }
  878.  
  879. // ------------------------------------------------------------------------
  880. // TContext::Remove
  881. // Remove context-check handlers.
  882. // If any error occurs, return it.
  883. //
  884. OSErr
  885. TContext::Remove()
  886. {
  887.     OSErr result=noErr;
  888.     OSErr eachResult;
  889.     for(short i=0; i<this->fHandlerCnt; i++)
  890.     {
  891.         eachResult = AGRemoveContextHandler(&this->fContext[i].refNum);
  892.             // Track only errors in result.
  893.         result = (eachResult!=noErr)?eachResult:result;
  894.     }
  895.     this->fHandlerCnt = 0;
  896.     return result;
  897. }
  898.  
  899. // =========================================================================
  900. // TTypeStr
  901. // ------------------------------------------------------------------------
  902. TTypeStr::TTypeStr(OSType theType)
  903. {
  904.     TypeToChar toConvert;
  905.     toConvert.type = theType;
  906.             // Convert stored OSType to pstring.
  907.             // Replace zeros with spaces.
  908.     this->fString[0] = 4;
  909.     for(short i=0; i<4; i++)
  910.         this->fString[i+1] = (toConvert.ch[i])?toConvert.ch[i]:' ';
  911. }
  912.  
  913. // ------------------------------------------------------------------------
  914. // TTypeStr::String
  915. StringPtr
  916. TTypeStr::String()
  917. {
  918.     return &this->fString[0];
  919. }
  920.  
  921. // =========================================================================
  922. // TFeedback
  923. // ------------------------------------------------------------------------
  924. TFeedback::TFeedback()
  925. {
  926.     this->fShowTime = 0;
  927.     this->fInterval = kIntervalShow;
  928.     this->fCurrent = 0;
  929.     this->fPrevious = 0;
  930.     this->fShow = false;
  931.     this->fBeep = false;
  932.     this->fOurApp = nil;
  933.     this->fDocFB = nil;
  934. }
  935.  
  936. // ------------------------------------------------------------------------
  937. TFeedback::~TFeedback()
  938. {
  939. }
  940.  
  941. // ------------------------------------------------------------------------
  942. // TFeedback::DoIdle
  943. // Do any action required during the idle processing.
  944. //
  945. void
  946. TFeedback::DoIdle()
  947. {
  948.     this->Show();
  949. }
  950.  
  951. // ------------------------------------------------------------------------
  952. // TFeedback::Draw
  953. void
  954. TFeedback::Draw(OSType toDraw, short mode)
  955. {
  956.     TTypeStr flashChar = toDraw;
  957.             // Draw it in the feedback window (if present).
  958.     if(this->fDocFB)
  959.         this->fDocFB->DrawData(flashChar.String(), mode, this->fWhichData);
  960. }
  961.  
  962. // ------------------------------------------------------------------------
  963. // TFeedback::GetBeep
  964. // Return the want-beep flag.
  965. Boolean
  966. TFeedback::GetBeep()
  967. {
  968.     return this->fBeep;
  969. }
  970.  
  971. // ------------------------------------------------------------------------
  972. // TFeedback::Init
  973. // Set the application collaborator (and any others you might want to add.)
  974. void
  975. TFeedback::Init(TAppMo* theApp, short whichData)
  976. {
  977.         // The collaborator
  978.     this->fOurApp = theApp;
  979.         // The data
  980.     this->fWhichData = whichData;
  981. }
  982.  
  983. // ------------------------------------------------------------------------
  984. // TFeedback::SetBeep
  985. // Set the want-beep flag.
  986. void
  987. TFeedback::SetBeep(Boolean wantBeep)
  988. {
  989.     this->fBeep = wantBeep;
  990. }
  991.  
  992. // ------------------------------------------------------------------------
  993. // TFeedback::SetFlash
  994. void
  995. TFeedback::SetFlash(OSType theFlash)
  996. {
  997.     this->fCurrent = theFlash;
  998.     this->fShow = true;
  999. }
  1000.  
  1001. // ------------------------------------------------------------------------
  1002. // TFeedback::SetFlash
  1003. // Accept a char ptr, convert its first four characters to an OSType.
  1004. void
  1005. TFeedback::SetFlash(Ptr theFlash)
  1006. {
  1007.     CharToType    flashChar;
  1008.     flashChar.type = '    ';
  1009.     for(short i=0; i<4; i++)
  1010.         if(theFlash[i]!=0)
  1011.             flashChar.ch[i] = theFlash[i];
  1012.     this->fCurrent = flashChar.type;
  1013.     this->fShow = true;
  1014. }
  1015.  
  1016. // ------------------------------------------------------------------------
  1017. // TFeedback::SetFlash
  1018. // Accept a pascal string, convert its first four characters to an OSType.
  1019. void
  1020. TFeedback::SetFlash(Str255 theFlash)
  1021. {
  1022.     short len = (theFlash[0]>4)?4:theFlash[0];
  1023.     CharToType    flashChar;
  1024.     flashChar.type = '    ';
  1025.     for(short i=0; i<len; i++)
  1026.         flashChar.ch[i] = theFlash[i+1];
  1027.     this->fCurrent = flashChar.type;
  1028.     this->fShow = true;
  1029. }
  1030.  
  1031. // ------------------------------------------------------------------------
  1032. // TFeedback::Show
  1033. // Show anything new.
  1034. // When interval is up, erase.
  1035. //
  1036. void
  1037. TFeedback::Show()
  1038. {
  1039.     short drawThisCycle=true;
  1040.     if(this->fShow)
  1041.     {
  1042.         if(this->fPrevious)
  1043.         {
  1044.                 // Something is up there, erase it.
  1045.             this->Draw(this->fPrevious, kErase);
  1046.                 // Is this a repeat of the same item?
  1047.             if(this->fPrevious==this->fCurrent)
  1048.             {
  1049.                     // Same item, don't draw this cycle.
  1050.                     // Delay one cycle to make it "flash".
  1051.                     // Unless the sysbeep is on, that will
  1052.                     // give enough delay to make it flash.
  1053.                 drawThisCycle = this->fBeep;
  1054.             }
  1055.                 // Clear our previous item, it was erased.
  1056.             this->fPrevious = 0;
  1057.         }
  1058.             // Auditory feedback and delay between erase and draw.
  1059.         if(this->fBeep)
  1060.         {
  1061.             SysBeep(1);
  1062.         }
  1063.             // Show.
  1064.         if(drawThisCycle)
  1065.         {
  1066.             this->fShow = false;
  1067.             this->fShowTime = TickCount();
  1068.             this->Draw(this->fCurrent, kPaint);
  1069.             this->fPrevious = this->fCurrent;
  1070.         }
  1071.     }
  1072.     else
  1073.     {
  1074.             // Nothing new to show.
  1075.             // Only erase previous if we have a positive interval.
  1076.         if(this->fPrevious && this->fInterval)
  1077.         {
  1078.             if(TickCount()>this->fShowTime+this->fInterval)
  1079.             {
  1080.                 // Counter timed out and no new stuff.
  1081.                 // Erase old stuff.
  1082.                     // Something is up there, erase it.
  1083.                 this->Draw(this->fPrevious, kErase);
  1084.                 this->fPrevious = 0;
  1085.             }
  1086.         }
  1087.     }
  1088. }
  1089.     
  1090.