home *** CD-ROM | disk | FTP | other *** search
/ MacFormat 1995 May / macformat-024.iso / Shareware City / Developers / BoxMaker++ / BoxMaker++ ƒ / boxmaker.cp < prev    next >
Encoding:
Text File  |  1995-01-20  |  22.2 KB  |  894 lines  |  [TEXT/KAHL]

  1. #include <Types.h>
  2. #include <Memory.h>
  3. #include <QuickDraw.h>
  4. #include <OSUtils.h>
  5. #include <ToolUtils.h>
  6. #include <Menus.h>
  7. #include <Packages.h>
  8. #include <StandardFile.h>
  9. #include <Traps.h>
  10. #include <Files.h>
  11. #include <Aliases.h>
  12. #include <AppleEvents.h>
  13. #include <GestaltEqu.h>
  14. #include <Processes.h>
  15. #include <Fonts.h>
  16. #include <OSEvents.h>
  17. #include <Resources.h>
  18. #include <Desk.h>
  19. #include <Errors.h>
  20.  
  21. #include "boxmaker constants.h"
  22. #include "boxmaker.h"
  23.  
  24. const AEAddressDesc boxmaker::self = GetTargetToSelf();
  25.  
  26. const CursHandle boxmaker::theClock = GetCursor( watchCursor);
  27.  
  28. Boolean boxmaker::itsProcessable() const
  29. {
  30.     Boolean result = false;
  31.  
  32.     if( itsVisible() || passInvisibles)
  33.     {
  34.         //
  35.         // First check against the 'typs' resource, if one was present
  36.         //
  37.         if( numTypes == -1)
  38.         {
  39.             result = true;    // all items must be passed on
  40.         } else {
  41.             result = matchesTypeList();
  42.         }
  43.         if( result == true)
  44.         {
  45.             //
  46.             // We passed the 'typs' resource check; check against
  47.             // the 'tycr' resource.
  48.             //
  49.             if( numTypeCreatorPairs == -1)
  50.             {
  51.                 result = true;
  52.             } else {
  53.                 result = matchesTypeCreatorPairs();
  54.             }
  55.         }
  56.     }
  57.     return result;
  58. }
  59.  
  60. Boolean boxmaker::matchesTypeList() const
  61. {
  62.     const OSType ourType = theCInfoPBRec.hFileInfo.ioFlFndrInfo.fdType;
  63.     Boolean result = false;
  64.  
  65.     OSType *theType = theTypes;
  66.     
  67.     for( int i = 0; i < numTypes; i++)
  68.     {
  69.         if( ourType == *theType)
  70.         {
  71.             result = true;
  72.             break;
  73.         }
  74.     }
  75.     return result;
  76. }
  77.  
  78. Boolean boxmaker::matchesTypeCreatorPairs() const
  79. {
  80.     const OSType theType    = theCInfoPBRec.hFileInfo.ioFlFndrInfo.fdType;
  81.     const OSType theCreator = theCInfoPBRec.hFileInfo.ioFlFndrInfo.fdCreator;
  82.     Boolean result = false;
  83.  
  84.     OSTypePair *thePair = theTypeCreatorPairs;
  85.     
  86.     for( int i = 0; i < numTypeCreatorPairs; i++)
  87.     {
  88.         if( matches( theType, thePair->type) && matches( theCreator, thePair->creator))
  89.         {
  90.             result = true;
  91.             break;
  92.         }
  93.     }
  94.     return result;
  95. }
  96.  
  97. void boxmaker::InitToolbox() 
  98. {    
  99.     InitGraf( &qd.thePort);
  100.     InitFonts();
  101.     InitWindows();
  102.     InitMenus();
  103.     TEInit();
  104.     InitDialogs( 0L);
  105.     InitCursor();
  106.     FlushEvents( everyEvent, 0);
  107.     MoreMasters();
  108.     MoreMasters();
  109. }
  110.  
  111. Boolean boxmaker::InitGlobals() 
  112. {
  113.     the_status    = shell_is_running;
  114.     gMainDialog    = NULL;
  115.  
  116.     long aLong;
  117.     return (Gestalt( gestaltAppleEventsAttr, &aLong) == noErr);
  118. }
  119.  
  120. void boxmaker::SetUpMenus()
  121. {
  122.     Handle theMenus = GetNewMBar( kMBarID);
  123.     
  124.     SetMenuBar( theMenus);
  125.  
  126.     MenuHandle theAppleMenu = GetMHandle( kAppleMenuID);
  127.     AddResMenu( theAppleMenu, 'DRVR');
  128.     DrawMenuBar();
  129. }
  130. //
  131. //    This routine is called during startup to display a splash screen.
  132. //    
  133. //    This was recommend by the Blue Team HI person, John Sullivan, who
  134. //    feels that all apps should display something so that users can easily
  135. //    tell what is running, and be able to switch by clicking.  Thanks John!
  136. //
  137. void boxmaker::GetMainDialog()
  138. {
  139.     if( !gMainDialog)
  140.     {
  141.         gMainDialog = GetNewDialog( kSettingsDialogID,
  142.                                     &myDialogRecord, (WindowPtr)-1L);
  143.     }
  144. }
  145.  
  146. void boxmaker::ShowAbout()
  147. {
  148.     (void)Alert( kAboutAlertID, NULL);
  149. }
  150.  
  151. void boxmaker::DoAppleMenu( short itemID)
  152. {
  153.     if( itemID == 1)
  154.     {
  155.         ShowAbout();
  156.     } else {
  157.         Str255 itemStr;
  158.         GetItem( GetMHandle( kAppleMenuID), itemID, itemStr);
  159.         OpenDeskAcc( itemStr);
  160.     }
  161. }
  162.  
  163. void boxmaker::DoMenu( long retVal)
  164. {
  165. //    const short menuID = HiWord( retVal);
  166. //    const short itemID = LoWord( retVal);
  167.     const short menuID = retVal >> 16;
  168.     const short itemID = retVal & 0x00000FFFF;
  169.  
  170.     switch( menuID)
  171.     {
  172.         case kAppleMenuID:
  173.             DoAppleMenu( itemID);
  174.             break;
  175.             
  176.         case kFileMenuID:
  177.             switch( itemID)
  178.             {
  179.                 case kSelectFileItem:
  180.                     SelectFile();
  181.                     break;
  182.             
  183.                 case kPrefsItem:
  184.                     ShowPreferences();
  185.                     break;
  186.                     
  187.                 case kQuitItem:
  188.                     SendQuitToSelf();
  189.                     break;
  190.             }
  191.             break;
  192.         
  193.         default:
  194.             break;            
  195.     }
  196.     HiliteMenu( 0);
  197. }
  198.  
  199. void boxmaker::DoMouseDown()
  200. {
  201.     WindowPtr    whichWindow;
  202.     short whichPart = FindWindow( myEvent.where, &whichWindow);
  203.     switch( whichPart)
  204.     {
  205.         case inMenuBar:
  206.             DoMenu( MenuSelect( myEvent.where));
  207.             break;
  208.         
  209.         case inSysWindow:
  210.             SystemClick( &myEvent, whichWindow);
  211.             break;
  212.         
  213.         case inDrag:
  214.             DragWindow( whichWindow, myEvent.where, &qd.screenBits.bounds);
  215.             break;
  216.  
  217.         case inGoAway:
  218.             if( TrackGoAway( whichWindow, myEvent.where))
  219.             {
  220.                 HideWindow( whichWindow);
  221.             }
  222.             break;
  223.         //
  224.         // We do not handle zooming or resizing. It seems simple, but one would
  225.         // need to add a couple of virtual functions to make it practical, so
  226.         // we do not do it.
  227.         //
  228.         default:
  229.             break;
  230.     }
  231. }
  232.  
  233. void boxmaker::DoKeyDown()
  234. {
  235.     if( myEvent.modifiers & cmdKey)
  236.     {
  237.         DoMenu( MenuKey( (char)myEvent.message & charCodeMask));
  238.     }
  239. }
  240.  
  241. boxmaker::boxmaker()
  242. {
  243.     InitToolbox();
  244.     if( !InitGlobals())
  245.     {
  246.         ErrorAlertQuit( kErrStringID, kCantRunErr, 0);
  247.     }
  248.     InitAEVTStuff();
  249.     SetUpMenus();
  250.     GetMainDialog();
  251.     //
  252.     // These handles will never get freed again. This is no problem,
  253.     // since they are only allocated once, and would be freed at exit
  254.     // time, anyway.
  255.     //
  256.     Handle theTypesHandle = Get1Resource( 'typs', 128);
  257.     if( theTypesHandle == 0)
  258.     {
  259.         numTypes = -1;
  260.         theTypes = 0L;
  261.     } else {
  262.         MoveHHi( theTypesHandle);
  263.         HLock( theTypesHandle);
  264.         numTypes = GetHandleSize( theTypesHandle) >> 2;
  265.         theTypes = (OSType *)*theTypesHandle;
  266.     }
  267.     Handle theTypeCreatorPairsHandle = Get1Resource( 'tycr', 128);
  268.     if( theTypeCreatorPairsHandle == 0)
  269.     {
  270.         numTypeCreatorPairs = -1;
  271.         theTypeCreatorPairs = 0L;
  272.     } else {
  273.         MoveHHi( theTypeCreatorPairsHandle);
  274.         HLock( theTypeCreatorPairsHandle);
  275.         numTypeCreatorPairs = GetHandleSize( theTypeCreatorPairsHandle) >> 2;
  276.         theTypeCreatorPairs = (OSTypePair *)*theTypeCreatorPairsHandle;
  277.     }
  278.     theCInfoPBRec.hFileInfo.ioNamePtr = theFSSpec.name;
  279.     //
  280.     // read extra flags:
  281.     //
  282.     Handle flagHandle = Get1Resource( 'flgs', 128);
  283.     if( flagHandle)
  284.     {
  285.         const Boolean *flags = (const Boolean *)*flagHandle;
  286.         enterFolders    = flags[ 0];
  287.         passFolders     = flags[ 1];
  288.         enterInvisibles = flags[ 2];
  289.         passInvisibles  = flags[ 3];
  290.         ReleaseResource( flagHandle);
  291.     } else {
  292.         enterFolders    = false;
  293.         passFolders     = false;
  294.         enterInvisibles = false;
  295.         passInvisibles  = false;
  296.     }
  297.     GetCurrentProcess( &myPSN);
  298.     time_of_next_cup_of_tea = TickCount() + kFirstSleepInterval;
  299. }
  300.  
  301. void boxmaker::run()
  302. {
  303.     Boolean canQuitNow = false;
  304.     while( !canQuitNow)
  305.     {
  306.         const short currentSleep = amInFront() ? kForeSleepValue : kBackSleepValue;
  307.     
  308.         gotEvent = WaitNextEvent( everyEvent, &myEvent, currentSleep, NULL);
  309.         //
  310.         // We use 'EventloopHook( …) && …' rather than '… && EventloopHook( …)'
  311.         // since we always want to call 'EventloopHook'.
  312.         //
  313.         canQuitNow = EventloopHook() && (the_status >= shell_is_finishing);
  314.     
  315.         if( gotEvent)
  316.         {
  317.             const int gotCmdKey = (myEvent.what == keyDown) && (myEvent.modifiers & cmdKey);
  318.     
  319.             if( !gotCmdKey && IsDialogEvent( &myEvent))
  320.             {
  321.                 short itemHit;
  322.                 DialogPtr theDialog;
  323.                 if( DialogSelect( &myEvent, &theDialog, &itemHit))
  324.                 {
  325.                     HandleDialogEvent( itemHit, theDialog);
  326.                 }
  327.             } else {
  328.                 switch( myEvent.what)
  329.                 {
  330.                     case kHighLevelEvent:
  331.                         DoHighLevelEvent();
  332.                         break;
  333.                         
  334.                     case mouseDown:
  335.                         DoMouseDown();
  336.                         break;
  337.                         
  338.                     case keyDown:
  339.                     case autoKey:
  340.                         DoKeyDown();
  341.                         break;
  342.                         
  343.                     default:
  344.                         break;
  345.                 }
  346.             }
  347.         }
  348.     }
  349. }
  350.  
  351. void boxmaker::InitAEVTStuff()
  352. {
  353.     OSErr aevtErr =
  354.  
  355.         AEInstallEventHandler( kCoreEventClass, kAEOpenApplication,
  356.                      (AEEventHandlerUPP) HandleOAPP, (long)this, false)
  357.  
  358.         || AEInstallEventHandler( kCoreEventClass, kAEOpenDocuments,
  359.                     (AEEventHandlerUPP) HandleODOC, (long)this, false)
  360.  
  361.         || AEInstallEventHandler( kCoreEventClass, kAEPrintDocuments,
  362.                     (AEEventHandlerUPP) HandlePDOC, (long)this, false)
  363.  
  364.         || AEInstallEventHandler( kCoreEventClass, kAEQuitApplication,
  365.                     (AEEventHandlerUPP) HandleQuit, (long)this, false);
  366.     FailErr( aevtErr);
  367. }
  368.  
  369. OSErr boxmaker::GotRequiredParams( AppleEvent *theAppleEvent)
  370. {
  371.     DescType    typeCode;
  372.     Size        actualSize;
  373.     OSErr        result;
  374.  
  375.     OSErr err = AEGetAttributePtr( theAppleEvent, keyMissedKeywordAttr,
  376.                     typeWildCard, &typeCode, NULL, 0, &actualSize);
  377.     
  378.     if( err == errAEDescNotFound)    // we got all the required params: all is ok
  379.     {
  380.         result = noErr;
  381.     } else if( err == noErr) {
  382.         result = errAEEventNotHandled;
  383.     } else {
  384.         result = err;
  385.     }
  386.     return result;
  387. }
  388. //
  389. //    This routine is the handler for the oapp (Open Application) event.
  390. //
  391. //    It first checks the number of parameters to make sure we got them all
  392. //    (even though we don't want any) and then calls the OpenApp userProc.
  393. //    Finally it checks to see if the caller wanted a reply & sends one,
  394. //    setting any error.
  395. //
  396. pascal OSErr HandleOAPP( AppleEvent *theAppleEvent, AppleEvent *reply, long handlerRefcon)
  397. {
  398.     OSErr result = boxmaker::GotRequiredParams( theAppleEvent);
  399.  
  400.     boxmaker *me = (boxmaker *)handlerRefcon;
  401.     me->the_status = shell_is_OApped;
  402.     
  403.     me->OpenApp();        // pass it on to the app specific routine
  404.  
  405.     if( reply->dataHandle != NULL )    //    a reply is sought
  406.     {
  407.         const char error_message[] = "Opening";
  408.         const long error_length = sizeof( error_message) - 1;    // don't include zero terminator
  409.         result = AEPutParamPtr( reply, 'errs', 'TEXT', error_message, error_length);
  410.     }
  411.     return result;
  412. }
  413. //
  414. //    This routine is the handler for the quit (Quit Application) event.
  415. //
  416. //    It first checks the number of parameters to make sure we got them all 
  417. //    (even though we don't want any) and then calls the QuitApp userProc.
  418. //    Finally it checks to see if the caller wanted a reply & sends one,
  419. //    setting any error.
  420. //
  421. pascal OSErr HandleQuit( AppleEvent *theAppleEvent, AppleEvent *reply, long handlerRefcon)
  422. {
  423.     OSErr result = boxmaker::GotRequiredParams( theAppleEvent);
  424.  
  425.     boxmaker *me = (boxmaker *)handlerRefcon;
  426.     
  427.     me->the_status = shell_is_quitting;
  428.  
  429.     if( reply->dataHandle != NULL )    //    a reply is sought
  430.     {
  431.         const char error_message[] = "Quitting";
  432.         const long error_length = sizeof( error_message) - 1;    // don't include zero terminator
  433.         result = AEPutParamPtr( reply, 'errs', 'TEXT', error_message, error_length);
  434.     }
  435.     return result;
  436. }
  437. //    
  438. //    This routine is the low level processing routine for both the 
  439. //    odoc (Open Document) and pdoc (Print Document) events.
  440. //
  441. //    This routine is the key one, since this is how we get the list of
  442. //    files/folders/disks to process.  The first thing to do is the get the
  443. //    list of files, and then make sure that's all the parameters (should be!).
  444. //    We then process each file in the list by calling _HandleOneDoc.
  445. //
  446. OSErr boxmaker::_HandleDocs( AppleEvent *theAppleEvent,
  447.                                 AppleEvent *reply, Boolean opening)
  448. {
  449.     AEDescList    docList;
  450.     long        itemsInList;
  451.  
  452.     OSErr result =
  453.         AEGetParamDesc( theAppleEvent, keyDirectObject, typeAEList, &docList);
  454.  
  455.     if( result == noErr)
  456.     {
  457.         result = GotRequiredParams( theAppleEvent);
  458.         if( result == noErr)
  459.         {
  460.             result = AECountItems( &docList, &itemsInList);
  461.         }
  462.     }
  463.     StartABunch( itemsInList, opening);
  464.     for( int index = 1; (index <= itemsInList) && (result == noErr); index++)
  465.     {
  466.         FSSpec        aFSSpec;
  467.         Size        actualSize;
  468.         AEKeyword    keywd;
  469.         DescType    typeCode;
  470.         
  471.         result = AEGetNthPtr( &docList, index, typeFSS, &keywd,
  472.                         &typeCode, (Ptr)&aFSSpec, sizeof( aFSSpec ), &actualSize);
  473.  
  474.         if( result == noErr)
  475.         {
  476.             //
  477.             // Fill in the volume reference number
  478.             // (it will not change during a recursive descent)
  479.             // But the different dropped items might be on different volumes,
  480.             // so we do it inside the 'itemsInList' loop.
  481.             // This is true since we do not 'follow aliases' during the descent.
  482.             // (implementing that would also mean having to check for endless loops,
  483.             // and thus would not be trivial)
  484.             //
  485.             theCInfoPBRec.hFileInfo.ioVRefNum = aFSSpec.vRefNum;
  486.     
  487.             result = _HandleOneDoc( aFSSpec, opening);
  488.         }
  489.     }
  490.     EndABunch( itemsInList, opening);
  491.     if( (the_status == shell_is_running) && opening)
  492.     {
  493.         //
  494.         //    The reason we also check for 'opening' is based on a recommendation
  495.         //    in the Apple event Registry which specifically states that you should
  496.         //    NOT quit on a 'pdoc' as the Finder will send you a 'quit' when it is
  497.         //    ready for you to do so.
  498.         //
  499.         the_status = shell_is_finishing;
  500.     }
  501.     const OSErr dispose_result = AEDisposeDesc( &docList);
  502.     if( result == noErr)
  503.     {
  504.         result = dispose_result;
  505.     }
  506.     return result;
  507. }
  508. //
  509. // _HandleOneDoc is called repeatedly by _HandleDocs
  510. //
  511. OSErr boxmaker::_HandleOneDoc( const FSSpec &aFSSpec, Boolean opening)
  512. {
  513.     OSErr result = GatherInfo( aFSSpec);
  514.  
  515.     if( result == noErr)
  516.     {
  517.         if( itsADirectory())
  518.         {
  519.             result = _HandleADir( theSubDirID, opening);
  520.         } else {
  521.             if( itsProcessable())
  522.             {
  523.                 SetUp_theFSSpec_from_theCInfoPBRec();
  524.                 OpenDoc( opening);
  525.             }
  526.         }
  527.     }
  528.     return result;
  529. }
  530. //
  531. // _HandleADir is the low-level recursive directory handler.
  532. //
  533. OSErr boxmaker::_HandleADir( long dirID, Boolean opening)
  534. {
  535.     OSErr result = noErr;
  536.     
  537.     if( itsVisible() || passInvisibles)
  538.     {
  539.         if( passFolders)
  540.         {
  541.             SetUp_theFSSpec_from_theCInfoPBRec();
  542.             OpenDoc( opening);
  543.         }
  544.         if( enterFolders && (itsVisible() || enterInvisibles))
  545.         {
  546.             SetUp_theFSSpec_from_theCInfoPBRec();
  547.             if( mayEnterFolder( opening))
  548.             {
  549.                 const char theAccessRights = getAccessRights();
  550.                 //
  551.                 // We must enter the folder whenever we have
  552.                 // either read or search privileges, or both
  553.                 //
  554.                 if( (theAccessRights & 3) == 3)
  555.                 {
  556.                     CantEnterFolder( opening);
  557.                 } else {
  558.                     EnterFolder( opening);
  559.                     int index = 1;
  560.                     do
  561.                     {
  562.                         result = GatherInfo( dirID, index);
  563.                         
  564.                         if( result == noErr)
  565.                         {
  566.                             if( itsADirectory())
  567.                             {
  568.                                 result = _HandleADir( theSubDirID, opening);
  569.                             } else {
  570.                                 if( itsProcessable())
  571.                                 {
  572.                                     SetUp_theFSSpec_from_theCInfoPBRec();
  573.                                     OpenDoc( opening);
  574.                                 }
  575.                             }
  576.                         }
  577.                         index += 1;
  578.                     } while( result == noErr);
  579.                     if( result == fnfErr)    // fnfErr simply indicates end of directory
  580.                     {
  581.                         result = noErr;
  582.                     }
  583.                     ExitFolder( opening);
  584.                 }
  585.             }
  586.         }
  587.     }
  588.     return result;
  589. }
  590. //
  591. //    This routine is the handler for the odoc (Open Document) event.
  592. //    
  593. //    The odoc event simply calls the common _HandleDocs routines, which will
  594. //    do the dirty work of parsing the AEVT & calling the userProcs.
  595. //
  596. pascal OSErr HandleODOC( AppleEvent *theAppleEvent, AppleEvent *reply, long handlerRefcon)
  597. {
  598.     boxmaker *me = (boxmaker *)handlerRefcon;
  599.     return me->_HandleDocs( theAppleEvent, reply, true);
  600. }
  601. //
  602. //    This routine is the handler for the pdoc (Print Document) event.
  603. //
  604. //    The pdoc event like the odoc simply calls the common _HandleDocs routines
  605. //
  606. pascal OSErr HandlePDOC( AppleEvent *theAppleEvent, AppleEvent *reply, long handlerRefcon )
  607. {
  608.     boxmaker *me = (boxmaker *)handlerRefcon;
  609.     return me->_HandleDocs( theAppleEvent, reply, false);
  610. }
  611. //
  612. //    This is the routine called by the main event loop, when a high level
  613. //    event is found.  Since we only deal with Apple events, and not other
  614. //    high level events, we just pass everything onto the AEM via AEProcessAppleEvent
  615. //
  616. void boxmaker::DoHighLevelEvent()
  617. {
  618.     FailErr( AEProcessAppleEvent( &myEvent));
  619. }
  620.  
  621. void boxmaker::SelectFile()
  622. {
  623.     StandardFileReply stdReply;
  624.  
  625.     StandardGetFile( NULL, numTypes, theTypes, &stdReply);
  626.  
  627.     if( stdReply.sfGood)
  628.     {
  629.         SendODOCToSelf( &stdReply.sfFile);
  630.     }
  631. }
  632. //
  633. //    A quick & dirty error reporter
  634. //
  635. void boxmaker::ErrorAlert( short errorNo, short stringIndexID, short stringListID)
  636. {
  637.     Str255    param;
  638.     Str15    errorStr;
  639.  
  640.     NumToString( errorNo, errorStr);
  641.     GetIndString( param, stringListID, stringIndexID);
  642.     ParamText( param, errorStr, NULL, NULL);
  643.     (void)Alert( kErrorAlertID, NULL);
  644. }
  645.  
  646. AEAddressDesc boxmaker::GetTargetToSelf()
  647. {
  648.     ProcessSerialNumber    psn;
  649.  
  650.     psn.highLongOfPSN = 0;
  651.     psn.lowLongOfPSN  = kCurrentProcess;
  652.  
  653.     AEAddressDesc result;
  654.     (void)AECreateDesc( typeProcessSerialNumber, (Ptr)&psn,
  655.                                 sizeof(ProcessSerialNumber), &result);
  656.     return result;
  657. }
  658. //
  659. //    This routine is the low level routine used by the SendODOCToSelf
  660. //    routine.  It gets passed the list of files (in an AEDescList)
  661. //    to be sent as the data for the 'odoc', builds up the event
  662. //    and sends off the event.  
  663. //
  664. //    It is broken out from SendODOCToSelf so that a SendODOCListToSelf could
  665. //    easily be written and it could then call this routine - but that is left
  666. //    as an exercise to the reader.
  667. //
  668. void boxmaker::_SendDocsToSelf( AEDescList *aliasList)
  669. {
  670.     AppleEvent openDocAE;
  671.     OSErr err = AECreateAppleEvent(kCoreEventClass, kAEOpenDocuments,
  672.                 &self, kAutoGenerateReturnID, kAnyTransactionID, &openDocAE);
  673.  
  674.     if( err == noErr)
  675.     {
  676.         err = AEPutParamDesc( &openDocAE, keyDirectObject, aliasList);
  677.  
  678.         if( err == noErr)
  679.         {
  680.             //
  681.             //    Since we are sending to ourselves, there is no need for reply.
  682.             //
  683.             AppleEvent replyAE;
  684.             err = AESend( &openDocAE, &replyAE, kAENoReply | kAECanInteract,
  685.                                                 kAENormalPriority, 3600, NULL, NULL);
  686.             //
  687.             //    NOTE: Since we are not requesting a reply, we do not need to
  688.             //    need to dispose of the replyAE.  It is there simply as a 
  689.             //    placeholder.
  690.             //
  691.         }
  692.     }
  693.     err = AEDisposeDesc( &openDocAE);
  694. }
  695. //
  696. //    This is the routine called by SelectFile to send a single odoc to ourselves.
  697. //
  698. //    It calls the above low level routine to do the dirty work of sending the AEVT -
  699. //    all we do here is build a AEDescList of the file to be opened.
  700. //
  701. void boxmaker::SendODOCToSelf( FSSpec *theFileSpec)
  702. {
  703.     AEDescList    aliasList;
  704.  
  705.     const OSErr error = AECreateList( NULL, 0, false, &aliasList);
  706.  
  707.     if( error == noErr)
  708.     {
  709.         AliasHandle    aliasH;
  710.  
  711.         (void)NewAlias( NULL, theFileSpec, &aliasH);
  712.  
  713.         AEDesc aliasDesc;
  714.         aliasDesc.descriptorType = typeAlias;
  715.         aliasDesc.dataHandle     = (Handle)aliasH;
  716.         
  717.         (void)AEPutDesc( &aliasList, 0, &aliasDesc);
  718.  
  719.         DisposHandle( (Handle)aliasH);
  720.  
  721.         _SendDocsToSelf( &aliasList);
  722.  
  723.         (void)AEDisposeDesc( &aliasList);
  724.     }
  725. }
  726.  
  727. Boolean boxmaker::amInFront() const
  728. {
  729.     ProcessSerialNumber frontProcess;
  730.     Boolean result = false;
  731.     if( GetFrontProcess( &frontProcess) == noErr)
  732.     {
  733.         (void)SameProcess( &myPSN, &frontProcess, &result);
  734.     }
  735.     return result;
  736. }
  737.  
  738. void boxmaker::SendQuitToSelf (void)
  739. {
  740.     AppleEvent quitAE;
  741.     OSErr error = AECreateAppleEvent( kCoreEventClass, kAEQuitApplication,
  742.                     &self, kAutoGenerateReturnID, kAnyTransactionID, &quitAE);
  743.  
  744.     if( error == noErr)
  745.     {
  746.         AppleEvent replyAE;
  747.         error = AESend( &quitAE, &replyAE, kAENoReply | kAECanInteract,
  748.                                         kAENormalPriority, 3600, NULL, NULL);
  749.         //
  750.         //    NOTE: Since we are not requesting a reply, we do not need to
  751.         //    need to dispose of the replyAE.  It is there simply as a 
  752.         //    placeholder.
  753.         //
  754.     }
  755.     (void)AEDisposeDesc( &quitAE);
  756. }
  757.  
  758. void boxmaker::errDebug( OSErr errno, unsigned char *errString)
  759. {
  760.     if( errno != noErr)
  761.     {
  762.         Str63 debugString;
  763.         NumToString( errno, *(Str255 *)&debugString);
  764.  
  765.         const int numberLength = debugString[ 0];
  766.         //
  767.         // the '+ 1' is for the colon after the error number
  768.         //
  769.         const int numFitting = 63 - (numberLength + 1);
  770.         const int errStringLength = errString[ 0];
  771.         const int numToCopy =
  772.             (errStringLength < numFitting) ? errStringLength : numFitting;
  773.  
  774.         unsigned char *firstafter = &debugString[ numberLength + 1];
  775.         *firstafter++ = ':';
  776.         const unsigned char *currentMessageChar = &errString[ 1];
  777.  
  778.         for( int i = 0; i < numToCopy; i++)
  779.         {
  780.             *firstafter++ = *currentMessageChar++;
  781.         }
  782.         debugString[ 0] = numberLength + 1 + errStringLength;
  783.         DebugStr( debugString);
  784.     }
  785. }
  786.  
  787. OSErr boxmaker::_GatherInfo( long theDirID)
  788. {
  789.     theCInfoPBRec.hFileInfo.ioDirID = theDirID;
  790.     //
  791.     // Note: According to IM, the field 'ioACUser' of the parameter block
  792.     // is unmodified when GetCatInfo is called for a non-network directory.
  793.     // (well, that is if one interprete a lack of an output arrow on page IV-153
  794.     // of IM-IV as meaning that the field in question is left untouched).
  795.     // However, with the arrival of 'File Manager extensions in a shared Environment'
  796.     // users can no longer assume that the field is unmodified, so one could suppose
  797.     // that GetCatInfo now _always_ returns something in that field. To be compatible
  798.     // with non-shared environments I set the field to a certainly good value before
  799.     // calling PBGetCatInfo. We use zero, which translates to 'everybody may do anything',
  800.     // which is a logical value in a non-shared environment.
  801.     // Also, I do not have the most recent header files, so I hack my way to the
  802.     // field in question.
  803.     //
  804.     ((char *)(&theCInfoPBRec))[ 31] = (char)0;
  805.     const OSErr result = PBGetCatInfoSync( &theCInfoPBRec);
  806.     
  807.     TimeForTea();
  808.     //
  809.     // ioDirID is both in- and output for GetCatInfo. If the item for which info
  810.     // was requested is a directory it is changed to the ID of that directory.
  811.     // In that case we have to set it back to the right value before we can do
  812.     // a 'SetCatInfo'. 'OpenDoc' assumes that theCInfoPBRec _can_ be used to set
  813.     // file information, so we reset it immediately. We do keep the ID of the item
  814.     // itself in 'theSubDirID'.
  815.     //
  816.     theSubDirID = theCInfoPBRec.hFileInfo.ioDirID;
  817.     theCInfoPBRec.hFileInfo.ioDirID = theDirID;
  818.     
  819.     return result;
  820. }
  821.  
  822. void boxmaker::StartABunch( long numTopLevelItems, Boolean opening)
  823. {
  824.     SetCursor( *theClock);
  825.     //
  826.     // Disable all menus:
  827.     //
  828.     Handle theMenuBar = GetMenuBar();
  829.     HLock( theMenuBar);
  830.  
  831.     const menulist *theList = (const menulist *)*theMenuBar;
  832.  
  833.     const int numMenus = theList->lastHandleOffset / 6;
  834.     
  835.     for( int i = 0; i < numMenus; i++)
  836.     {
  837.         const MenuHandle currentMenu = theList->theMenus[ i].theMenu;
  838.         DisableItem( currentMenu, 0);
  839.     }
  840.     DrawMenuBar();
  841.     DisposeHandle( theMenuBar);
  842. }
  843.  
  844. void boxmaker::EndABunch( long numTopLevelItems, Boolean opening)
  845. {
  846.     InitCursor();
  847.     //
  848.     // Enable all menus:
  849.     //
  850.     Handle theMenuBar = GetMenuBar();
  851.     HLock( theMenuBar);
  852.  
  853.     const menulist *theList = (const menulist *)*theMenuBar;
  854.  
  855.     const int numMenus = theList->lastHandleOffset / 6;
  856.     
  857.     for( int i = 0; i < numMenus; i++)
  858.     {
  859.         const MenuHandle currentMenu = theList->theMenus[ i].theMenu;
  860.         EnableItem( currentMenu, 0);
  861.     }
  862.     DrawMenuBar();
  863.     DisposeHandle( theMenuBar);
  864. }
  865.  
  866. void boxmaker::TimeForTea()
  867. {
  868.     //
  869.     // 950116: In an attempt to make dropboxes built with boxmaker++ more
  870.     // MultiFinder-friendly (anybody know a more modern name for that?)
  871.     // we call WaitNextEvent every now and then during a directory traversal.
  872.     // We do not want to get any further 'ODOC' events here, though, so we
  873.     // don't handle any events. This is only a rudimentary solution to the
  874.     // problem, but it does work (sort of).
  875.     //
  876.     if( TickCount() > time_of_next_cup_of_tea)
  877.     {
  878.         const Boolean inFront = amInFront();
  879.         const short currentSleep    = inFront ? kForeSleepValue    : kBackSleepValue;
  880.         const short currentInterval = inFront ? kForeSleepInterval : kBackSleepInterval;
  881.  
  882.         EventRecord junkEvent;
  883.         (void)WaitNextEvent( 0, &junkEvent, currentSleep, nil);
  884.  
  885.         if( inFront)
  886.         {
  887.             SetCursor( *theClock);
  888.         }
  889.         FlushEvents( mDownMask | mUpMask | keyDownMask | keyUpMask | autoKeyMask, 0);
  890.         
  891.         time_of_next_cup_of_tea = TickCount() + currentInterval;
  892.     }
  893. }
  894.