home *** CD-ROM | disk | FTP | other *** search
/ Celestin Apprentice 5 / Apprentice-Release5.iso / Source Code / C / Applications / Shutter 1.1 / Shutter.c < prev    next >
Encoding:
C/C++ Source or Header  |  1996-06-25  |  19.5 KB  |  492 lines  |  [TEXT/CWIE]

  1. /*
  2.     FILE: Shutter.c
  3.     
  4.     Main C Source Code
  5.     
  6.     This entire file is Copyright ©1996 Ross Younger. You are permitted to use portions 
  7.     in your own work, as long as you give me a mention in your finished product.
  8. */
  9.  
  10. #include "Shutter.h"
  11.  
  12. /*
  13.     I could quite happily have put the constants and prototypes into Shutter.h; if they needed to be accessed by multiple source 
  14.     files, I could have explicitly included the header into the project. Alternatively I could have put the entire contents of 
  15.     Shutter.h here. But that would have made this bit of the source too long and untidy in my opinion. So in the end, it is a 
  16.     compromise between avoiding the ultimate one-source file and keeping it all together in the same file where you can see it.
  17. */
  18.  
  19.  
  20. /*    CONSTANTS    */
  21. #define    myResType    'Pref'
  22. #define    myPrefsID    64
  23. #define    dMain        128
  24. #define    dPrefs        129
  25. #define    kErrorStrID    256
  26. #define    aProblems        512
  27. #define    kMinSpace        8192 /*    The amount of free space your application requires in order to run smoothly, as measured
  28.                             immediately after Setup. The only way to find out what's right is to torture test it yourself! 
  29.                             I tend to round up to the nearest 4K or so.                                        */
  30.  
  31. /*    GLOBALS    */
  32. PrefsType gPrefs;
  33.  
  34. /*    PROTOTYPES    (only the required ones)    */
  35. void AlertUser(short index);
  36. void SetBoolControl(DialogPtr dlog, short item, SInt16 value);
  37. void doPrefs (void);
  38. Boolean Sys7Check (void);
  39. Boolean TrapAvailable(short theTrap);
  40. TrapType GetTrapType (short theTrap);
  41. short NumToolboxTraps (void);
  42. void DrawDefaultButton(DialogPtr thisDlog, short thisItem);
  43. void HiliteAControl (DialogPtr theDlog, short theItem, short theValue);
  44. void Flash (DialogPtr theDlog, short theItem);
  45. /**********************************************************************************/
  46. //      M A I N      F U N C T I O N S
  47. /**********************************************************************************/
  48. void Setup(void)
  49. {
  50.     long reply, reply2;
  51.     for (reply=0; reply<4; reply++)
  52.         MoreMasters(); // Allocate 4 handle blocks for us to use.
  53.     // Unpack the Mac Toolbox now, but only what we need (to save memory).
  54.     InitGraf(&qd.thePort);
  55.     InitResources();
  56.     InitWindows();
  57.     InitDialogs(0L);
  58.     InitFonts();    
  59.     // Check for sys 7
  60.     if (!Sys7Check)
  61.         AlertUser(2);    // Call our abandon ship routine, telling it which string to use in the dialog
  62.     /* 
  63.         Call Gestalt for CPU type. We want >= MC68020. Check out "Gestalt.h" for details.
  64.         (Although 020 is not ESSENTIAL here, I check for it anyway, as an example of using Gestalt.)
  65.     */
  66.     Gestalt('cput', &reply); 
  67.     if (reply < 3)
  68.         AlertUser(3);
  69.     // Check the app's memory allocation
  70.     PurgeSpace(&reply, &reply2); // returns total mem, then max contiguous mem
  71.     if (reply < kMinSpace)
  72.         {
  73.         if (UnloadScrap() != noErr)        // If not enough, try unloading the scrap...
  74.             AlertUser(1);                // (exiting stage right if there is a problem with that)
  75.         else {
  76.             PurgeSpace(&reply, &reply2);    //... and check again.
  77.             if (reply < kMinSpace)
  78.                 AlertUser(1); 
  79.             }
  80.         }
  81.     FlushEvents(everyEvent, 0);
  82. }
  83. /**********************************************************************************/
  84. void GetPrefsFromResource(void)    // Gets the prefs from the resource (within the app) & copies to global (structured) variable.
  85. {
  86.     Handle myRes=GetResource(myResType, myPrefsID);
  87.     HLock(myRes);    /*     Lock the handle so it can't move about and become discontiguous, 
  88.                     which would cause all sorts of nasty gremlins when we do the BlockMove!     */
  89.     BlockMove(*myRes, &gPrefs, sizeof(gPrefs));
  90.         // BlockMove takes WhereFrom (as a Ptr), WhereTo (a Ptr again), Size in bytes.
  91.     HUnlock(myRes); // Don't leave the handle locked! 
  92.     DetachResource(myRes); // This makes the handle "forget" that it's a particular resource in the app
  93.     DisposeHandle(myRes); // … and this deallocates the handle completely.
  94.  
  95. }
  96. /*
  97.     If you are accessing a file that is not the application itself, you will need to mess around with FSpOpenResFile, CloseResFile, 
  98.     etc, and perhaps the Standard File package. There's probably some code out there which will show you how, but not in this
  99.     project. The caveat from using resources within the app is that you cannot write to them on a locked disk.
  100.     
  101.     Ideally all applications should NEVER edit themselves. This should be a major target of yours. Use a prefs file instead. 
  102.     Despite the fact that this one doesn't. :-)
  103.     
  104. */
  105. /**********************************************************************************/
  106. void SavePrefsIntoResource(void)    // Saves the prefs back into the prefs resource.
  107. {
  108.     Handle myRes=GetResource(myResType, myPrefsID);
  109.     HLock(myRes);    /*     Lock the handle so it can't move about and become discontiguous, 
  110.                     which would cause all sorts of nasty gremlins when we do the BlockMove!     */
  111.     
  112.     BlockMove(&gPrefs, *myRes, sizeof(gPrefs));
  113.     // Notice the striking similarity between the above line and one in the GetPrefsFromResource function!
  114.     
  115.     HUnlock(myRes); // Don't leave the handle locked! 
  116.     
  117.     ChangedResource(myRes); // Mark the resource as changed
  118.     WriteResource(myRes); // Save the data
  119.     if (ResError())
  120.         {
  121.         SysBeep(0);
  122.         DisposeHandle(myRes); // If we get a resource error (probably to say that the app is locked or is on a locked disk)
  123.         return;    // then deallocate the memory used by the handle, and forget about it quietly. In your program you might
  124.                 // want to put up an error message (via AlertUser or something similar) at this point.
  125.         }
  126.     DetachResource(myRes); // Remove the handle from its file
  127.     DisposeHandle(myRes); // … and deallocate it.
  128.     UpdateResFile(CurResFile()); // Save the change.
  129. }
  130.     /* * * DANGER * * *
  131.     This method of accessing resources is dangerous, in that if the program quits unexpectedly (or crashes) it may not be able to
  132.     complete the update of the resource (due to the disk cache), and that may damage the application. Always keep a backup.
  133.     This is another reason why you should use a prefs file!
  134.     */
  135. /**********************************************************************************/
  136. pascal Boolean DialogFilterProc (DialogRef theDialog, EventRecord *theEvt, short *itemHit)
  137. {
  138.     Boolean    filtered=false;
  139.     const short    eventKind = (*theEvt).what;
  140.     const char        theKey = (char)((*theEvt).message & charCodeMask); 
  141.     // The above line converts ALL keypresses to single char ASCII, even modified ones such as Command-keystrokes.
  142.     // To check for Command, etc, use an expression like (theEvt.modifiers && cmdKey).
  143.     
  144.     if (eventKind == keyDown)        // If we get a key depressed message, activate the hilite of the item.
  145.         {                        // Note that we are NOT interested in autoKey's here, but *you* might be.
  146.         filtered=true;
  147.         switch (theKey)
  148.             {
  149.             case 'Y': case 'y': // Y for Yes
  150.             case 13: case 3:  // [or Return, or Enter... Enter returns ASCII 3, btw]
  151.                 Flash (theDialog, 1);
  152.                 *itemHit=1;
  153.                 break;
  154.             case 'N': case 'n': // N for No(vember)
  155.                 Flash (theDialog, 2);
  156.                 *itemHit=2;
  157.                 break;
  158.             case 'S': case 's': // S for Shutdown
  159.                 Flash (theDialog, 4);
  160.                 *itemHit=4;
  161.                 break;
  162.             case 'R': case 'r': // R for Restart
  163.                 Flash (theDialog, 5);
  164.                 *itemHit=5;
  165.                 break;
  166.             default: // other, unsupported key
  167.                 filtered=false;
  168.                 break;
  169.             }
  170.         }
  171.     /* 
  172.         I suppose I *could* look for H for Help, but I don't know if that's all that 
  173.         much use from the keyboard, since it only does an HMSetBalloons(). And 
  174.         balloons depend on the mouse position anyway.
  175.     */
  176.     
  177.     else if (eventKind == updateEvt)        // If we're told to update
  178.         DrawDefaultButton (theDialog, 1);     // ...redraw this round rect that we added ourselves. 
  179.                                     // (the ModalDialog trap takes care of redrawing the dialog items)
  180.         // BTW, we don't return true for this event since the system dialog handler might want it.
  181.     
  182.     return (filtered);    // If true, the event is not processed any further.
  183. }
  184.  
  185. /**********************************************************************************/
  186. void RunDialog (void)     // Runs the main dialog
  187. {
  188.     Boolean     shutDown = (gPrefs.myPrefsShort & 0x0001); // Logical 'AND'. This takes 0x0001 as Shutdown default, 
  189.                                                 // and 0x0000 as Restart default.
  190.     Boolean     exit=false;
  191.     Boolean     cancelled=false;
  192.     short     theItem=0;
  193.     Boolean     origBalloons=HMGetBalloons();    // Get (& store) current 
  194.     Boolean     curBalloons=origBalloons;        // Balloon Help state
  195.  
  196.     CGrafPtr     saveP;    // So we can restore the original GWorld before exiting this fn. 
  197.     GDHandle     saveD;    // (Not necessary, since there is no window underneath, but good manners anyway.)
  198.     
  199.     DialogRef     theDlog = GetNewDialog (dMain, 0L, (WindowPtr) -1); // Dialog ID, Dialog storage, Behind What (-1 = nothing)
  200.     
  201.     GetGWorld(&saveP, &saveD);                    // Store current GWorld (see above)
  202.     SetGWorld((CGrafPtr)theDlog, GetMainDevice());    // Set current GWorld to be the dlog
  203.     
  204.     if (theDlog==0L) // if no dialog resource or not enough memory, then…
  205.         {
  206.         SysBeep(1);     // Warn user…
  207.         ExitToShell();     // and bail out. This shouldn't happen after the program has been developed.
  208.         }
  209.  
  210.     // We could use a DrawDialog(theDlog) to draw the dialog's items, but this is taken care of, at least the first time.
  211.     DrawDefaultButton(theDlog, 1);        // Draw its default button ourselves
  212.  
  213.     SetBoolControl(theDlog, 4, shutDown);    // Hilite the default option
  214.     SetBoolControl(theDlog, 5, !shutDown);    // …and not the other.
  215.     do    {
  216.         ModalDialog(DialogFilterProc, &theItem);    /*     Run the dialog, and return the item clicked. 
  217.                                             (NB. Only enabled items [thru ResEdit] are returned)    */
  218.         switch (theItem)
  219.             {
  220.             case 1: // Yes button
  221.                 exit=true;
  222.                 cancelled=false;
  223.                 break;
  224.             case 2: // No button
  225.                 exit=true;
  226.                 cancelled=true;
  227.                 break;
  228.             case 3: // Help button (toggles Balloon Help)
  229.                 curBalloons=HMGetBalloons();
  230.                 curBalloons=!curBalloons;
  231.                 HMSetBalloons(curBalloons);
  232.                 break;
  233.             case 4: // Shut Down [radio]
  234.                 shutDown=true;
  235.                 SetBoolControl(theDlog, 4, shutDown);
  236.                 SetBoolControl(theDlog, 5, !shutDown);
  237.                 break;
  238.              case 5: // Restart [radio]
  239.                 shutDown=false;
  240.                 SetBoolControl(theDlog, 4, shutDown);
  241.                 SetBoolControl(theDlog, 5, !shutDown);
  242.                 break;
  243.             case 7: // Logo [which triggers an easter egg]
  244.                 doPrefs();
  245.                 break;
  246.             }
  247.         } while (!exit);
  248.  
  249.     HMSetBalloons(origBalloons);    // Restore Balloon Help's original state.
  250.     
  251.     SetGWorld(saveP, saveD);    // Restore original GWorld
  252.     DisposeDialog(theDlog);        // Get rid of the dlog to free up the memory we used
  253.  
  254.     // Now react to the user's choice.
  255.     if (!cancelled)                // If not cancelled…
  256.         if (shutDown)
  257.             ShutDwnPower();    // Shut down the Mac, or
  258.         else    ShutDwnStart();    // Restart, as applicable.
  259. }
  260. /**********************************************************************************/
  261. pascal Boolean prefsDialogFilter (DialogRef theDialog, EventRecord *theEvt, short *itemHit)
  262. {
  263.     if ((*theEvt).what == updateEvt)
  264.         DrawDefaultButton(theDialog, 1); // This is the only thing we need to update.
  265.         // We do not return true from this so the system can update the rest.
  266.     return (false);
  267. /*    There _could_ be another keypress catching routine here. */
  268. }
  269. /**********************************************************************************/
  270. void doPrefs (void) // Runs the Easter Egg & prefs dialog
  271. {
  272.     Boolean    exit=false;
  273.     Boolean    myPref = (gPrefs.myPrefsShort && 0x0001); // Logical AND again. 0x0001 (or true) for Shut Down.
  274.     short    theItem=0;
  275.     CGrafPtr    saveP; // These two ARE needed this time because we want to go back
  276.     GDHandle    saveD; // to the dialog underneath when we're finished.
  277.     DialogRef    myDialog = GetNewDialog (dPrefs, 0L, (WindowPtr) -1);
  278.     
  279.     GetGWorld(&saveP, &saveD);                    // Store current GWorld
  280.     SetGWorld((CGrafPtr)myDialog, GetMainDevice());    // Set current GWorld to be the dlog
  281.     
  282.     DrawDialog (myDialog);
  283.     
  284.     if (myDialog==0L) // if no dialog resource or not enough memory, then…
  285.         {
  286.         SysBeep(1);     // Warn user…
  287.         ExitToShell();     // and bail out. This shouldn't happen after the program has been developed.
  288.         }
  289.     SetBoolControl(myDialog, 3, myPref); // Set current pref
  290.     SetBoolControl(myDialog, 4, !myPref); // and not the other.
  291.         
  292.     
  293.     do    {
  294.         ModalDialog (prefsDialogFilter, &theItem); 
  295.         switch (theItem)
  296.             {
  297.             case 1: // OK
  298.                 exit=true;
  299.                 break;
  300.  
  301.             case 3: // Shut Down button
  302.                 myPref=true;
  303.                 SetBoolControl(myDialog, 3, true);
  304.                 SetBoolControl(myDialog, 4, false); 
  305.                 break;
  306.  
  307.             case 4: // Restart button
  308.                 myPref=false;
  309.                 SetBoolControl(myDialog, 3, false);
  310.                 SetBoolControl(myDialog, 4, true); 
  311.                 break;
  312.             }
  313.         
  314.         } while (!exit);
  315.     
  316.     SetGWorld(saveP, saveD);    // Restore original GWorld
  317.     DisposeDialog(myDialog);        // Get rid of the dlog to free up the memory we used
  318.     
  319.     if (myPref)
  320.         gPrefs.myPrefsShort = 0x0001;
  321.     else    gPrefs.myPrefsShort = 0x0000;
  322.     
  323. //    gPrefs.myPrefsShort = ( (myPref) ? 0x0001 : 0x0000 ); 
  324.     /*
  325.         The above line probably appears to be double-Dutch [or treble-COBOL :-) ] at first. But fret not. All it does is set up my 
  326.         global prefs variable with the new state of the default. 
  327.         
  328.         The line is an example of an inline, and it is equivalent to:
  329.  
  330.             {
  331.                 if (myPref)
  332.                     gPrefs.myPrefsShort = 0x0001;
  333.                 else    gPrefs.myPrefsShort = 0x0000;
  334.             }
  335.         
  336.         See? Generally, the syntax is ( (condition) ? valueIfTrue : valueIfFalse ). The values _can_ be snippets of code, but this 
  337.         may slow things down.
  338.  
  339.         Looks aside, the only difference is that an inline is compiled into an awful lot less code, and is a lot faster. 
  340.         NB.     Some compilers do not allow inlines, in which case you will need to replace the inline line with the commented 
  341.             block of code. Some compilers will accept inlines but not compile them as such. Some (eg MetroWerks) have an 
  342.             option to turn inlining on or off.
  343.     
  344.     */
  345.     
  346.     SavePrefsIntoResource(); // This does exactly what it says!
  347. }
  348. /**********************************************************************************/
  349. void main(void)
  350. {
  351.     MaxApplZone();
  352.     // We could check the option key or command key here to show prefs dialog. But I'm just accessing it from the main dialog.
  353.     // It would be something to do with the OAPP AppleEvent and (eventRecord.modifiers && cmdKey), I think.
  354.     Setup();
  355.     GetPrefsFromResource();
  356.     RunDialog();
  357.     /*
  358.         I usually put a CleanUp() routine in, which takes down any special handlers, or 
  359.         anything else which needs to be taken down, but there's nothing to clean up here.
  360.     */
  361.  
  362.     // The end! The prog quits automatically.
  363. }
  364. //*****************************************************************************************
  365. //    D I A L O G   U T I L I T I E S
  366. //*****************************************************************************************
  367. void AlertUser(short index)                // This fn is the ULTIMATE bail-out for SERIOUS errors.
  368. {
  369.     Str255 str1;
  370.     short dummy;
  371.     GetIndString(str1, kErrorStrID, index);    // Taken out of the STR# resource with ID index. No string and we're dead!
  372.     ParamText(str1, "\p", "\p", "\p");        // Set it up as parameter ^0, with parms ^1, ^2 and ^3 as empty [pascal] strings.
  373.     dummy=Alert(aProblems, 0);            // Run the standard alert. It gives a return value of the item hit, 
  374.                                     // but we ignore this as there's only one.
  375.     ExitToShell();                        // Scotty, get us out of here!
  376. }
  377. //*****************************************************************************************
  378. void SetBoolControl(DialogPtr dlog, short item, short value)    // Sets the value of a boolean control item (radio button, check box)
  379. {
  380.     Handle itemHandle;
  381.     short itemType;
  382.     Rect itemRect;
  383.     
  384.     GetDialogItem(dlog, item, &itemType, &itemHandle, &itemRect);    // Grab a handle to it, etc
  385.     if ((itemType != statText) && (itemType != editText))    // disallow text items
  386.         SetCtlValue((ControlHandle)itemHandle, value);        // Change the value now. (1=on, 0=off)
  387.     else SysBeep(1);            /*    Warn, if the item is of an incorrect type
  388.                                 This line should NEVER need to be called in the finished program!    */
  389. }
  390. //*****************************************************************************************
  391. void HiliteAControl (DialogPtr theDlog, short theItem, short theValue)
  392. {
  393.     Handle itemHandle;
  394.     short itemType;
  395.     Rect rect;
  396.     
  397.     GetDialogItem(theDlog, theItem, &itemType, &itemHandle, &rect);    // Get item's handle, etc, etc
  398.  
  399.     if ((itemType != statText) && (itemType != editText))    // disallow text items
  400.         HiliteControl( (ControlRef) itemHandle, theValue);    // Hilite as appropriate (1=on, 0=off)
  401.     else    SysBeep(1); /* Need a warning if its not the right item type. (Only control items can be hilited.)
  402.                     A FINISHED PROGRAM SHOULD NEVER SEE THIS LINE EXECUTED!    */
  403. }
  404. //***************************************************************************************************
  405. Rect GetItemsBox(DialogRef theDialog, short itemNo)        // Gets the surrounding Rect of a dialog item
  406. {
  407.     short itemType=0;
  408.     Handle item=0L;
  409.     Rect Box;
  410.     GetDialogItem(theDialog, itemNo, &itemType, &item, &Box);
  411.     return(Box);
  412. }
  413. //***************************************************************************************************
  414. void DrawDefaultButton(DialogPtr thisDlog, short thisItem)
  415. {                 // Draws that nice round Rect around a dialog's default item
  416.     Rect         myRect;
  417.     RGBColor    theColour;
  418.     
  419.     theColour.red=theColour.green=theColour.blue=0L; // Black is 0/65535 red, same green, same blue.
  420.     RGBForeColor(&theColour);        // Set the fg colour to black.
  421.     
  422.     myRect = GetItemsBox (thisDlog, thisItem);        
  423.     InsetRect(&myRect, (short)-4, (short)-4);        // Push the rect out 4 pixels either way to clear the button
  424.     PenSize(3, 3);                                // THICK item rect.
  425.     FrameRoundRect(&myRect, 15, 15);                // Draw it.
  426.     /*
  427.         This assumes that thisDlog is set as the current GWorld. If its not then that causes probs. 
  428.         (We _could_ program our way round this if necessary.)
  429.     */
  430. }
  431. //*****************************************************************************************
  432. void Flash (DialogPtr theDlog, short theItem)    // Flashes a control's hilite on & off
  433. {
  434.     Handle itemHandle;
  435.     short itemType;
  436.     Rect rect;
  437.     long dummy; // This is ignored but we need somewhere to put the data returned from the sys call.
  438.     
  439.     GetDialogItem(theDlog, theItem, &itemType, &itemHandle, &rect);    // Get item's handle, etc, etc
  440.  
  441.     if ((itemType==statText) || (itemType==editText))
  442.         {
  443.         SysBeep(1);/*    Need a warning if its not the right item type. (Only control items can be hilited.)
  444.                     A FINISHED PROGRAM SHOULD NEVER SEE THIS LINE EXECUTED!    */
  445.         return;
  446.         }
  447.     HiliteControl((ControlRef)itemHandle, 1);    // Hilite on
  448.     Delay(10, &dummy);                // hang around for 1/6 second (10 ticks)
  449.     HiliteControl((ControlRef)itemHandle, 0);    // Hilite off
  450. }
  451. //*****************************************************************************************
  452. //    S Y S T E M   7   C H E C K
  453. //*****************************************************************************************
  454. /*
  455.     The next bit of this program is an autonomous System 7 check. Just call Sys7Check() and it will return true if
  456.     System 7.0 or greater is present. Oh, and don't forget its prototypes.
  457. */
  458. Boolean Sys7Check(void)
  459. {
  460.     long sysVersion;
  461.     if (!TrapAvailable(_Gestalt)) return (false);
  462.     if (!Gestalt(gestaltSystemVersion, &sysVersion)) 
  463.         {
  464.         if (sysVersion >= 0x0700) return(true);
  465.         }
  466.     return(false);
  467. }
  468. //*****************************************************************************************
  469. Boolean TrapAvailable(short theTrap)
  470. {
  471.     TrapType tType=GetTrapType(theTrap);
  472.     if (tType==ToolTrap) {
  473.         theTrap = (theTrap & 0x07FF);
  474.         if (theTrap>=NumToolBoxTraps()) theTrap=_Unimplemented;
  475.         }
  476.     return (NGetTrapAddress(theTrap, tType) != NGetTrapAddress(_Unimplemented, ToolTrap));
  477. }
  478. //*****************************************************************************************
  479. TrapType GetTrapType (short theTrap)
  480. {
  481.     if ((theTrap & 0x0800)>0) return(ToolTrap);
  482.     else return(OSTrap);
  483. }
  484. //*****************************************************************************************
  485. short NumToolboxTraps (void)
  486. {
  487.     if (NGetTrapAddress(_InitGraf, ToolTrap) == NGetTrapAddress(0xAA6E, ToolTrap)) return (0x0200);
  488.     else return (0x0400);
  489. }
  490. //*****************************************************************************************
  491. //*****************************************************************************************
  492. // The end. To be continued...?