home *** CD-ROM | disk | FTP | other *** search
/ AppleScript - The Beta Release / AppleScript - The Beta Release.iso / Documentation / develop / Better Apple Event Coding / Code Samples / Application.cp < prev    next >
Encoding:
Text File  |  1992-10-16  |  13.7 KB  |  613 lines  |  [TEXT/MPS ]

  1. /*------------------------------------------------------------------------------------------
  2.  
  3.     Program:    CPlusTESample 2.0AE
  4.     File:        Application.cp
  5.     Uses:       Application.h
  6.                 Document.h
  7.  
  8.     by Andrew Shebanow
  9.     of Apple Macintosh Developer Technical Support
  10.     with modifications by Eric Berdahl
  11.  
  12.     Copyright © 1989-1990 Apple Computer, Inc.
  13.     Copyright © 1992 Eric Berdahl
  14.     All rights reserved.
  15.  
  16. ------------------------------------------------------------------------------------------*/
  17.  
  18. // Mac Includes
  19. #ifndef __TYPES__
  20. #include <Types.h>
  21. #endif
  22. #ifndef __QUICKDRAW__
  23. #include <QuickDraw.h>
  24. #endif
  25. #ifndef __FONTS__
  26. #include <Fonts.h>
  27. #endif
  28. #ifndef __EVENTS__
  29. #include <Events.h>
  30. #endif
  31. #ifndef __CONTROLS__
  32. #include <Controls.h>
  33. #endif
  34. #ifndef __WINDOWS__
  35. #include <Windows.h>
  36. #endif
  37. #ifndef __RESOURCES__
  38. #include <Resources.h>
  39. #endif
  40. #ifndef __MENUS__
  41. #include <Menus.h>
  42. #endif
  43. #ifndef __TEXTEDIT__
  44. #include <TextEdit.h>
  45. #endif
  46. #ifndef __DIALOGS__
  47. #include <Dialogs.h>
  48. #endif
  49. #ifndef __DESK__
  50. #include <Desk.h>
  51. #endif
  52. #ifndef __SCRAP__
  53. #include <Scrap.h>
  54. #endif
  55. #ifndef __TOOLUTILS__
  56. #include <ToolUtils.h>
  57. #endif
  58. #ifndef __MEMORY__
  59. #include <Memory.h>
  60. #endif
  61. #ifndef __SEGLOAD__
  62. #include <SegLoad.h>
  63. #endif
  64. #ifndef __FILES__
  65. #include <Files.h>
  66. #endif
  67. #ifndef __OSUTILS__
  68. #include <OSUtils.h>
  69. #endif
  70. #ifndef __TRAPS__
  71. #include <Traps.h>
  72. #endif
  73.  
  74. #ifndef __APPLICATION__
  75. #include "Application.h"
  76. #endif
  77.  
  78. #ifndef __DOCUMENT__
  79. #include "Document.h"
  80. #endif
  81.  
  82. // OSEvent is the event number of the suspend/resume and mouse-moved events sent
  83. // by MultiFinder. Once we determine that an event is an osEvent, we look at the
  84. // high byte of the message sent to determine which kind it is. To differentiate
  85. // suspend and resume events we check the resumeMask bit.
  86. const short kOsEvent = app4Evt;                // event used by MultiFinder
  87. const short kSuspendResumeMessage = 0x01;    // high byte of suspend/resume event message
  88. const short kClipConvertMask = 0x02;        // bit of message field clip conversion
  89. const short kResumeMask = 0x01;                // bit of message field for resume vs. suspend
  90. const short kMouseMovedMessage = 0xFA;        // high byte of mouse-moved event message
  91.  
  92. extern "C" {
  93.     // from MPW standard library
  94.     void _DataInit();                // sets up A5 globals
  95. };
  96.  
  97. // useful state checking Boolean
  98. Boolean gHaveColorQD;
  99.  
  100. // just as "normal" global variables that are declared extern in
  101. // header files must still be declared somewhere in order to reserve
  102. // space, static class variables must also be declared outside of
  103. // the class definition. The syntax is confusing, but then, thats
  104. // what makes C++ so ***interesting***.
  105. OSType TApplication::fCreator;
  106. Boolean    TApplication::fHaveWaitNextEvent;
  107. Boolean    TApplication::fOnSystem7;
  108. TApplication* TApplication::gApplication = nil;
  109.  
  110. TApplication::TApplication(OSType creator)
  111. {
  112.     SysEnvRec envRec;
  113.     long stkNeeded;
  114.     long heapSize;
  115.  
  116.     // initialize the global application variable
  117.     gApplication = this;
  118.  
  119.     // initialize Mac Toolbox components
  120.     InitGraf((Ptr) &qd.thePort);
  121.     InitFonts();
  122.     InitWindows();
  123.     InitMenus();
  124.     TEInit();
  125.     InitDialogs((ResumeProcPtr) nil);
  126.     InitCursor();
  127.  
  128.     // Unload data segment: note that _DataInit must not be in Main!
  129.     UnloadSeg((ProcPtr) _DataInit);
  130.  
  131.     // ignore the error returned from SysEnvirons; even if an error occurred,
  132.     // the SysEnvirons glue will fill in the SysEnvRec
  133.     (void) SysEnvirons(curSysEnvVers, &envRec);
  134.  
  135.     // Are we running on a 128K ROM machine or better???
  136.     if (envRec.machineType < 0)
  137.       BigBadError(kErrStrings,eWrongMachine);        // if not, alert & quit
  138.  
  139.     gHaveColorQD = envRec.hasColorQD;
  140.  
  141.     fOnSystem7 = (envRec.systemVersion >= 0x0700);
  142.  
  143.     // if we need more stack space, get it now
  144.     stkNeeded = StackNeeded();
  145.     if (stkNeeded > StackSpace())
  146.       {
  147.         // new address is heap size + current stack - needed stack
  148.         SetApplLimit((Ptr) ((long) GetApplLimit() - stkNeeded + StackSpace()));
  149.       }
  150.  
  151.     // Check for minimum heap size
  152.     heapSize = (long) GetApplLimit() - (long) ApplicZone();
  153.     if (heapSize < HeapNeeded())
  154.       BigBadError(kErrStrings,eSmallSize);
  155.  
  156.     // expand the heap so new code segments load at the top
  157.     MaxApplZone();
  158.  
  159.     // allocate an empty document list
  160.     fDocList = new TDocumentList;
  161.  
  162.     // check to see if WaitNextEvent is implemented
  163.     fHaveWaitNextEvent = TrapAvailable(_WaitNextEvent, ToolTrap);
  164.  
  165.     // initialize our class variables
  166.     fCurDoc = nil;
  167.     fDone = false;
  168.     fInBackground = false;
  169.     fMouseRgn = nil;
  170.     fWhichWindow = nil;
  171.     fCreator = creator;
  172. }
  173.  
  174.  
  175. TApplication* TApplication::GetApplication()
  176. {
  177.     return gApplication;
  178. }
  179.  
  180.  
  181. void TApplication::EventLoop()
  182. {
  183.     int gotEvent;
  184.     EventRecord tEvt;
  185.  
  186.     SetUp();                    // call setup routine
  187.     DoIdle();                    // do idle once
  188.  
  189.     while (!fDone)
  190.       {
  191.         // always set up fWhichWindow before doing anything
  192.         fWhichWindow = FrontWindow();
  193.         if (fWhichWindow != nil)
  194.           {
  195.             // see if window belongs to a document
  196.             fCurDoc = fDocList->FindDoc(fWhichWindow);
  197.             // make sure we always draw into correct window
  198.             SetPort(fWhichWindow);
  199.           }
  200.         else fCurDoc = nil;
  201.  
  202.         DoIdle();            // call idle time handler
  203.  
  204.         if (fHaveWaitNextEvent)
  205.           gotEvent = WaitNextEvent(everyEvent, &tEvt, SleepVal(), fMouseRgn);
  206.         else
  207.           {
  208.             SystemTask();
  209.             gotEvent = GetNextEvent(everyEvent, &tEvt);
  210.           }
  211.         fTheEvent = tEvt;
  212.  
  213.         // if we got a real event, process it
  214.         if (gotEvent)
  215.           ProcessEvent();
  216.  
  217.         AdjustCursor();
  218.       }
  219. }
  220.  
  221. void TApplication::ProcessEvent()
  222. {
  223.     // make sure alert is loaded in memory BEFORE we do any event
  224.     // processing. This is necessary since the alert for the case
  225.     // when we need to display an out of memory alert.
  226.     CouldAlert(rUserAlert);
  227.     TRY
  228.       {
  229.         AdjustCursor();
  230.         switch (fTheEvent.what)
  231.           {
  232.             case mouseDown:
  233.                 DoMouseDown();
  234.                 break;
  235.             case mouseUp:
  236.                 DoMouseUp();
  237.                 break;
  238.             case keyDown:
  239.             case autoKey:
  240.                 DoKeyDown();
  241.                 break;
  242.             case updateEvt:
  243.                 DoUpdateEvt();
  244.                 break;
  245.             case diskEvt:
  246.                 DoDiskEvt();
  247.                 break;
  248.             case activateEvt:
  249.                 DoActivateEvt();
  250.                 break;
  251.             case kOsEvent:
  252.                 DoOSEvent();
  253.                 break;
  254.             case kHighLevelEvent:
  255.                 DoHighLevelEvent();
  256.                 break;
  257.             default:
  258.                 break;
  259.           }
  260.       }
  261.     RECOVER
  262.       {
  263.         AlertUser((short) gFailMessage, gFailError);
  264.         // don't let error bubble up any farther
  265.         goto doneEvent;
  266.       }
  267.     ENDTRY
  268.  
  269. doneEvent:
  270.     return;
  271. }
  272.  
  273. void TApplication::DoHighLevelEvent()
  274. {
  275.     FailOSErr(AEProcessAppleEvent(&fTheEvent));
  276. }
  277.  
  278. void TApplication::DoKeyDown()
  279. {
  280.     char key;
  281.     long mResult;
  282.  
  283.     key = (char) (fTheEvent.message & charCodeMask);
  284.     if ((fTheEvent.modifiers & cmdKey) && (fTheEvent.what == keyDown))
  285.       {
  286.         // only do command keys if we are not autokeying
  287.         AdjustMenus();                    // make sure menus are up to date
  288.         mResult = MenuKey(key);
  289.         if (mResult != 0)                // if it wasn't a menu key, pass it through
  290.           {
  291.             DoMenuCommand(HiWord(mResult), LoWord(mResult));
  292.             return;
  293.           }
  294.       }
  295.     if (fCurDoc != nil)
  296.       {
  297.         EventRecord tEvt;
  298.  
  299.         // we copy event record so that we don't pass reference to object field
  300.         tEvt = fTheEvent;
  301.         fCurDoc->DoKeyDown(&tEvt);
  302.       }
  303. }
  304.  
  305. void TApplication::DoActivateEvt()
  306. {
  307.     // event record contains window ptr
  308.     fWhichWindow = (WindowPtr) fTheEvent.message;
  309.     // see if window belongs to a document
  310.     fCurDoc = fDocList->FindDoc(fWhichWindow);
  311.     SetPort(fWhichWindow);
  312.  
  313.     if (fCurDoc != nil)
  314.       fCurDoc->DoActivate((fTheEvent.modifiers & activeFlag) != 0);
  315. }
  316.  
  317. void TApplication::DoUpdateEvt()
  318. {
  319.     // event record contains window ptr
  320.     fWhichWindow = (WindowPtr) fTheEvent.message;
  321.     // see if window belongs to a document
  322.     fCurDoc = fDocList->FindDoc(fWhichWindow);
  323.     SetPort(fWhichWindow);
  324.  
  325.     if (fCurDoc != nil)
  326.       fCurDoc->DoUpdate();
  327. }
  328.  
  329. // NOTE: we use an anonymous parameter here so that the compiler
  330. // doesn't warn us about it being unused. Since we give it a name
  331. // in the class definition, we still know what its used for.
  332. void TApplication::DoSuspend(Boolean)
  333. {
  334.     if (fCurDoc != nil)
  335.       fCurDoc->DoActivate(!fInBackground);
  336. }
  337.  
  338. void TApplication::DoResume(Boolean)
  339. {
  340.     if (fCurDoc != nil)
  341.       fCurDoc->DoActivate(!fInBackground);
  342. }
  343.  
  344. void TApplication::DoOSEvent()
  345. {
  346.     Boolean doConvert;
  347.     unsigned char evType;
  348.  
  349.     // is it a multifinder event?
  350.     evType = (unsigned char) (fTheEvent.message >> 24) & 0x00ff;
  351.     switch (evType) {     // high byte of message is type of event
  352.         case kMouseMovedMessage:
  353.             DoIdle();                    // mouse-moved is also an idle event
  354.             break;
  355.         case kSuspendResumeMessage:
  356.             doConvert = (fTheEvent.message & kClipConvertMask) != 0;
  357.             fInBackground = (fTheEvent.message & kResumeMask) == 0;
  358.             if (fInBackground)
  359.               DoSuspend(doConvert);
  360.             else DoResume(doConvert);
  361.             break;
  362.     }
  363. }
  364.  
  365. void TApplication::DoMouseDown()
  366. {
  367.     long mResult;
  368.     short partCode;
  369.     WindowPtr tWind;
  370.     EventRecord tEvt;
  371.  
  372.     // gotta watch those object field dereferences
  373.     partCode = FindWindow(fTheEvent.where, &tWind);
  374.     fWhichWindow = tWind;
  375.     tEvt = fTheEvent;
  376.     switch (partCode)
  377.       {
  378.         case inSysWindow:
  379.             DoMouseInSysWindow();
  380.             break;
  381.         case inMenuBar:
  382.             AdjustMenus();
  383.             mResult = MenuSelect(tEvt.where);
  384.             if (mResult != 0)
  385.               DoMenuCommand(HiWord(mResult),LoWord(mResult));
  386.             break;
  387.         case inGoAway:
  388.             DoGoAway();
  389.             break;
  390.         case inDrag:
  391.             DoDrag();
  392.             break;
  393.         case inGrow:
  394.             if (fCurDoc != nil)
  395.               fCurDoc->DoGrow(&tEvt);
  396.             break;
  397.         case inZoomIn:
  398.         case inZoomOut:
  399.             if ((TrackBox(fWhichWindow, tEvt.where, partCode)) &&
  400.                 (fCurDoc != nil))
  401.               fCurDoc->DoZoom(partCode);
  402.             break;
  403.         case inContent:
  404.             // If window is not in front, make it so
  405.             if ( fWhichWindow != FrontWindow() )
  406.               SelectWindow(fWhichWindow);
  407.             else if (fCurDoc != nil)
  408.               fCurDoc->DoContent(&tEvt);
  409.             break;
  410.       }
  411. }
  412.  
  413. void TApplication::DoDrag()
  414. {
  415.     DragWindow(fWhichWindow, fTheEvent.where, &qd.screenBits.bounds);
  416. }
  417.  
  418. void TApplication::DoGoAway()
  419. {
  420.     if (TrackGoAway(fWhichWindow, fTheEvent.where))
  421.       {
  422.         if (fCurDoc != nil)
  423.           {
  424.             if (fCurDoc->DoClose(true, yesResult, false) != cancelResult)
  425.               {
  426.                 fDocList->RemoveDoc(fCurDoc);
  427.                 delete fCurDoc;
  428.               }
  429.           }
  430.         else CloseDeskAcc(((WindowPeek) fWhichWindow)->windowKind);
  431.         // make sure our current document/window references are valid
  432.         fWhichWindow = FrontWindow();
  433.         if (fWhichWindow != nil)
  434.           {
  435.             fCurDoc = fDocList->FindDoc(fWhichWindow);
  436.             SetPort(fWhichWindow);
  437.           }
  438.         else fCurDoc = nil;
  439.       }
  440. }
  441.  
  442. void TApplication::ProcessArgs()
  443. {
  444.     if (!fOnSystem7)
  445.     {
  446.         short message, numFiles, curFile;
  447.         AppFile fileInfo;
  448.  
  449.         /* count the files */
  450.         CountAppFiles(&message,&numFiles);
  451.         if (numFiles == 0)
  452.           {
  453.             // create a single empty document
  454.             DoNew();
  455.             return;
  456.           }
  457.         for (curFile = 1; curFile <= numFiles; curFile++)
  458.           {
  459.             /* get file info */
  460.             GetAppFiles(curFile,&fileInfo);
  461.             /* open/print the file */
  462.             if (message != appPrint)
  463.               {
  464.                 TRY
  465.                   {
  466.                     OpenADoc(fileInfo.vRefNum,0,fileInfo.fName,fileInfo.fType);
  467.                   }
  468.                 RECOVER
  469.                   {
  470.                     goto processNextFile;
  471.                   }
  472.                 ENDTRY
  473.               }
  474.     processNextFile:
  475.             /* clear finder arg for this file */
  476.             ClrAppFiles(curFile);
  477.           }
  478.     }
  479. }
  480.  
  481. void TApplication::DoQuit(Boolean askUser, YNCResult defaultResult)
  482. {
  483.     while (true)
  484.       {
  485.         fWhichWindow = FrontWindow();
  486.         if (fWhichWindow == nil)
  487.           break;
  488.         fCurDoc = fDocList->FindDoc(fWhichWindow);
  489.         if (fCurDoc != nil)
  490.           {
  491.             // if the user cancels the quit
  492.             if (fCurDoc->DoClose(askUser, defaultResult, true) == cancelResult)
  493.               return;
  494.             else
  495.               {
  496.                 fDocList->RemoveDoc(fCurDoc);
  497.                 delete fCurDoc;
  498.               }
  499.           }
  500.         else CloseDeskAcc(((WindowPeek) fWhichWindow)->windowKind);
  501.         // make sure we aren't in an infinite loop. This could occur
  502.         // if the CloseDeskAcc was failing for some reason.
  503.         if (FrontWindow() == fWhichWindow)
  504.           {
  505.             // send the window to the back of the list.
  506.             // if the FrontWindow is still the same, we will exit
  507.             // the loop. Otherwise, we let the loop keep running so
  508.             // that we have a chance to close our other windows
  509.             // cleanly
  510.             SendBehind(fWhichWindow, nil);
  511.             if (FrontWindow() == fWhichWindow)
  512.               break;
  513.           }
  514.       }
  515.     fDone = true;
  516.     fWhichWindow = nil;
  517.     fCurDoc = nil;
  518. }
  519.  
  520. Boolean TApplication::TrapAvailable(short tNumber,TrapType tType)
  521. {
  522.     // Check and see if the trap exists. On 64K ROM machines, tType will be ignored.
  523.     return NGetTrapAddress(tNumber, tType) != GetTrapAddress(_Unimplemented);
  524. }
  525.  
  526. void TApplication::WDToDirID(short wdRefNum, short& vRefNum, long& dirID)
  527. {
  528.     const short kRootDirID = 2;
  529.     long junk;
  530.  
  531.     OSErr err = GetWDInfo(wdRefNum,&vRefNum,&dirID,&junk);
  532.     if (err != noErr)
  533.       {
  534.         vRefNum = wdRefNum;        // if GetVol doesn't return valid vRefNum/dirID pair,
  535.         dirID = kRootDirID;        // use wdRefNum as a vRefNum and use root for dirID
  536.       }
  537. }
  538.  
  539. void AlertUser(short errResID, short errCode)
  540. {
  541.     Str255 messageStr;
  542.  
  543.     // if we have a hilited menu, turn it off before displaying alert
  544.     HiliteMenu(0);
  545.  
  546.     if (errResID != 0)
  547.       {
  548.         GetIndString(messageStr, errResID, errCode);
  549.         ParamText(messageStr, "\p", "\p", "\p");
  550.       }
  551.     else
  552.       {
  553.         // we need to lookup the error in our table
  554.         LookupErrorString(errCode,kSysErrStrings,messageStr);
  555.         ParamText(messageStr, "\p", "\p", "\p");
  556.       }
  557.     SetCursor(&qd.arrow);
  558.     (void) Alert(rUserAlert, (ModalFilterProcPtr) nil);
  559. }
  560.  
  561. void BigBadError(short errResID, short errCode)
  562. {
  563.     AlertUser(errResID,errCode);
  564.     ExitToShell();
  565. }
  566.  
  567. Boolean LookupErrorString(short value, short resID, StringPtr str)
  568. {
  569.     struct ErrRecord {
  570.         short lowErr;
  571.         short highErr;
  572.         short index;
  573.     };
  574.     typedef struct ErrRecord* ErrRecordPtr;
  575.  
  576.     Handle            table;
  577.     ErrRecordPtr    pEntry;
  578.     unsigned long    tableOffset;
  579.     long            lenTab;
  580.     int                strID;
  581.  
  582.     // start with an empty string
  583.     str[0] = 0;
  584.  
  585.     table = GetResource('errs', resID);
  586.     if (!table)
  587.       {
  588.         lenTab = (long) (GetHandleSize((Handle) table) / sizeof(ErrRecord));
  589.  
  590.         strID = 0;
  591.         tableOffset = 0;
  592.  
  593.         for (long i = 1; i <= lenTab; i++)
  594.           {
  595.             pEntry = (ErrRecordPtr) ((unsigned long) *table) + tableOffset;
  596.  
  597.             if (pEntry->lowErr == 0)
  598.               strID = pEntry->index;
  599.             else if ((pEntry->lowErr <= value) && (value <= pEntry->highErr))
  600.               {
  601.                 if (pEntry->index > 0)
  602.                   GetIndString(str, strID, pEntry->index);
  603.                 return true;
  604.               }
  605.  
  606.             tableOffset += sizeof(ErrRecord);
  607.           }
  608.       }
  609.     return false;
  610. }
  611.  
  612. // That's all, folks...
  613.