home *** CD-ROM | disk | FTP | other *** search
/ MacHack 2001 / MacHack 2001.toast / pc / The Hacks / CSFinalHack Module / ESDemo.c < prev    next >
Encoding:
C/C++ Source or Header  |  2001-06-23  |  31.5 KB  |  962 lines

  1. /* -----------------------------------------------------------------------------------------
  2. ESDemo.c
  3.     
  4.     ©1995-2000 Ammon Skidmore, Skidperfect Software Inc. http://www.skidperfect.com/
  5.     
  6.     Public Domain: use, borrow and steal whatever code you want for your own modules!
  7.     
  8.     This is a "quick" hack that I threw together to test and demonstrate
  9.     Extensions Strip's powerful additions to the Control Strip API.  Do not
  10.     think that I bothered to optimize anything or that I choose the best methods
  11.     to approach each situation.  I'd rather waste my time doing that to ES :-)
  12.     
  13.     Key features of this module:
  14.     -    Fat binary.
  15.     -    Backward compatibility with Control/Desktop Strip.
  16.     -    Intelligent drag handling.  Under Control/Desktop Strip the draggable
  17.         area is the location of the module's icon.  Under Extensions Strip this
  18.         area is the total amount of space that the module takes up.
  19.     -    2 out of the 3 possible methods for sending "safe" AppleEvents that don't rely
  20.         upon the front process' context.  Under Control/Desktop Strip the event is still
  21.         sent, but will not work if the front process does not support Apple Events.
  22.     -    Ability to get key presses under Extensions Strip without having to install
  23.         a custom jGNE filter (as my Key Holder module does.)
  24.     -    Ability to change the sdevFeatures without having to restart.  Examples are
  25.         given for changing almost all of the possible features, even locking and
  26.         unlocking my sdev code.
  27.     -    Demonstrates the changing of display height and width.
  28.     -    Demonstrates a good compatible method of handling hierarchial menus.
  29.     
  30.     Notes about PPC linking:
  31.     -    For PPC compiling, remember to use 'weak links' to shared libraries that may not
  32.         be on every user's computer.  Not doing so will cause a crash because Extensions
  33.         Strip will be forced to quit if the library is not present.  Modules should use
  34.         Gestalt calls to determine the presence of libraries they use.
  35.         For example, this project has a weak link to the DragLib, yet it does not have
  36.         one to the ControlStripLib or the InterfaceLib because these two libraries
  37.         will always be present.
  38.     -    This project, because it is Extensions Strip savvy, must be linked with the
  39.         ControlStripLibStub that comes with ES rather than the default ControlStripLib
  40.         of the Universal headers.  This way it can link to the additional ES functions.
  41.     
  42. Version History
  43. -=-=-=-=-=-=-=-
  44. 1.0        8/95 - 10/95    - initial release
  45.         30 Oct, 1995    - Made THINK compiler friendly by Vincent Tan.
  46.  
  47. 1.1        09 Nov, 1995    - Added the menu item 'Large Icon' to demonstrate the changing
  48.                         of display height and width.  Also, the icons were slightly
  49.                         modified, the creator type was changed to '!!!!', and a
  50.                         'hfdr' resource was added so programs like Conflict Catcher
  51.                         can look at our info text.  Maybe future Finders will display
  52.                         this balloon help as well...
  53.         15 Nov, 1995    - Added Code Warrior 7 projects, by François Pottier.
  54.  
  55. 1.1.1    20 Nov, 1995    - Fixed a bug that caused the icon to be drawn incorrectly
  56.                         when not using Extensions Strip.
  57.                         - Changed the drag handlers to return dragNotAcceptedErr when
  58.                         the draw was not accepted, instead of the value -1 as before.
  59.  
  60. 1.1.2    27 July, 1998    - Converted project to Code Warrior IDE 3.  Note: CW IDE 2 allows
  61.                         you to open the project but it clears all the build settings.
  62.                         - Fixed a bug that caused the icon to be drawn incorrectly
  63.                         when when using the miniature module mode of Extensions Strip 1.8.
  64.                         - Now works if the triangle PICT resource is deleted.
  65.  
  66. 1.1.3    05 Nov, 1998    - Converted project to CW IDE 4.
  67.                         - The PPC code is now compiled as a shared library so that it
  68.                             will run under Control Strip 2.0 (part of System 8.5 and later)
  69.                             and Extensions Strip 1.8 and later.  To get this to work, you
  70.                             need to rename ExtensionsStripLib to ControlStripLibStub.
  71.                         - Added a FAT taget to the project.  No source changes were made.
  72.  
  73. 1.2        30 July, 2000    - Converted project to CW IDE 5 (with latest patches: Update 5.3).
  74.                         - Demonstrates a compatible method of handling hierarchial menus.
  75.                         - Rearranged the code a bit and added some more comments.
  76.  
  77. 1.2.1    10 Aug, 2000    - when sdevInit() fails, it now frees memory by calling sdevClose().
  78.                         - made all the shared libraries "Import Weak".  A common mistake.
  79.                         - added some debugging macros and updated some comments.
  80.         25 April, 2001    - updated project file for CodeWarrior 6 (no source changes needed)
  81.                         - removed an old, unnecessary ES 2 beta flag (sdevMiniModulesFriendly)
  82.  
  83. -=-=-=-=-=-=-=-
  84. Framework taken from the CSShell.c Control Strip Sample by Martin R. Wachter.
  85. It is freely availible and can be found on the usual UMich/Info-Mac mirrors.
  86. ----------------------------------------------------------------------------------------- */
  87.  
  88. #ifdef powerc
  89.     // for Metrowerks' linker, this defines the mixed-mode interface for main().
  90.     ProcInfoType __procinfo = kPascalStackBased
  91.          | RESULT_SIZE(SIZE_CODE(sizeof(long)))
  92.          | STACK_ROUTINE_PARAMETER(1, SIZE_CODE(sizeof(long)))
  93.          | STACK_ROUTINE_PARAMETER(2, SIZE_CODE(sizeof(long)))
  94.          | STACK_ROUTINE_PARAMETER(3, SIZE_CODE(sizeof(Rect*)))
  95.          | STACK_ROUTINE_PARAMETER(4, SIZE_CODE(sizeof(WindowRef)));
  96. #endif
  97.  
  98. //#define DEBUG_ON    /* comment this out when making a release! */
  99.  
  100. #ifndef SystemSevenOrLater
  101. #define SystemSevenOrLater 1
  102. #endif
  103.  
  104. #define CALL_NOT_IN_CARBON 1        // OS X doesn't have ControlStrip
  105.  
  106. #include <Gestalt.h>
  107. #include <Icons.h>
  108. #include <Drag.h>
  109. #include <ControlStrip.h>
  110. #include "ExtensionsStrip.h"
  111. #include "Custom Def Utils.h"
  112.  
  113. #include "ESDemo.h"
  114.  
  115. /******************************************************************************
  116.  main: Control Strip's entry point into our sdev code
  117.  ******************************************************************************/
  118. pascal long main (long message, long params, Rect *statusRect, WindowRef statusPort)
  119. {
  120.     register long        result    = 0L;
  121.     MyGlobalHandle        globH    = (MyGlobalHandle)params;
  122.     
  123.     switch (message)
  124.     {
  125.         //
  126.         // Default Control Strip selectors:
  127.         //
  128.         
  129.         case sdevInitModule:         // check environs, allocate globals
  130.             result = sdevInit(statusPort);
  131.             break;
  132.         
  133.         case sdevCloseModule:         // release my memory
  134.             sdevClose(globH, statusPort);
  135.             break;
  136.         
  137.         case sdevFeatures:             // let the strip track the mouse down
  138.             result = (**globH).currentFeatures;
  139.             break;
  140.         
  141.         case sdevGetDisplayWidth:    // inform the strip how much space we need
  142.         {
  143.             PicHandle    myArrowPict        = (**globH).myArrowPict;
  144.             
  145.             if ((**globH).useLargeIcon)
  146.                 result = kLargeIconWidth + ((unsigned long)kLargeIconWidth << 16);
  147.             else
  148.                 result = kSmallIconWidth;
  149.             
  150.             if (myArrowPict)    /* if the arrow PICT was loaded, add it's width */
  151.                 result += width((*myArrowPict)->picFrame);
  152.             break;
  153.         }
  154.         
  155.         case sdevPeriodicTickle:    // never called by Extensions Strip because we
  156.         {                            // returned the sdevDontPeriodicTickle bit.
  157.             Rect    viewRect = *statusRect;
  158.             
  159.             // update the draggable box upon each idle cycle in case the user
  160.             // shrunk the Control Strip bar so that we are not visible, but our
  161.             // old position still is because the end of the bar is there.
  162.             viewRect.right = viewRect.left + (    (**globH).useLargeIcon ?
  163.                                                 kLargeIconWidth : kSmallIconWidth    );
  164.             (**globH).myBox = viewRect;
  165.             
  166.             break;
  167.         }
  168.         
  169.         case sdevDrawStatus:         // draw my icon and arrow pict
  170.             sdevDraw(globH, statusRect, statusPort);
  171.             break;
  172.         
  173.         case sdevMouseClick:         // the mouse was clicked & released in my button
  174.             result = sdevClick(globH, statusRect, statusPort);
  175.             break;
  176.         
  177.         case sdevSaveSettings:        // no settings in this module
  178.             break;
  179.         
  180.         case sdevShowBalloonHelp:    // we have a custom ballon help string
  181.         {
  182.             Str255    helpString;
  183.             
  184.             SBGetDetachedIndString(helpString, (**globH).myStrings, kHelpStringIndex);
  185.             SBShowHelpString(statusRect, helpString);
  186.             
  187.             break;
  188.         }
  189.         
  190.         //
  191.         // Extensions Strip specific selectors:
  192.         // Note that we check for the presence of ES just in case...
  193.         //
  194.         
  195.         case sdevInAppContext:        // we are inside Extensions Strip's context so
  196.                                     // is is safe to send Apple Events from here.
  197.         {
  198.             ProcessSerialNumber    finderPSN;
  199.             ProcessInfoRec        infoRec;
  200. #define        kFinderType            'FNDR'
  201. #define        kSysCreator            'MACS'
  202.  
  203.             if ((**globH).hasExtensionsStrip) {
  204.                 infoRec.processInfoLength    =    sizeof(ProcessInfoRec);
  205.                 infoRec.processName            =    nil;    //    no name wanted processName;
  206.                 infoRec.processAppSpec        =    nil;    //    no spec wanted &procSpec;
  207.                 
  208.                 if (FindAProcess(kFinderType, kSysCreator, &finderPSN, &infoRec) == noErr)
  209.                     QuitProcess(finderPSN, FALSE);    // FALSE because we don't HAVE to use
  210.                                                     // _SBSimpleAESend here
  211.             }
  212.             break;
  213.         }
  214.         
  215.         case sdevInterceptedEvent:    // a chance to look at and modify the current event
  216.         {
  217.             EventRecord    *theEvent;
  218.             
  219.             if ((**globH).hasExtensionsStrip) {
  220.                 // for demonstration's purpose, we'll intercept command + *
  221.                 // (* on the keypad only since cmd-shift-8 is an FKEY combo that
  222.                 // is intercepted by the System.)
  223.                 theEvent = SBGetCurrentEvent();
  224.             // we can assume a keydown since our mask filters out the rest.
  225.             //    if (theEvent->what == keyDown)
  226.                     if (theEvent->modifiers & cmdKey) {
  227.                         switch (theEvent->message & charCodeMask)
  228.                         {
  229.                             case '*':
  230.                                 SysBeep(0);
  231.                                 theEvent->what = nullEvent;        // don't pass on the event
  232.                                 break;
  233.                         }
  234.                     }
  235.             }
  236.             break;
  237.         }
  238.         
  239.         case sdevDragAccept:    // let Extensions Strip know if we won't handle the drag
  240.             if ((**globH).hasExtensionsStrip &&
  241.                         !(**globH).canAcceptDrag)
  242.                 result = 1;                // we don't accept the drag
  243.             break;
  244.         
  245.         case sdevEventMask:        // let Extensions Strip know we want key down events
  246.             if ((**globH).hasExtensionsStrip)
  247.                 result = keyDownMask;    // our GNE style event mask
  248.             break;
  249.     }
  250.     
  251.     return (result);
  252. }
  253.  
  254.  
  255. static pascal void MyDrawPicture(PicHandle thePicture, Rect *destRect)
  256. {
  257.     CallDrawPictureProc(gSaveDrawPicture, gDownArrow, destRect);
  258. }
  259.  
  260.  
  261. Boolean    inMenuBarMode = false, fakeOutButton = false;
  262. static pascal Boolean MyButton(void)
  263. {
  264.     Boolean    result;
  265.     
  266.     result = CallButtonProc(gSaveButton);
  267.     if (fakeOutButton) {
  268.         return (!result);
  269.     }
  270.     
  271.     return (result);
  272. }
  273.  
  274.  
  275. // MDEF Code Resource entry point
  276. static pascal void menuMDEF(short msg, MenuHandle whichMenu, Rect *menuRect, Point hitPt, short *itemID )
  277. {
  278. //    char        cMemTags;
  279.     Handle        oldMDEF, curMDEF;
  280.     Rect        popupRect;
  281.     Point        localPt;
  282.     Boolean        bail                = false,
  283.                 outsideOfTriangle    = false;
  284.  
  285.     CallMenuDefProc((MenuDefUPP)*(gOldMDEF), msg, whichMenu, menuRect, hitPt, itemID);
  286.  
  287.     switch (msg)
  288.     {
  289.         case kMenuChooseMsg:
  290.             GetMouse(&localPt);
  291.             // if in the menu area, do nothing special
  292.             if (PtInRect(hitPt, menuRect))
  293.                 break;
  294.             if (PtInRect(localPt, menuRect))
  295.                 break;
  296.             // if menu item is blinking (was selected), do nothing special
  297.             if (*(long*)&hitPt == 0L)
  298.                 break;
  299.             // if released mouse before dragging to other modules, go into mouseup menubar mode
  300. //            if (!Button() && !esGlob->inMenuBarMode)
  301. //                esGlob->isMouseUpMenu = true;
  302.             
  303.             // if mouse is inside the module's bounds, just record the last mouse pos
  304.             if ((hitPt.h > menuRect->left-4) && (hitPt.h < menuRect->left+35)) {
  305.             } else {
  306.                 inMenuBarMode = true;
  307.                 if (!Button()) {
  308. //                    PostEvent(mouseDown, 0);
  309.                 } else {
  310. //                    PostEvent(mouseUp, 0);
  311.                     PostEvent(mouseDown, 0);
  312.                     fakeOutButton = true;
  313.                 }
  314.                 *itemID = 0;
  315.             }
  316.             break;
  317.     }
  318. }
  319.  
  320.  
  321. static long CSMenuHackPopUpMenuSelect(MenuHandle theMenu, short top, short left, short popUpItem)
  322. {
  323.     long        result;
  324.     Handle        mdefStubH=0L;
  325.     
  326.     gOldMDEF = (**theMenu).menuProc;
  327.     mdefStubH = GetUniversalFunctionHandle((ProcPtr)menuMDEF, uppMenuDefProcInfo);
  328.     (**theMenu).menuProc = mdefStubH;
  329.     
  330.     result = CallPopupMenuSelectProc(gOldPopupMenuSelect, theMenu, top, left, popUpItem);
  331.     
  332.     fakeOutButton = false;
  333.     
  334.     (**theMenu).menuProc = gOldMDEF;
  335.     if (mdefStubH) DisposeHandle(mdefStubH);
  336.     return result;
  337. }
  338.  
  339.  
  340.  
  341. /******************************************************************************
  342.  sdevInit: allocate memory for our globals and init our globals
  343.  ******************************************************************************/
  344. long sdevInit(WindowRef statusPort)
  345. {
  346.     MyGlobalHandle    globH;
  347.     
  348.     //
  349.     // get some memory
  350.     //
  351.     globH = (MyGlobalHandle) NewHandleClear(sizeof(MyGlobals));
  352.     if (!globH)
  353.         goto exit;
  354.     
  355.     //
  356.     // temporarily lock our global handle because we pass pointers of it to toolbox
  357.     // functions.  If I wanted to be really cool I'd load all the handles into local
  358.     // variables to remove this need, as well as the repetitive double deferencing...
  359.     //
  360.     HLock((Handle)globH);
  361.  
  362.     //
  363.     // get our icon suite
  364.     //
  365.         if (SBGetDetachIconSuite(&(**globH).iconSuite, rIconSuiteId, svAllSmallData) != noErr) goto exit;
  366.     //
  367.     // get our string list
  368.     //
  369.         (**globH).myStrings = GetResource('STR#', rMyStringsID);
  370.         if (!(**globH).myStrings) goto exit;
  371.         DetachResource((**globH).myStrings);
  372.     //
  373.     // get our menus
  374.     //
  375.         (**globH).myMenuH        = GetMenu(rPopupMenuID);
  376.         (**globH).myHierMenuH    = GetMenu(rHierMenuID);
  377.         if ( (!(**globH).myMenuH) || (!(**globH).myHierMenuH) )
  378.             goto exit;
  379.         DetachResource((Handle)(**globH).myMenuH);
  380.         DetachResource((Handle)(**globH).myHierMenuH);
  381.     //
  382.     // get our arrow picture
  383.     //
  384.         (**globH).myArrowPict = GetPicture(rArrowPictID);
  385.         if ((**globH).myArrowPict)
  386.             DetachResource((Handle)(**globH).myArrowPict);
  387.     //
  388.     // setup our initial features
  389.     //
  390.         (**globH).currentFeatures = (
  391.                       (1L<<sdevWantMouseClicks)        // we handle mouse down
  392.                     | (1L<<sdevDontAutoTrack)        // we track the mouse, too
  393.                     | (1L<<sdevHasCustomHelp)        // custom help string
  394.                     | (1L<<sdevKeepModuleLocked)        // I need to be locked
  395.                                                     //         for drag-n-drop.
  396.                 );
  397.     //
  398.     // determine if Extensions Strip is running.
  399.     // If so, get my reference number and setup ES specific feature flags
  400.     //
  401.         (**globH).hasExtensionsStrip = HasExtensionsStrip();
  402.         if ((**globH).hasExtensionsStrip)
  403.         {
  404.             (**globH).myReferenceNum = SBGetMyReferenceNum();
  405.             (**globH).currentFeatures +=
  406.                 (
  407.                       (1L<<sdevHasDragHandlers)        // flag that we have drag handlers
  408.                     | (1L<<sdevDontPeriodicTickle)    // we don't need any idle time
  409.                     | (1L<<sdevInterceptAllEvents)    // get all events under Extensions Strip
  410.                 );
  411.         }
  412.     //
  413.     // setup the drag stuff
  414.     //
  415.         InstallDragHandlers(statusPort, globH);
  416.         (**globH).acceptFiles = true;
  417.         SetItemMark((**globH).myMenuH, kDragFiles, sdevMenuItemMark);
  418.  
  419.  
  420. // hack stuff:
  421.     if (! gPopupPatch) gPopupPatch = NewPopupMenuSelectProc(CSMenuHackPopUpMenuSelect);
  422.     if (! gPopupPatch) goto exit;
  423.     
  424.     gOldPopupMenuSelect = (PopupMenuSelectUPP) NGetTrapAddress(_PopUpMenuSelect, toolOrOSTrap(_PopUpMenuSelect));
  425.     NSetTrapAddress(gPopupPatch, _PopUpMenuSelect, toolOrOSTrap(_PopUpMenuSelect));
  426.  
  427.     if (! gButtonPatch) gButtonPatch = NewButtonProc(MyButton);
  428.     if (! gButtonPatch) goto exit;
  429.     gSaveButton = (ButtonUPP) NGetTrapAddress(_Button, toolOrOSTrap(_Button));
  430.     NSetTrapAddress(    (UniversalProcPtr) gButtonPatch,
  431.                         _Button, toolOrOSTrap(_Button));
  432.  
  433.     gDownArrow = (PicHandle)Get1Resource('PICT', 128);
  434.     if (! gDownArrow) goto exit;
  435.     DetachResource((Handle)gDownArrow);
  436.     if (! gDrawPicturePatch) gDrawPicturePatch = NewDrawPictureProc(MyDrawPicture);
  437.     if (! gDrawPicturePatch) return (-1L);
  438.     gSaveDrawPicture = (DrawPictureUPP) NGetTrapAddress(_DrawPicture, toolOrOSTrap(_DrawPicture));
  439.     NSetTrapAddress(    (UniversalProcPtr) gDrawPicturePatch,
  440.                         _DrawPicture, toolOrOSTrap(_DrawPicture));
  441.  
  442. {// clear strip
  443.     RgnHandle    curClip = NewRgn();
  444.     SetPort(statusPort);
  445.     SetClip(statusPort->visRgn);
  446.     EraseRgn(statusPort->visRgn);
  447.     InvalRgn(statusPort->visRgn);
  448.     SetClip(curClip);
  449.     DisposeRgn(curClip);
  450. }
  451.  
  452.     //
  453.     // set my handle free and return its value
  454.     //
  455.         HUnlock((Handle)globH);
  456.         return ((long) globH);
  457.     
  458.     //
  459.     // did not get any memory, don't install
  460.     //
  461. exit:
  462.     sdevClose(globH, statusPort);        // (ammon 000804)
  463.     return (-1L);
  464. }
  465.  
  466. /******************************************************************************
  467.  sdevClose: free memory for our globals
  468.  ******************************************************************************/
  469. void sdevClose(MyGlobalHandle globH, WindowRef statusPort)
  470. {
  471. // useful macros:
  472. #define    myDisposeHandle(h)\
  473.     if ((h) != nil)\
  474.         DisposeHandle((Handle) (h))
  475. #define    myDisposeIconSuite(h, p1)\
  476.     if ((h) != nil)\
  477.         DisposeIconSuite(h, p1)
  478.  
  479. // dispose of everything:
  480.     if (globH){
  481.         myDisposeIconSuite((**globH).iconSuite, true);
  482.         myDisposeHandle((**globH).myMenuH);
  483.         myDisposeHandle((**globH).myHierMenuH);
  484.         myDisposeHandle((**globH).myStrings);
  485.         myDisposeHandle((**globH).myArrowPict);
  486.         
  487.         RemoveDragHandlers(statusPort, globH);
  488.         
  489.         DisposeHandle((Handle) globH);
  490.     }
  491.  
  492.     if (gOldPopupMenuSelect)
  493.         NSetTrapAddress(gOldPopupMenuSelect, _PopUpMenuSelect, toolOrOSTrap(_PopUpMenuSelect));
  494.     if (gSaveButton)
  495.         NSetTrapAddress(gSaveButton, _Button, toolOrOSTrap(_Button));
  496.     if (gSaveDrawPicture)
  497.         NSetTrapAddress((UniversalProcPtr) gSaveDrawPicture, _DrawPicture, toolOrOSTrap(_DrawPicture));
  498. {// clear strip
  499.     RgnHandle    curClip = NewRgn();
  500.     SetPort(statusPort);
  501.     SetClip(statusPort->visRgn);
  502.     EraseRgn(statusPort->visRgn);
  503.     InvalRgn(statusPort->visRgn);
  504.     SetClip(curClip);
  505.     DisposeRgn(curClip);
  506. }
  507. }
  508.  
  509. /******************************************************************************
  510.  sdevClick: handle a click in our status rect
  511.  ******************************************************************************/
  512. long sdevClick(MyGlobalHandle globH, const Rect    *statusRect, WindowRef statusPort)
  513. {
  514.     short            menuItem;
  515.     MenuHandle        menuH        = (**globH).myMenuH,        // local copy to save deferencing
  516.                     hierMenuH    = (**globH).myHierMenuH;    // (ammon 000730)
  517.     long            result        = 0L;
  518.     char            itemMark;
  519.     short            theBit, index;
  520.     long            menuResult;                                // (ammon 000730)
  521.     
  522.     // reflect the current features in th popup menu
  523.     itemMark = ((**globH).currentFeatures & (1L<<sdevWantMouseClicks)) ? sdevMenuItemMark : noMark;
  524.     SetItemMark(menuH, kHiliteClicks, itemMark);
  525.     itemMark = ((**globH).currentFeatures & (1L<<sdevHasCustomHelp)) ? sdevMenuItemMark : noMark;
  526.     SetItemMark(menuH, kCustomBalloons, itemMark);
  527.     itemMark = ((**globH).currentFeatures & (1L<<sdevInterceptAllEvents)) ? sdevMenuItemMark : noMark;
  528.     SetItemMark(menuH, kCheckKeyDowns, itemMark);
  529.     itemMark = ((**globH).useLargeIcon) ? sdevMenuItemMark : noMark;
  530.     SetItemMark(menuH, kUseLargeIcon, itemMark);
  531.     itemMark = ((**globH).currentFeatures & (1L<<sdevHasDragHandlers)) ? sdevMenuItemMark : noMark;
  532.     SetItemMark(menuH, kUseDragNDrop, itemMark);
  533.     
  534.     // disable what Control/Desktop Strip doesn't support
  535.     if (!(**globH).hasExtensionsStrip) {
  536.         DisableItem(menuH, kSafeFinderQuit2);
  537.         SetItemMark(menuH, kCheckKeyDowns, noMark);
  538.     }
  539.     
  540.     // (ammon 000730) setup the hierarchial menu
  541.     InsertMenu(hierMenuH, -1);
  542.     
  543.     // show our popup menu
  544.     menuItem = SBTrackPopupMenu(statusRect, menuH);
  545.     
  546.     // (ammon 000730) take down the hierarchial menu and find out what menu was selected
  547.     DeleteMenu(rHierMenuID);
  548.     menuResult = MenuChoice();
  549.     // Note, MenuChoice() would not be needed if we used ES 2's API call SBTrackHierMenu(),
  550.     // though then we wouldn't be compatible with Control Strip and the like.
  551.     
  552.     switch ((menuResult & 0xFFFF0000) >> 16)    // (ammon 000730) switch on the menu id
  553.     {
  554.         case rHierMenuID:        // (ammon 000730)
  555.             for (index=0; index<menuItem; index++)
  556.             {
  557.                 SysBeep(0);
  558.             }
  559.             break;
  560.         default:                // NOTE: do not rely on the menu id of the menu passed to
  561.                                 // SBTrackPopupMenu().  It may have changed behind your back
  562.                                 // if there was a conflict with another menu resource.
  563.                                 // (This is more likely to occur under pre-8.5 systems where
  564.                                 // modules are run in the front app's context.)
  565.             switch (menuItem)
  566.             {
  567.                 
  568.                 case kBeepIfPPC:
  569.         #ifdef powerc
  570.                     SysBeep(0);
  571.         #endif
  572.                     break;
  573.                 
  574.                 case kHiliteClicks:
  575.                     theBit = sdevWantMouseClicks;
  576.                     // pass on
  577.                 
  578.                 case kCustomBalloons:
  579.                     if (menuItem == kCustomBalloons)
  580.                         theBit = sdevHasCustomHelp;
  581.                     // pass on
  582.                 
  583.                 case kCheckKeyDowns:
  584.                     if (menuItem == kCheckKeyDowns)
  585.                         theBit = sdevInterceptAllEvents;
  586.                     // pass on
  587.                 
  588.                 case kUseDragNDrop:
  589.                     if ((**globH).hasExtensionsStrip) {
  590.                         if (menuItem == kUseDragNDrop) {
  591.                             theBit = sdevHasDragHandlers;
  592.                             // Install/remove handlers for fun.  We can also now
  593.                             // unlock our code, all for demonstration purposes.
  594.                             if ((**globH).currentFeatures & (1L<<sdevHasDragHandlers)) {
  595.                                 (**globH).currentFeatures -= (1L<<sdevKeepModuleLocked);
  596.                                 RemoveDragHandlers(statusPort, globH);
  597.                                 DisableItem(menuH, kDragFiles);
  598.                                 DisableItem(menuH, kDragTextClippings);
  599.                             } else {
  600.                                 (**globH).currentFeatures += (1L<<sdevKeepModuleLocked);
  601.                                 InstallDragHandlers(statusPort, globH);
  602.                                 EnableItem(menuH, kDragFiles);
  603.                                 EnableItem(menuH, kDragTextClippings);
  604.                             }
  605.                         }
  606.                         
  607.                         // handle the passed bit
  608.                             if ((**globH).currentFeatures & (1L<<theBit))
  609.                                 (**globH).currentFeatures -= (1L<<theBit);
  610.                             else
  611.                                 (**globH).currentFeatures += (1L<<theBit);
  612.                             result += (1L<<sdevFeaturesChange);    // request a sdevFeatures call
  613.                     }
  614.                     break;
  615.                 
  616.                 case kUseLargeIcon:
  617.                     (**globH).useLargeIcon = !(**globH).useLargeIcon;
  618.                     // request to be asked our dimensions again
  619.                     result = (1L<<sdevResizeDisplay);
  620.                     break;
  621.                 case kDragFiles:
  622.                     (**globH).acceptFiles = !(**globH).acceptFiles;
  623.                     itemMark = (**globH).acceptFiles ? sdevMenuItemMark : noMark;
  624.                     SetItemMark(menuH, kDragFiles, itemMark);
  625.                     break;
  626.                 
  627.                 case kDragTextClippings:
  628.                     (**globH).acceptText = !(**globH).acceptText;
  629.                     itemMark = (**globH).acceptText ? sdevMenuItemMark : noMark;
  630.                     SetItemMark(menuH, kDragTextClippings, itemMark);
  631.                     break;
  632.                 
  633.                 case kSafeFinderQuit1: {
  634.                     ProcessSerialNumber    finderPSN;
  635.                     ProcessInfoRec        infoRec;
  636.  
  637.                     infoRec.processInfoLength    =    sizeof(ProcessInfoRec);
  638.                     infoRec.processName            =    nil;    //    no name wanted processName;
  639.                     infoRec.processAppSpec        =    nil;    //    no spec wanted &procSpec;
  640.                     
  641.                     if (FindAProcess(kFinderType, kSysCreator, &finderPSN, &infoRec) == noErr)
  642.                         QuitProcess(finderPSN, (**globH).hasExtensionsStrip);
  643.                     break;
  644.                     }
  645.                 
  646.                 case kSafeFinderQuit2:
  647.                     // request sdevInAppContext to be called once within Extensions Strip's context
  648.                     result = (1L<<sdevQueueModule);
  649.                     break;
  650.                                 
  651.                 case kCloseDown:
  652.                     result = (1L<<sdevCloseNow);
  653.                     break;
  654.             }    // end switch menuItem
  655.     }    // end switch menu id
  656.     
  657.     return(result);
  658. }
  659.  
  660. /******************************************************************************
  661.  sdevDraw: draw our icon and arrow pict
  662.  ******************************************************************************/
  663. void sdevDraw(MyGlobalHandle globH, Rect *statusRect, WindowRef statusPort)
  664. {
  665.     Rect        viewRect = *statusRect;
  666.     short        arrowHeight = 0;
  667.     PicHandle    myArrowPict = (**globH).myArrowPict;    // local copy to save deferencing
  668.     
  669.     //
  670.     // update the drag area (only required for Control/Desktop Strip)
  671.     //
  672.     viewRect.right = viewRect.left + (    (**globH).useLargeIcon ?
  673.                                         kLargeIconWidth : kSmallIconWidth    );
  674.     (**globH).myBox = viewRect;
  675.     
  676.     //
  677.     // Draw our icon
  678.     //
  679.     DrawMyIcon(globH, false);
  680.     
  681.     //
  682.     // Draw our right-arrow pict to show that we have a popup menu
  683.     //
  684.     if (myArrowPict)
  685.     {
  686.         short        arrowHeight;
  687.         
  688.         arrowHeight = height((*myArrowPict)->picFrame);
  689.         viewRect.left = viewRect.right;
  690.         viewRect.right += width((*myArrowPict)->picFrame);
  691.         viewRect.top += ((height(viewRect) - arrowHeight) >> 1);
  692.         viewRect.bottom = viewRect.top + arrowHeight;
  693.         DrawPicture(myArrowPict, &viewRect);
  694.     }
  695. }
  696.  
  697. void DrawMyIcon(MyGlobalHandle globH, Boolean selected)
  698. {
  699.     Rect        viewRect;
  700.     
  701.     if ((**globH).iconSuite)
  702.     {
  703.         // Get our icon's area
  704.         if ((**globH).hasExtensionsStrip) {
  705.             SBGetModuleBounds(&viewRect, (**globH).myReferenceNum);
  706.             viewRect.right = viewRect.left + (    (**globH).useLargeIcon ?
  707.                                                 kLargeIconWidth : kSmallIconWidth    );
  708.         } else
  709.             viewRect = (**globH).myBox;
  710.         
  711.         // Draw our icon
  712.         PlotIconSuite(&viewRect, atNone, selected ? ttSelected : ttNone,
  713.                       (**globH).iconSuite);
  714.     }
  715. }
  716.  
  717. /* The gestalt code: */
  718. #pragma mark -
  719.  
  720. /******************************************************************************
  721.  HasDragMgr: determine if the Drag Manager exists, and supports floaters
  722.  ******************************************************************************/
  723. Boolean    HasDragMgr(void)
  724. {
  725.     long    gestaltResponse;
  726.  
  727.     return(    (Gestalt(gestaltDragMgrAttr, &gestaltResponse) == noErr) &&
  728.             (gestaltResponse & (1L << gestaltDragMgrPresent)) &&
  729.             (gestaltResponse & (1L << gestaltDragMgrFloatingWind)) );
  730. }
  731.  
  732. /******************************************************************************
  733.  HasExtensionsStrip: determine if Extensions Strip is installed and running
  734.  ******************************************************************************/
  735. Boolean    HasExtensionsStrip(void)
  736. {
  737.     long    gestaltResponse;
  738.  
  739.     return(    (Gestalt(gestaltExtensionsStripAttr, &gestaltResponse) == noErr) &&
  740.             (gestaltResponse & (1L << gestaltExtensionsStripExists)) );
  741. }
  742.  
  743. /* The apple event code: */
  744. #pragma mark -
  745.  
  746. /******************************************************************************
  747.  FindAProcess: run through the process list looking for the indicated program
  748.  ******************************************************************************/
  749. OSErr FindAProcess(OSType typeToFind, OSType creatorToFind,
  750.                              ProcessSerialNumberPtr processSN,
  751.                              ProcessInfoRecPtr infoRecToFill)
  752. {
  753.     OSErr    myErr    =    noErr;
  754.  
  755.     processSN->lowLongOfPSN        =    kNoProcess;
  756.     processSN->highLongOfPSN    =    0;
  757.     do
  758.     {
  759.         myErr    =    GetNextProcess(processSN);
  760.         if (myErr == noErr)
  761.             GetProcessInformation(processSN, infoRecToFill);
  762.     }
  763.     while ( (myErr == noErr) && ((infoRecToFill->processSignature != creatorToFind) ||
  764.                                  (infoRecToFill->processType != typeToFind)) );
  765.     return(myErr);
  766. }
  767.  
  768. /******************************************************************************
  769.  QuitProcess: Send a quit event to the specified process.  If you are using the
  770.                program Extensions Strip the event will be sent safely from within
  771.                its Apple Event aware context.
  772.  ******************************************************************************/
  773. void QuitProcess (ProcessSerialNumber PSN, Boolean hasExtensionsStrip)
  774. {
  775.     AEAddressDesc    applicationAddress;
  776.     AppleEvent        theEvent, returnEvent;
  777.     OSErr            err;
  778.  
  779.     AECreateDesc(typeProcessSerialNumber, &PSN, sizeof(ProcessSerialNumber), &applicationAddress);
  780.     AECreateAppleEvent(kCoreEventClass, kAEQuitApplication, &applicationAddress,
  781.                         kAutoGenerateReturnID, kAnyTransactionID, &theEvent);
  782.     AEDisposeDesc(&applicationAddress);
  783.     if (hasExtensionsStrip) {
  784.         err = SBSimpleAESend(&theEvent);
  785.     } else {
  786.         err = AESend(&theEvent, &returnEvent,
  787.                     kAENoReply + kAEAlwaysInteract + kAECanSwitchLayer,
  788.                     kAENormalPriority,
  789.                     kAEDefaultTimeout, nil, nil);
  790.     }
  791.     if (err) {
  792.         // better error handling goes here
  793.         SysBeep(0);
  794.     }
  795.     AEDisposeDesc(&theEvent);
  796. }
  797.  
  798. /* The drag manager code: */
  799. #pragma mark -
  800.  
  801. /******************************************************************************
  802.  InstallDragHandlers: install & create everything having to do with drag handlers
  803.  ******************************************************************************/
  804. void InstallDragHandlers(WindowRef statusPort, MyGlobalHandle globH)
  805. {
  806.     // local copies
  807.     DragTrackingHandlerUPP    myTrackingUPP;
  808.     DragReceiveHandlerUPP    myReceiveUPP;
  809.     
  810.     if (HasDragMgr()) {
  811.         myTrackingUPP = NewDragTrackingHandlerProc(DragTrackingHandler);
  812.         myReceiveUPP = NewDragReceiveHandlerProc(DragReceiveDropHandler);
  813.         
  814.         if (myTrackingUPP && myReceiveUPP) {    // got UPPs successfully
  815.             InstallTrackingHandler(myTrackingUPP, statusPort, (void*) globH);
  816.             InstallReceiveHandler(myReceiveUPP, statusPort, (void*) globH);
  817.         }
  818.         
  819.         (**globH).myTrackingUPP = myTrackingUPP;
  820.         (**globH).myReceiveUPP = myReceiveUPP;
  821.     } else {
  822.         // disable the drag-related menu items
  823.         DisableItem((**globH).myMenuH, kUseDragNDrop);
  824.         DisableItem((**globH).myMenuH, kDragFiles);
  825.         DisableItem((**globH).myMenuH, kDragTextClippings);
  826.     }
  827. }
  828.  
  829. /******************************************************************************
  830.  RemoveDragHandlers: remove & dispose everything having to do with drag handlers
  831.  ******************************************************************************/
  832. void RemoveDragHandlers(WindowRef statusPort, MyGlobalHandle globH)
  833. {
  834.     // local copies
  835.     DragTrackingHandlerUPP    myTrackingUPP = (**globH).myTrackingUPP;
  836.     DragReceiveHandlerUPP    myReceiveUPP = (**globH).myReceiveUPP;
  837.     
  838.     if (myTrackingUPP) {
  839.         RemoveTrackingHandler(myTrackingUPP, statusPort);
  840.         DisposeRoutineDescriptor(myTrackingUPP);
  841.         (**globH).myTrackingUPP = 0L;
  842.     }
  843.     
  844.     if (myReceiveUPP) {
  845.         RemoveReceiveHandler(myReceiveUPP, statusPort);
  846.         DisposeRoutineDescriptor(myReceiveUPP);
  847.         (**globH).myReceiveUPP = 0L;
  848.     }
  849. }
  850.  
  851. /******************************************************************************
  852.  IsMouseInMyBounds: returns true if the mouse is over my total clickable area
  853.  ******************************************************************************/
  854. Boolean IsMouseInMyBounds(MyGlobalHandle globH, WindowRef win, DragReference theDrag)
  855.  {
  856.     Boolean        result = false;
  857.     Point        theMouse;
  858.     Rect        hotBounds;
  859.     
  860.     if (GetDragMouse(theDrag, &theMouse, 0L) == noErr) {
  861.         GlobalToLocal(&theMouse);
  862.         if ((**globH).hasExtensionsStrip)
  863.             SBGetTotalModuleBounds(&hotBounds, win, (**globH).myReferenceNum);
  864.         else
  865.             hotBounds = (**globH).myBox;
  866.         
  867.         result = PtInRect(theMouse, &hotBounds);
  868.     }
  869.     
  870.     return(result);
  871.  }
  872.  
  873. /******************************************************************************
  874.  DragTrackingHandler: called by the Drag Manager during drags on the strip
  875.  ******************************************************************************/
  876. pascal OSErr DragTrackingHandler(short message, WindowRef win, void *handlerRefCon,
  877.                                                         DragReference theDrag)
  878. {
  879.     MyGlobalHandle    globH = (MyGlobalHandle) handlerRefCon;
  880.     ItemReference    theItem;
  881.     FlavorType        theType;
  882.     unsigned short    count, index, numFlavors, i;
  883.     
  884.     switch (message)
  885.     {
  886.         case kDragTrackingEnterHandler:
  887.             // do special global initialization here
  888.             break;
  889.         
  890.         case kDragTrackingEnterWindow:
  891.             // determine if we can support at least one of the drag items
  892.             (**globH).canAcceptDrag = false;        // default to rejection
  893.             if (CountDragItems(theDrag, &count) == noErr)
  894.             {
  895.                 for (index = 1; index <= count; index++)    // check all the items
  896.                 {
  897.                     if ((GetDragItemReferenceNumber(theDrag, index, &theItem) == noErr) &&
  898.                      (CountDragItemFlavors(theDrag, theItem, &numFlavors) == noErr)) {
  899.                         
  900.                         for (i = 1; i <= numFlavors; i++)    //    check all the flavors
  901.                         {
  902.                             if ((GetFlavorType(theDrag, theItem, i, &theType) == noErr) &&
  903.                                 (((**globH).acceptFiles ? (theType == flavorTypeHFS) : 0) ||
  904.                                 ((**globH).acceptText ? (theType == 'TEXT') : 0)))
  905.                             {
  906.                                 (**globH).canAcceptDrag = true;    // I like this flavor
  907.                             }
  908.                         }
  909.                     }
  910.                 }
  911.             }
  912.             break;
  913.         
  914.         case kDragTrackingInWindow:
  915.             // check if the mouse is over me
  916.             if ((**globH).canAcceptDrag)
  917.                 if ((**globH).hasEntered != IsMouseInMyBounds(globH, win, theDrag))
  918.                 {
  919.                     (**globH).hasEntered = !(**globH).hasEntered;
  920.                     DrawMyIcon(globH, (**globH).hasEntered);
  921.                 }
  922.             break;
  923.         
  924.         case kDragTrackingLeaveWindow:
  925.             // if dragTrackingInWindow didn't handle the mouse leaving our area, do it here.
  926.             if ((**globH).hasEntered)
  927.             {
  928.                 (**globH).hasEntered = false;
  929.                 DrawMyIcon(globH, false);
  930.             }
  931.             break;
  932.         
  933.         case kDragTrackingLeaveHandler:
  934.             break;
  935.     }
  936.     
  937.     return((**globH).canAcceptDrag ? noErr : dragNotAcceptedErr);
  938. }
  939.  
  940. /******************************************************************************
  941.  DragReceiveDropHandler: called by the Drag Manager after a drag was dropped on the strip
  942.  ******************************************************************************/
  943. pascal OSErr DragReceiveDropHandler(WindowRef win, void *handlerRefCon,
  944.                                                 DragReference theDrag)
  945. {
  946.     MyGlobalHandle    globH    = (MyGlobalHandle) handlerRefCon;
  947.     OSErr            result    = dragNotAcceptedErr;    // default to cancel (do zoom-back animation)
  948.     
  949.     if ((**globH).hasEntered)
  950.     {
  951.         
  952.         //
  953.         // process the drag here:
  954.         //
  955.         SysBeep(0);
  956.         
  957.         result = noErr;        // accept the drag (so there won't be any zoom-back animation)
  958.     }
  959.     
  960.     return(result);
  961. }
  962.