home *** CD-ROM | disk | FTP | other *** search
/ PD ROM 1 / PD ROM Volume I - Macintosh Software from BMUG (1988).iso / Programming / Programming Tools / Pascal Demos from Apple / sinegrd⁄skelr / SKEL.TEXT < prev    next >
Encoding:
Text File  |  1985-06-03  |  28.8 KB  |  713 lines  |  [TEXT/ttxt]

  1. {%describe 'a Skeleton demo program'}
  2. {$X-}       {Turn automatic run-time stack expansion off - it's a Lisa concept.}
  3. {$R-}       {Turn off range checking}
  4.  
  5. PROGRAM Skel;
  6.  
  7. {          By    Steve Maker
  8.                  Courseware Development Group
  9.                  Kiewit Computation Center
  10.                  Dartmouth College
  11.                  Hanover, N.H.  03755
  12.  
  13.  
  14. Copyright and support notice:
  15.            SKEL may be copied and used by anyone, but not sold.
  16.            Please send comments and significant changes back to me so that I
  17.            may incorporate them into future versions.   However, please seek
  18.            support from your regular support channels; SKEL is distributed as-is.
  19.  
  20. Why SKEL?
  21.            Skel is a skeleton demo program.  Its purpose is to illustrate
  22.            in a clear fashion, isolated from any particular application,
  23.            the basic code for handling a simple Macintosh user interface.
  24.  
  25.            It strives to be correct as far as it goes, without many short-cuts
  26.            that would lead to trouble in larger applications.
  27.  
  28.            I think of SKEL as a program that does nothing, but does it well.
  29.  
  30. What does SKEL do?
  31.            It handles:
  32.                Events, carefully handling only those which are its
  33.                        business, and passing the others on to their
  34.                        respective handlers.
  35.                A Window, which is filled with Dark Gray, and can be
  36.                        activated or inactivated, updated, dragged
  37.                        and grown but NOT scrolled or closed.
  38.                Menus, including the Apple Menu.  An "About Skel" menu entry
  39.                        is provided.  A File menu offers Rattle and Frighten,
  40.                        which just invoke dialog boxes, and Quit.  Command
  41.                        key equivalents are supported.
  42.                The Desk Accessories, carefully supported in the Apple menu,
  43.                        and correctly meshed with the other features.
  44.                        NOT supported are Undo, Cut, Copy, Paste and Clear
  45.                        for desk accessories.
  46.                A Modal Dialog Box, used to communicate with the user.
  47.                Special icons for the application and its related files.
  48.                The Finder information (in the resource file).
  49.  
  50.            In accordance with Macintosh guidelines, everything possible is
  51.            kept in the resource file: window description, menus,
  52.            dialog specification, and the "About Skel" and other strings.
  53.  
  54.            In addition, the resource file handles the Bundle, File References,
  55.            and Icons that determine what Skel's icon looks like, and other
  56.            information for the Finder.
  57.  
  58. How do I use SKEL?
  59.            Study it.  Modify it to test your knowledge.  Steal working
  60.            pieces of code for your own programs.  Beat on it.  Subject
  61.            it to cruel and unusual experiments.  Pay heed to its warnings.
  62.  
  63. What do I study first in SKEL?
  64.            Initially you should ignore several sections of SKEL, and the calls
  65.            made to them.  I recommend X-ing them out in your listing.
  66.            The sections to ignore on the first round of study are:
  67.  
  68.                Report: ignore the implementation
  69.                RunDeskAcc: ignore all of it
  70.                SetUpMemory: ignore all of it
  71.                DrawWindow: ignore the scroll bar and grow icon handling
  72.                ReSize: ignore all of it
  73.                DoCommand: ignore the Desk Accessory handling in the Apple Menu
  74.                MainEventLoop:
  75.                   MouseDown handling: ignore inSysWindow, inDrag, inGrow
  76.                   keyDown, autoKey handling: ignore this.
  77.  
  78.            In the resource definition file, SKELR:
  79.                Finder information (offset by asterisks):
  80.                   ignore this whole section, icons and all.
  81.  
  82. What should I read in Inside Macintosh?
  83.            You should read the following sections of Inside Macintosh,
  84.            in the order given.  At first, just lightly skim the sections
  85.            with parenthesized names.  Read the others in some depth.  Read
  86.            the starred (*) ones in great detail.  Eventually, you will have
  87.            read all sections thoroughly, and many many times, I promise you.
  88.  
  89.            To start:
  90.              * Inside Macintosh: A Road Map
  91.               (User Interface Guidelines)
  92.                Structure of a Macintosh Application
  93.              * Putting Together a Macintosh Application
  94.  
  95.            Then, (low-level sections are listed first):
  96.             *  Memory Mgr Intro
  97.               (Memory Mgr)
  98.             *  Resource Mgr (through "Using the Resource Mgr")
  99.             *  QuickDraw
  100.               (Desk Mgr)
  101.             *  Event Mgr (through "Event Mgr routines")
  102.                Window Mgr
  103.                Menu Mgr
  104.               (Dialog Mgr)
  105.  
  106. How do I get SKEL to run?
  107.            Insert a Macintosh-formatted diskette into your Lisa.
  108.            Then run one of the following exec files:
  109.  
  110.                     <example/VanillaExec(example/Skel,'SKEL')
  111.            or
  112.                     <example/Exec(example/Skel,,,'SKEL')
  113.  
  114.  
  115. What are the funny % describes for?
  116.            They are formatting commands for a Pascal formatter used at Dartmouth
  117.            on Lisa Pascal code, for producing a readable listing.
  118.  
  119. What is the history of SKEL?
  120.       v1.0 July 14, 1984   sm: major revision of earlier version
  121.            Sept 30, 1984   sm: used \14 for apple symbol in res. file,
  122.                                bracketed OpenDeskAcc with Get and SetPort,
  123.            Oct 11, 1984   sm:  changed FREF, BNDL resources from HEXA
  124.                                   to readable,
  125.                                nested some routines in SKEL,
  126.                                added constants for FILE menu items,
  127.       v2.0 Nov 12, 1984   sm:  made resources pre-loaded and/or purgeable,
  128.                                turned off range-checking,
  129.                                documented no Resume proc passed to InitDialogs,
  130.                                added SetUpMemory:
  131.                                   calls MoreMasters, MaxApplZone,
  132.                                   sets the NIL address to -1,
  133.                                   lots of general memory doc.
  134.                                added a warning about passing doubly-
  135.                                   dereferenced handles,
  136.                                removed en/disabling of Rattle and Frighten items,
  137.       v2.1 Dec 4, 1984    sm:  added menu key handling,
  138.                                put Rattle and Frighten strings in res file,
  139.                                rewrote intro documentation
  140.  
  141.       v2.2 Mar 6, 1985    sm:  converted to % describes,
  142.                                fixed SKELX for both 2.0 and 3.0 workshop,
  143.                                set the event mask,
  144.       v2.3 Apr 8, 1985    sm:  fixed for Post-3.0 Pascal Workshop:
  145.                                 (uses clause, Skelx),
  146.       v2.4 May 9, 1985    sm:  SkelR: changed ICN# = HEXA to ICN#, since RMaker
  147.                                  now knows about icon lists, and pruned duplicate
  148.                                  TYPE statements,
  149.                                made windowptr params in grow, update, and activate
  150.                                  evt handling slightly more general,
  151.                                made the report box big enough for several lines
  152.                                  of text (dialog mgr will handle word wrap),
  153.                                added windowptr params in DrawWindow, etc,
  154.                                added info to "About Skel",
  155.                                added RunDeskAcc to run a desk acc; it now checks
  156.                                  memory carefully,
  157.                                cleaned up handling of About... and findWindow,
  158.  
  159. What is left to do in Skel?
  160.            - Support Edit menu, usable with desk accessories?
  161.  
  162. }
  163.  
  164.  {%describe '(declarations)'}
  165.  
  166.  
  167.    USES {$U-}                           {Turn off Lisa libraries}
  168.       {$U Obj/MemTypes    } MemTypes,   {use type defs in MemTypes unit}
  169.       {$U Obj/QuickDraw   } QuickDraw,  {Search "Obj/Quickdraw" for  }
  170.       {$U Obj/OSIntf      } OSIntf,     {  the "QuickDraw" unit, etc.}
  171.       {$U Obj/ToolIntf    } ToolIntf,
  172.  
  173.       {these are not needed for SKEL, but may be useful later.}
  174.       {$U Obj/PasLibIntf  } PasLibIntf, {New Macintosh PasLib.}
  175.       {$U Obj/PackIntf    } PackIntf,   {Packages}
  176.       {$U Obj/SaneLib     } Sane,       {new Floating Point}
  177.       {$U Obj/FixMath     } FixMath,    {fixed-pt math for graf3d}
  178.       {$U Obj/Graf3D      } Graf3D,     {3D graphics}
  179.       {$U Obj/MacPrint    } MacPrint,   {printing}
  180.       {$U Obj/SpeechIntf  } SpeechIntf; {Macintalk speech synthesis}
  181.  
  182.    CONST
  183.       lastMenu = 2;    { number of menus }
  184.       appleMenu = 1;          { menu ID for desk accessory menu }
  185.       fileMenu = 2;           { menu ID for File menu }
  186.  
  187.       iAbout = 1;             {items in the Apple menu}
  188.  
  189.       iRattle = 1;            {items in the File menu}
  190.       iFrighten = 2;
  191.       {--------}
  192.       iQuit = 4;
  193.  
  194.     VAR
  195.       screenPort: GrafPtr;         {a port for the whole screen}
  196.       myWindow: WindowPtr;         {our one window}
  197.       wRecord: WindowRecord;       {storage for window record}
  198.       dragRect: Rect;              {rect to drag within}
  199.       growRect: Rect;              {bounds for the growth of the windows}
  200.       myMenus: ARRAY [1..lastMenu] OF MenuHandle;   {our menus}
  201.  
  202.  
  203.  {%describe 'Print a string in dialog box'}
  204.   {############################   Report   #################################}
  205.  
  206.   {   We put up a dialog box, show the string, and wait for user to hit OK.
  207.  
  208.       It's important not to pass Report a de-referenced handle;
  209.    if Report were in another segment, loading it could cause a memory
  210.    compaction; the de-referenced handle could become invalid.  Watch out
  211.    for this and similar nasties everywhere in your program.
  212.    For more info, see the Memory Manager and the Segment Loader.
  213.  }
  214.  
  215.    PROCEDURE Report(reportstr: str255);
  216.       CONST RptBoxID = 257;   {ID of our report dialog in resource file}
  217.             rptText = 2;      {Item # of dialog's report text}
  218.  
  219.       VAR
  220.          itemhit: INTEGER;    {which Item was clicked on (only OK avail)}
  221.          ReportPtr: DialogPtr;
  222.  
  223.       BEGIN {Report}
  224.  
  225.          {set text to display}
  226.          ParamText(reportStr, '', '', '');
  227.  
  228.          ReportPtr := getNewDialog(RptBoxID, NIL, pointer(-1));    {get from Resource file;
  229.                                                                     NIL => use heap storage;
  230.                                                                     -1 => make dlg frontmost}
  231.          ModalDialog(NIL, itemHit);     {carry out dialog;
  232.                                          NIL => no FilterProc;
  233.                                          return item Hit when done}
  234.          DisposDialog(ReportPtr);        {release storage and remove dialog from screen}
  235.       END; {Report}
  236.  
  237.  
  238.  {%describe 'run a desk accessory'}
  239.   {############################   RunDeskAcc   #################################}
  240.  
  241.   {   Run the desk accessory with the passed menu item number
  242.    (in the menu whose handle is passed).
  243.    If we have any trouble, return FALSE, else TRUE.
  244.    We must check that there is enough memory to do so,
  245.    and save the port, since some da's may trash it.}
  246.  
  247.   FUNCTION RunDeskAcc(mHan: menuHandle; theItem: integer): boolean;
  248.  
  249.    CONST
  250.       extraMem = 2000;        {extra mem for desk acc to use}
  251.  
  252.    VAR
  253.       refNum: integer;
  254.       name: str255;
  255.       savePort: grafPtr;      {for saving current port in when opening a desk acc}
  256.       DAHan: handle;          {handle to the desk acc}
  257.       junkPtr: Ptr;           {just to alloc a ptr, then dispose}
  258.  
  259.    BEGIN   {RunDeskAcc}
  260.  
  261.       {assume the worst}
  262.       RunDeskAcc := false;
  263.  
  264.       {get name}
  265.       GetItem(mHan, theItem, name);
  266.  
  267.       {reserve room for the DA, if necessary}
  268.       setResLoad(false);                {don't load it in}
  269.       DAHan := GetNamedResource('DRVR', name);    {get handle}
  270.       setResLoad(true);
  271.       if DAHan^ = NIL         {if not in memory already, reserve room low in heap}
  272.         then begin  {reserve it; exit if not enough memory.
  273.                       ResrvMem just plain lies; it oftens returns
  274.                       "memFullErr" when there is really room.  Thus we
  275.                       use NewPtr because it sets memError correctly.}
  276.              junkPtr := NewPtr(extraMem + SizeResource(DAHan));
  277.              if memError = memFullErr
  278.                then exit(runDeskAcc)
  279.                else disposPtr(junkPtr); {make room for the DA}
  280.              end;
  281.  
  282.       {Run it; make sure port is preserved}
  283.       getPort(savePort);
  284.       refNum := OpenDeskAcc(name);
  285.       setPort(savePort);
  286.  
  287.       {success}
  288.       runDeskAcc := true;
  289.  
  290.    END; {RunDeskAcc}
  291.  
  292.  
  293.  {%describe 'Once-only initialization for Skel'}
  294.   {############################   SetUp   #################################}
  295.  
  296.   {   Initialize our program.  It seems best to handle:
  297.    Memory inits first, ToolBox inits second, then the program variables' inits.
  298.    Note that the order of inits is important; see "Using the Dialog Manager"
  299.    in the Dialog Mgr section.}
  300.  
  301.   {   By the way...you want to make sure this is in the same segment as your main
  302.    program (the blank segment) so that you don't create "holes" in memory.}
  303.  
  304.   PROCEDURE SetUp;
  305.  
  306.      CONST
  307.         WindowID = 260;       {Resource ID for my window}
  308.  
  309.      VAR
  310.         screenRect: rect;     {size of screen; could be machine-dependent}
  311.  
  312.  
  313.  {%describe 'SetUps for handling memory'}
  314.   {############################   SetUpMemory   #################################}
  315.   {   This very important set of initializations can be left out of the first
  316.    versions of a program. We are making sure that memory is laid out as
  317.    we desire, with adequate protection against running out of memory, bad
  318.    handles, etc.}
  319.  
  320. Procedure SetUpMemory;
  321.  
  322.    CONST
  323.          maxStackSize = 8192;     {max size of stack; the heap gets the rest.
  324.                                    Most appropriate for 128K Macs.}
  325.  
  326.    TYPE
  327.         loMemPtr = ^longint;      {a pointer to low memory locations}
  328.  
  329.    VAR
  330.        nilPtr: loMemPtr;          {will have value NIL}
  331.        stackBasePtr: loMemPtr;    {points to current stack base}
  332.  
  333.    BEGIN {SetUpMemory}
  334.  
  335.         {If you define a GrowZone function to handle bad memory problems,
  336.          you should define it at the top level (not nested), and set it here.
  337.          We don't.}
  338.      (*  SetGrowZone(@MyGrowZone);
  339.      *)
  340.  
  341.         {Place a longint -1 (an odd and therefore illegal address) in the
  342.          memory location that would be referenced by an accidentally-NIL
  343.          handle, so the error will be caught at handle-reference time (as
  344.          an Address error, ID=02) instead of later on.}
  345.         nilPtr := NIL;
  346.         nilPtr^ := -1;
  347.  
  348.         {If you needed to use an Application heap limit other than the default
  349.          (which allows 8K for the stack), you'd set it here, possible using this
  350.          technique of explicitly specifying the maximum stack size and allocating
  351.          the rest to the heap.  Should be independent of memory size. }
  352.         stackBasePtr := loMemPtr($908);    {CurStackBase from Tlasm/sysequ.text}
  353.         SetApplLimit( pointer(stackBasePtr^ - maxStackSize) );
  354.  
  355.         {Expand the application heap zone to its maximum size, without purging
  356.          any purgeable resources.  This saves memory compactions and heap expansions later.}
  357.         MaxApplZone;
  358.  
  359.         {get plenty of master pointers now; if we let the Memory Manager allocate
  360.          them as needed, they'd form non-relocatable islands in the heap.}
  361.         MoreMasters;  MoreMasters;  MoreMasters;
  362.  
  363.         {Here you might install bulwarks against running out of memory unexpectedly.
  364.          One such (cheesy) technique is to here allocate a large handle, call it
  365.          "CheeseBuf", which you can de-allocate in your GrowZone function, when
  366.          you must obtain more memory to avoid a crash.  While de-allocated,
  367.          the program could prevent the user from doing anything requiring memory,
  368.          and tell him he must discard windows or some such memory freeing action.
  369.          Each time he does so, the program can try to re-allocate CheeseBuf; if it
  370.          succeeds, the user can go on doing memory-eating operations.}
  371.  
  372.    END; {SetUpMemory}
  373.  
  374.  
  375.  {%describe 'Once-only initialization for menus'}
  376.   {############################   SetUpMenus   #################################}
  377.  
  378.   {   We read in all menus from the resource file, and install them,
  379.     and all desk accessories (drivers).}
  380.  
  381.   PROCEDURE SetUpMenus;
  382.  
  383.      VAR
  384.         i: INTEGER;
  385.  
  386.      BEGIN {SetUpMenus}
  387.         for i := 1 to lastMenu do    {get all my menus in}
  388.            myMenus[i] := GetMenu(i); {use the fact that our menu ID's start at 1}
  389.  
  390.         AddResMenu(myMenus[appleMenu], 'DRVR');    {pull in all desk accessories }
  391.  
  392.         for i := 1 to lastMenu do
  393.            InsertMenu(myMenus[i], 0);          {insert menus; 0 => put at end}
  394.  
  395.         DrawMenuBar;
  396.      END; {SetUpMenus }
  397.  
  398.  
  399.  {%describe '(body of SetUp)'}
  400.  
  401.      BEGIN {SetUp}
  402.  
  403.         {init memory layout and protection}
  404.         SetUpMemory;
  405.  
  406.         {init QuickDraw, and everybody else}
  407.         InitGraf(@thePort);
  408.         InitFonts;
  409.         InitWindows;
  410.         InitMenus;
  411.         TEInit;
  412.         InitDialogs(NIL); {NIL => no Restart proc; see Dialog Mgr and System Error Handler}
  413.         InitCursor;
  414.  
  415.         {Init the system event mask, in case the previous program left it in
  416.          a bad state.  If you set it non-standard here, FIX IT BEFORE EXITING,
  417.          because the Finder (1.1g) does NOT set it.}
  418.         SetEventMask(everyEvent - keyUpMask);     {standard setting}
  419.  
  420.         {Get the port which is the whole screen, to use when deactivating our window.
  421.            This prevents the current grafPort pointer from ever dangling.}
  422.         GetWMgrPort(screenPort);   {get whole screen port that window mgr uses}
  423.         SetPort(screenPort);       {and start off with it}
  424.  
  425.         {get window: use wRecord storage.  Port is set to that of the new window.
  426.             GetNewWindow posts an update event for the new window,
  427.             so it will be redrawn right away.}
  428.         myWindow := GetNewWindow(windowID, @wRecord, POINTER(-1));    {-1 => frontmost window}
  429.  
  430.         {set up dragRect; we can drag the window within it}
  431.         screenRect := screenBits.bounds;          {don't assume screen size}
  432.         {set drag rect to avoid menu bar, and keep at least 4 pixels on screen}
  433.         SetRect(dragRect, 4, 24, screenRect.right-4, screenRect.bottom-4);
  434.  
  435.         {set up GrowRect, for limits on window growing}
  436.         SetRect(growRect, 48, 14, screenRect.right-7, screenRect.bottom-7);
  437.  
  438.         {pull in and set up our menus}
  439.         SetUpMenus;
  440.  
  441.      END; {SetUp}
  442.  
  443.  
  444.  {%describe 'Redraw my window'}
  445.   {############################   DrawWindow   #################################}
  446.  
  447.   {   We draw all the contents of our one window, myWindow.  Note that this must
  448.    include scroll bar areas and the grow icon; the Window Manager will NOT handle
  449.    those for us. Since there are no scroll bars, we must erase the region they
  450.    would be in.  Echh.}
  451.  
  452. PROCEDURE DrawWindow(aWindow: WindowPtr);
  453.  
  454.    VAR aRect: rect; {rectangle to erase}
  455.  
  456.    BEGIN {DrawWindow}
  457.  
  458.       {We only handle myWindow}
  459.       if aWindow <> myWindow
  460.         then exit(DrawWindow);
  461.  
  462.       {first, fill the window with dark gray; this fills scroll bars, too}
  463.       FillRect(myWindow^.portRect, dkGray);
  464.  
  465.       {second, erase the scroll bars and draw the grow icon}
  466.  
  467.       {erase the horizontal scroll bar}
  468.       SetRect(aRect,          {cover the horizontal bar}
  469.               myWindow^.portRect.left,     myWindow^.portRect.bottom-15,
  470.               myWindow^.portRect.right-15, myWindow^.portRect.bottom);
  471.       FillRect(aRect, white);  {fill with white}
  472.  
  473.       {erase the vertical scroll bar}
  474.       SetRect(aRect,          {cover the vertical bar}
  475.               myWindow^.portRect.right-15, myWindow^.portRect.top,
  476.               myWindow^.portRect.right,    myWindow^.portRect.bottom-15);
  477.       FillRect(aRect, white); {fill with white}
  478.  
  479.       DrawGrowIcon(myWindow); {draw the size box in the corner of the window}
  480.  
  481.     END; {DrawWindow}
  482.  
  483.  
  484.  {%describe 'Update the contents of the given window'}
  485.   {############################   UpdateWindow   #################################}
  486.  
  487.   {   This is our response to receipt of an update event for myWindow.  Since the
  488.    window is likely to be inactive, the current grafPort will be elsewhere.  We
  489.    must change it for drawing, yet leave it as it was.}
  490.  
  491.    PROCEDURE UpdateWindow(aWindow: WindowPtr);
  492.  
  493.       VAR
  494.          savePort: GrafPtr;   {to save and restore the old port}
  495.  
  496.       BEGIN {UpdateWindow}
  497.  
  498.          BeginUpdate(aWindow); {reset ClipRgn etc to only redraw what's necessary.}
  499.  
  500.          GetPort(savePort);    {don't trash the port; we might be updating an inactive window}
  501.          SetPort(aWindow);     {work in the specified window}
  502.  
  503.          drawWindow(aWindow);           {redraw contents of window}
  504.  
  505.          SetPort(savePort);    {all nice and tidy as before}
  506.  
  507.          EndUpdate(aWindow);
  508.  
  509.       END; {UpdateWindow}
  510.  
  511.  
  512.   {%describe 'Change the size of the given window'}
  513.   {############################   ReSize   #################################}
  514.  
  515.   {   Called on a mouse-down in the grow box, this allows the user to change
  516.    the size of the window.  We change the size, then
  517.    claim that the whole of the window contents is no longer validly drawn,
  518.    because we're too lazy to do so for just the scroll bar regions
  519.    that are actually subject to change.  See the Window Manager.}
  520.  
  521. PROCEDURE ReSize(a_window : WindowPtr; downPt: Point);
  522.  
  523.    VAR w, h : integer;           {new width and height of the sized window}
  524.        newSize : longint;       {the new size}
  525.  
  526.    BEGIN {ReSize}
  527.  
  528.        newSize := GrowWindow(a_window, downPt, growRect); {find new size}
  529.        w := LoWord(newSize); {find the width}
  530.        h := HiWord(newSize); {find the height}
  531.  
  532.        SizeWindow(a_window, w, h, true); {change to the new window size}
  533.  
  534.        {place whole window into update region to be sure it all gets updated.
  535.         This is more than is strictly necessary, but a lot easier than just
  536.         invalidating the regions that actually may have changed.}
  537.        InvalRect(a_window^.portRect);
  538.  
  539.     END; {ReSize}
  540.  
  541.  
  542.  {%describe 'the main loop that handles events'}
  543.   {############################   MainEventLoop   #################################}
  544.  
  545.   {   Brace yourself: here's where the action is.  Most Mac programs just wait for events
  546.    (as do we all), and then process them.  There are two sorts of events: those directly
  547.    initiated by the user, like key presses and mouse-downs, and those consequent events
  548.    posted by the Event Manager, like update and activate events.  The latter MUST be handled
  549.    correctly and carefully.  In particular, it's important for all events to make sure
  550.    that the event occurred in or for the window you expect -- it's possible to get events
  551.    which are not for one of our windows, despite GetNextEvent's return value.  Similarly,
  552.    be sure to check that the window it occured in is the active one, if it matters.
  553.  
  554.       A common mistake in handling update and activate events is in finding out which window
  555.    they are for.  It is "WindowPtr(myEvent.message)" that gives this information,
  556.    NOT "whichWindow" (the WindowPtr returned by FindWindow).  The latter pointer merely tells
  557.    you what window the mouse was in at the time the event was posted -- completely irrelevant
  558.    for Update and Activate events.  Think it through carefully.}
  559.  
  560.    PROCEDURE MainEventLoop;
  561.       VAR
  562.           myEvent: EventRecord;
  563.           whichWindow: WindowPtr;       {points to window of MouseDown}
  564.           userDone: boolean;            {true when user wants to exit program}
  565.  
  566.  
  567.  {%describe 'handle a command given through a menu selection'}
  568.   {############################   DoCommand   #################################}
  569.  
  570.   {   We carry out the command indicated by mResult.
  571.    If it was Quit, we return true, else false.  Since the menu was highlighted by
  572.    MenuSelect, we must finish by unhighlighting it to indicate we're done.}
  573.  
  574. FUNCTION DoCommand(mResult: LongInt): boolean;
  575.  
  576.    CONST
  577.       AboutSkelId = 1;     {Resource ID of report strings}
  578.       RattleID = 2;
  579.       FrightenID = 3;
  580.       InsuffMemID = 4;
  581.  
  582.    VAR
  583.       theMenu, theItem: integer;
  584.       aStr: str255;         {a utility string}
  585.  
  586.    BEGIN  {DoCommand}
  587.       DoCommand := false;            {assume Quit not selected}
  588.       theMenu := HiWord(mResult);    {get the menu selected}
  589.       theItem := LoWord(mResult);    {... and the item of that menu}
  590.       CASE theMenu OF
  591.  
  592.          0: ;    {user made no selection; do nothing}
  593.  
  594.          appleMenu:
  595.             CASE theItem OF
  596.  
  597.                iAbout: begin;  {get string, and tell about Skel}
  598.                        aStr := GetString(AboutSkelId)^^;   {see comment in Report}
  599.                        report(aStr);
  600.                        end;
  601.  
  602.               otherwise {run the indicated desk acc}
  603.                         if not runDeskAcc(myMenus[appleMenu], theItem)
  604.                           then begin              {insuff memory; report it}
  605.                                aStr := GetString(InsuffMemID)^^;
  606.                                report(aStr);
  607.                                end;
  608.  
  609.             END; {AppleMenu case}
  610.  
  611.          fileMenu:
  612.             CASE theItem OF
  613.  
  614.                iRattle: begin;                              {Rattle}
  615.                         aStr := GetString(RattleId)^^;
  616.                         report(aStr);
  617.                         end;
  618.  
  619.                iFrighten: begin;                            {Frighten}
  620.                         aStr := GetString(FrightenId)^^;
  621.                         report(aStr);
  622.                         end;
  623.  
  624.                iQuit:     DoCommand := true                 {Quit}
  625.  
  626.             END; {fileMenu case}
  627.  
  628.       END; {menu case }
  629.  
  630.       HiliteMenu(0)        {turn off hilighting on the menu just used}
  631.  
  632.    END; {DoCommand }
  633.  
  634.  
  635.  
  636.  {%describe '(body of MainEventLoop)'}
  637.  
  638.    BEGIN  {MainEventLoop}
  639.  
  640.       FlushEvents(EveryEvent, 0);    {discard leftover events}
  641.  
  642.       {get next event, and handle it appropriately, until user QUITs}
  643.       userDone := false;
  644.       REPEAT
  645.  
  646.          systemTask;       {handle desk accessories}
  647.  
  648.          if GetNextEvent(everyEvent, myEvent)  {get event; if for us...}
  649.            then begin
  650.                 case myEvent.what of           {handle each kind of event}
  651.  
  652.                    mouseDown:
  653.                       begin
  654.                           {find what window the mouse went down in, and where in it}
  655.                           case FindWindow(myEvent.where, whichWindow) of
  656.  
  657.                             inSysWindow:          {handle the desk accessories}
  658.                                   SystemClick(myEvent, whichWindow);
  659.  
  660.                             inMenuBar:  {handle the command}
  661.                                   userDone := DoCommand(MenuSelect(myEvent.where));
  662.  
  663.                             inDrag:     {drag the window}
  664.                                   DragWindow(whichWindow, myEvent.where, dragRect);
  665.  
  666.                             inContent:   {includes inGrow if window inactive. Activate window}
  667.                                   if whichWindow = myWindow  {moke sure it's for mine}
  668.                                     then if whichWindow <> FrontWindow
  669.                                            then SelectWindow(whichWindow);   {make it active}
  670.  
  671.                             inGrow:  {window is already active; change its size}
  672.                                   if whichWindow = myWindow  {moke sure it's for mine}
  673.                                     then ReSize(whichWindow, myEvent.where);
  674.  
  675.                             inGoAway:   {we don't have a GoAway region}
  676.  
  677.                          end; {case on findWindow}
  678.                       end;  {mouseDown handling}
  679.  
  680.                    keyDown, autoKey:   {if command key, pass the char to MenuKey}
  681.                       if BitAnd(myEvent.modifiers, cmdKey) <> 0
  682.                         then userDone := DoCommand(MenuKey(chr(BitAnd(myEvent.message, charCodeMask))));
  683.  
  684.                    updateEvt: {if it's for our window, update it}
  685.                       if WindowPtr(myEvent.message) = myWindow
  686.                         then UpdateWindow(WindowPtr(myEvent.message));             {redraw the window contents}
  687.  
  688.                    activateEvt:      {if for our window, set port as nec.}
  689.                       if WindowPtr(myEvent.message) = myWindow       {my window}
  690.                         then begin
  691.                              DrawGrowIcon(WindowPtr(myEvent.message));     {redraw grow icon to reflect new state}
  692.                              if odd(myEvent.modifiers)   {odd means an activate event}
  693.                                then SetPort(WindowPtr(myEvent.message))    {activate evt: work in our own port}
  694.                                else SetPort(screenPort); {deactivate evt: our port is gone;
  695.                                                           keep port from dangling}
  696.                              end;
  697.  
  698.                 end;  {case myEvent.what}
  699.                 end;  {THEN BEGIN for "it's our event"}
  700.       UNTIL userDone;
  701.  
  702.    END; {MainEventLoop}
  703.  
  704.  
  705.  {%describe '(body of Skel)'}
  706.  
  707.    BEGIN {Skel}
  708.  
  709.       SetUp;
  710.       MainEventLoop;
  711.  
  712.    END. {Skel}
  713.