home *** CD-ROM | disk | FTP | other *** search
- /*
- • This THINK C source code, project file, and resource file were created by
- • Dr. Craig Stone, Professor of Nuclear Chemistry at San Jose State
- • University, California USA, for the Macintosh Scientific and Technical
- • Users Association (MacSciTech), to accompany his article "Quickstart
- • Macintosh Programming" which appeared in the SciTech Journal of May, 1993.
- • Supplemental commenting was added by MacSciTech. The author
- • can be reached on Applelink at Stone.C. MacSciTech can be reached on
- • Applelink at MacSciTech, on America Online at SciTechMac, and on the
- • Internet at SciTechMac@aol.com or attila.nrl.navy.mil. You are encouraged
- • to use, modify, and improve this source code without restriction. If you
- • distribute this source code essentially unmodified and for public
- • consumption (e.g., you post it to a BBS), MacSciTech does request that
- • you leave the comments intact and keep all the project files together:
- •
- • About.c
- • Elements.c
- • MacSciTechDemo.c
- • MenuControl.c
- • Periodic.c
- • WindowControl.c
- • MST_Defines.h
- • MST_Externs.h
- • MST_Globals.h
- • MST_Menus.h
- • MST_Prototypes.h
- • MSTPChart.π
- • MSTPChart.π.rsrc
- •
- • All functions are "marked," so you can quickly navigate to any function
- • in a file by pressing the mouse down in the title bar of the source
- • code window while holding down the command key. This presents you with
- • a pop-up menu of all the functions. Similarly, all the include files
- • are available by holding down the option key while pressing the mouse
- • down in the title bar.
- •
- */
-
-
-
- /*
- • This file contains the function "main", which is the point at which
- • our program begins execution.
- */
-
- /*
- • We include two files up front. The file MST_Defines.h contains our constants.
- • MST_Globals.h defines (i.e., declares the type of and allocates
- • storage for) our global variables.
- */
-
- #include "MST_Defines.h"
- #include "MST_Globals.h"
-
-
- void Init_Variables (void); /*Declare Init_Variables() here for aesthetic reasons:*/
- /*main() can refer to it, yet we can place main() at*/
- /*the top of the source file where it is handy.*/
-
-
- /*_____________________________________________________________________________
- main()- main program
- _____________________________________________________________________________*/
-
- void main()
- {
-
- /*
- • Any program does two things. It initializes itself, and then enters into
- • an event loop until the user asks it to quit. Often the main() function
- • of a program places the initialization routines in a separate source
- • code file, and the event loop in another file, so that the main()
- • code is exceptionally short. Since our program is not very complex, we
- • will keep most of the initialization and the event loop in main().
- •
- • Here we declare our variables for main(). The variables are:
- •
- • stillInGoAway
- • thisChar
- • thisCode
- • thisMenu
- • thisItem
- • MainKey
- • thisResult Store information about the menu item selected here
- • thisRect A boundary rectangle so we cannot drag our window off the screen
- • theInput
- • thisEvent
- • thisWindow
- */
-
- char stillInGoAway, thisChar;
- short thisCode, thisMenu, thisItem, MainKey;
- long thisResult;
- Rect thisRect;
- TEHandle theInput;
- EventRecord thisEvent;
- WindowPtr thisWindow;
- /*
- • ----------------------------Program Initialization----------------------
- • First, we have to initialize all the Macintosh Toolbox Managers that we
- • will need. These initializations MUST be performed once, and ONLY once,
- • in your application. You would NOT initialize the managers if you were
- • writing a piece of code, such as an XCMD or INIT, which is not a
- • stand-alone application. It is also customary to flush any events which
- • may be pending in the event queue. These Toolbox initialization calls should
- • appear in exactly the order shown below.
- •
- */
- InitGraf (&thePort); /*Initialize Quickdraw*/
- InitFonts (); /*Initialize the Font Manager*/
- FlushEvents (everyEvent,0);
- InitWindows (); /*Initialize the Window Manager*/
- InitMenus (); /*Initialize the Menu Manager*/
- TEInit (); /*Initialize TextEdit*/
- InitDialogs (NIL); /*Initialize the Dialog Manager*/
- InitCursor (); /*Initialize the cursor*/
- /*
- •
- • Next we do whatever initialization is peculiar to our program. In our
- • case, we set up our menus and initialize our global and main() variables.
- • Init_ApplicationMenus() is in the file MenuControl.c, while Init_Variables()
- • is listed in this file after main().
- •
- */
- Init_ApplicationMenus();
- Init_Variables();
- Init_Windows();
-
- Quitting = FALSE; /*We don't want to quit yet!*/
- theInput = NIL;
-
- /*
- •
- • Typically a program would also at this point find out information about
- • the hardware and system software environment currently running it. This
- • information is necessary so the program can either adjust to the
- • environment, or advise the user that it cannot adjust to the environment,
- • and gracefully quit.
- •
- • For example, some things your program might want to know are: the number
- • of monitors, color/grayscale/monochrome, depth of color or grayscale,
- • ROM version, system version, and HFS or MFS file system.
- •
- • Our program here assumes at least System 7 and a color monitor. A monochrome
- • monitor will not display the periodic table grid.
- •
- */
-
- /*
- • ----------------------------Main Event Loop----------------------------
- */
-
- do
- {
- /*
- • We call our function Do_TEIdle, which itself calls the Toolbox function
- • TEIdle so that any TextEdit insertion points will blink properly. We don't
- • have any need for this in this application. Here, we choose to remove all
- • functionality from Do_TEIdle() in WindowControl.c. Alternatively, we
- • could have just removed the call to Do_TEIdle() instead.
- */
- Do_TEIdle (theInput);
-
- /*
- • Now call the Toolbox function WaitNextEvent, which is the core of every
- • Macintosh program. WaitNextEvent places the next event record into
- • thisEvent and pops that event off of the "First in, first out" event queue.
- • "everyEvent" is a system constant which tells WaitNextEvent we want to
- • retrieve all events, no matter what kind. Activate/deactivate events are
- • first priority, while update events are given last priority. (Update
- • events are generated by the system to tell us part of the screen needs
- • redrawing; activate/deactivate events tell us the front-most window
- • is changing.) 30 is the maximum number of ticks we are willing to be suspended
- • in the background without at least being passed a null event from the system.
- */
-
-
- if (WaitNextEvent (everyEvent,&thisEvent,30,NIL))
- {
- /*
- • Now we determine what the user did, and respond accordingly. Constants
- • such as mouseDown and keyDown are defined by the Macintosh Toolbox. THINK C
- • implements them exactly as shown in Inside Macintosh, even though
- • the convention in C is to name constants in all upper case.
- */
- thisCode = FindWindow (thisEvent.where, &thisWindow);
-
- switch (thisEvent.what)
- {
- case mouseDown: /*If the event was a mousedown*/
- {
- /*
- • In what part of the screen or window did the mouse event occur?
- • (Toolbox constants for screen parts include: inMenuBar,
- • inDrag (the drag region of an application window), inGoAway (the
- • close box of an application window), inContent (in the content region
- • of an application window), and inSysWindow (in a desk accessory window)).
- */
-
- if (thisCode == inMenuBar)
- {
- /*
- • If the mouseDown event was in the menubar, we decode it using the Toolbox
- • function MenuSelect, and pass the results to our menu handling routine.
- • MenuSelect also takes care of highlighting and flashing the menu items.
- */
- thisResult = MenuSelect(thisEvent.where);
- thisMenu = HiWord (thisResult);
- thisItem = LoWord (thisResult);
- Handle_TheMenus (thisMenu,thisItem);
- }
-
- else if ((thisCode == inDrag)&&(thisWindow != NIL))
- {
- /*
- • If the mouseDown event is in the drag region of the our window, we use
- • the Toolbox function DragWindow to drag it around, taking care not to
- • let the window get dragged completely to the edge of the screen.
- • This would need to be modified to handle multiple monitors.
- */
- thisRect.left = qd.screenBits.bounds.left + 10;
- thisRect.right = qd.screenBits.bounds.right - 10;
- thisRect.top = qd.screenBits.bounds.top + 10;
- thisRect.bottom = qd.screenBits.bounds.bottom - 10;
- DragWindow (thisWindow, thisEvent.where, &thisRect);
- }
-
- else if (thisCode == inGoAway)
- {
- /*
- • If the mouseDown was in the close box of the window, we monitor it with
- • the Toolbox function TrackGoAway. Like DragWindow, TrackGoAway does not
- • return until the mouse is released. TrackGoAway takes care of producing
- • the little starburst in the close box when the mouse is within the close
- • box, and removing the starburst when the mouse is outside the close box.
- • If the mouse is within the close box when the mouse is released,
- • TrackGoAway returns TRUE, in which case we call our routine to close
- • the window.
- */
- stillInGoAway = TrackGoAway(thisWindow,thisEvent.where);
- if (stillInGoAway == TRUE) Close_Window (thisWindow);
- }
-
- else if (thisCode == inContent)
- {
- /*
- • If the mouseDown was in the content region of our window (as opposed to
- • its frame, or outside the window altogether) we make it active if it is
- • not already, and perform our window handling routines.
- */
- if (thisWindow != FrontWindow()) SelectWindow (thisWindow);
- Do_Window (thisWindow,&thisEvent);
- break;
- }
-
- else if (thisCode == inSysWindow) SystemClick (&thisEvent, thisWindow);
- break;
- }
- /*
- • Handle keyDown and autoKey events. Note that contrary to Listing 5 in
- • the original article, Do_PeriodicChart() is no longer responsible for
- • handling keyboard events. That is now the job of a new function, Do_Key().
- • When Do_PeriodicChart() handled both types of events, the program
- • inquired whether an event was a mouse event or keyboard event twice:
- • once in the main event loop, and once in the Do_PeriodicChart function.
- • Handling keyboard events in a separate function is a better arrangement.
- */
- case keyDown:
- case autoKey:
- {
- Do_Key (FrontWindow(),&thisEvent);
- break;
- }
- /*
- • One of the most important parts of a program's code: Handle the Update Events.
- */
- case updateEvt:
- {
- Update_Window (&thisEvent);
- break;
- }
- /*
- • If there is a disk insertion event results in an error, we call the
- • Toolbox routine DIBadMount. This is the one you've seen which posts
- • alerts like "This disk is unreadable: Do you want to initialize it?"
- • followed by three buttons: Eject, One-Sided, and Two-Sided. The System
- • leaves it to the application to call DIBadMount so that disk utility
- • programs can handle bad disks with the "disk unreadable" alert being shown.
- */
- case diskEvt:
- {
- if (HiWord(thisEvent.message) != 0)
- {
- thisEvent.where.h = ((qd.screenBits.bounds.right - qd.screenBits.bounds.left) / 2) - (304 / 2);
- thisEvent.where.v = ((qd.screenBits.bounds.bottom - qd.screenBits.bounds.top) / 3) - (104 / 2);
- InitCursor();
- thisItem = DIBadMount(thisEvent.where, thisEvent.message);
- }
- break;
- }
- /*
- • And finally, we activate the window clicked in if it is an activate event
- • (as determined by testing the activeFlag bit in the modifiers field of
- • the event record). We have no housekeeping to do of any kind when
- • deactivating a window, so we have no deactivating routine. More complex
- • programs may need to do things like "unhighlight" the grow box in the
- • bottom right corner of a resizable window which is being deactivated.
- */
- case activateEvt:
- {
- SetCursor (&(qd.arrow));
- if ((thisWindow != NIL) && (thisEvent.modifiers & activeFlag))
- {
- Activate_Window (thisWindow);
- }
- break;
- }
-
- default: break;
- }
- }
- }
- while (Quitting == FALSE);
-
- /*
- • Upon reaching the end of our main() function, compiler-added code and
- • the operating system work to clean up after us. Our application's space
- • in RAM is deallocated, so that if we have open windows or other memory
- • allocated, we do not need to explicitly call DisposDialog(), DisposHandle,
- • etc., to free up those blocks of memory. (Note: there are Toolbox calls
- • available to allocate memory from the system heap and free multifinder
- • memory, and you must explicitly clean these up before quitting.) For
- • applications which save information to files, you will need to create
- • a scheme for tracking changes and saving data in variables and memory
- • blocks to files before quitting. Stephen Chernicoff shows a good scheme
- • for doing this in volume 2 of his four volume set "Macintosh Revealed,"
- • published by Hayden Books. FYI, Macintosh Revealed is written in Pascal.
- */
-
- }
-
- /*end main()*/
-
- /*______________________________________________________________________________
- Init_Variables()- initialize program variables
- ______________________________________________________________________________*/
-
-
- void Init_Variables()
- {
- int loop1;
-
- /*
- • Here we are just defining a few RGB (Red Green Blue) colors, as described
- • in Inside Macintosh. This is really nothing to be concerned about at this
- • point. We will, however, have to make sure we draw in black, not blue, if
- • we are running on a monochrome monitor. (The blue is invisible on a monochrome
- • monitor. Each variable of type RGBColor has a field to describe the
- • intensity of the red, green, and blue. These variables (e.g., Color_Red)
- • are our program's global variables which we defined in the file MST_Globals.h.
- */
- Color_Red.red = 65535;
- Color_Red.green = 0;
- Color_Red.blue = 0;
-
- Color_Blue.red = 0;
- Color_Blue.green = 0;
- Color_Blue.blue = 65535;
-
- Color_Green.red = 0;
- Color_Green.green = 65535;
- Color_Green.blue = 0;
-
- Color_Black.red = 0;
- Color_Black.green = 0;
- Color_Black.blue = 0;
-
- Color_White.red = 0;
- Color_White.green = 0;
- Color_White.blue = 0;
-
- Color_LtRed.red = 65535;
- Color_LtRed.green = 32767;
- Color_LtRed.blue = 32767;
-
- Color_LtGreen.red = 32767;
- Color_LtGreen.green = 65535;
- Color_LtGreen.blue = 32767;
-
- Color_LtBlue.red = 32767;
- Color_LtBlue.green = 32767;
- Color_LtBlue.blue = 65535;
-
- /*
- • Now we set up the coordinates for the rectangles we need to
- • draw the periodic chart. The coordinates are all local to the window
- • into which we will draw the chart; i.e., we will use the coordinates as
- • offsets down and to the right of the top left corner of the window. The
- • top left corner of the window is coordinate (0,0) in local coordinates.
- •
- • PeriodicRects[] is a global variable array which we defined in MST_Globals.h.
- • Each array element is of type Rect, a Quickdraw struct defined in Inside
- • Macintosh consisting of four QuickDraw coordinates. The fields of a Rect are
- • top, left, bottom, and right.
- •
- • Each element of the array has the same number as the atomic element it
- • corresponds to. We skip PeriodicRects[0] for this reason. BoxSize is a
- • constant defined in MST_Defines.h. In this way we can change the size of the
- • periodic chart just by making one change in the program.
- •
- • Of course there are many other schemes to accomplish our task of drawing
- • a periodic chart and detecting in which element mouseclicks occur. While some
- • are much more elegant, I chose this one because it makes the algorithms for
- • drawing and detecting very explicit.
- */
-
- /*
- • First we construct column 1
- */
-
- PeriodicRects[1].left = 26; PeriodicRects[1].right = 26 + BoxSize;
- PeriodicRects[3].left = 26; PeriodicRects[3].right = 26 + BoxSize;
- PeriodicRects[11].left = 26; PeriodicRects[11].right = 26 + BoxSize;
- PeriodicRects[19].left = 26; PeriodicRects[19].right = 26 + BoxSize;
- PeriodicRects[37].left = 26; PeriodicRects[37].right = 26 + BoxSize;
- PeriodicRects[55].left = 26; PeriodicRects[55].right = 26 + BoxSize;
- PeriodicRects[87].left = 26; PeriodicRects[87].right = 26 + BoxSize;
-
- PeriodicRects[1].top = 26; PeriodicRects[1].bottom = 26 + BoxSize;
- PeriodicRects[3].top = 26 + 1*BoxSize; PeriodicRects[3].bottom = 26 + 2*BoxSize;
- PeriodicRects[11].top = 26 + 2*BoxSize; PeriodicRects[11].bottom = 26 + 3*BoxSize;
- PeriodicRects[19].top = 26 + 3*BoxSize; PeriodicRects[19].bottom = 26 + 4*BoxSize;
- PeriodicRects[37].top = 26 + 4*BoxSize; PeriodicRects[37].bottom = 26 + 5*BoxSize;
- PeriodicRects[55].top = 26 + 5*BoxSize; PeriodicRects[55].bottom = 26 + 6*BoxSize;
- PeriodicRects[87].top = 26 + 6*BoxSize; PeriodicRects[87].bottom = 26 + 7*BoxSize;
-
- /*
- • Then column 2
- */
-
- PeriodicRects[4].left = 26 + BoxSize; PeriodicRects[4].right = 26 + 2*BoxSize;
- PeriodicRects[12].left = 26 + BoxSize; PeriodicRects[12].right = 26 + 2*BoxSize;
- PeriodicRects[20].left = 26 + BoxSize; PeriodicRects[20].right = 26 + 2*BoxSize;
- PeriodicRects[38].left = 26 + BoxSize; PeriodicRects[38].right = 26 + 2*BoxSize;
- PeriodicRects[56].left = 26 + BoxSize; PeriodicRects[56].right = 26 + 2*BoxSize;
- PeriodicRects[88].left = 26 + BoxSize; PeriodicRects[88].right = 26 + 2*BoxSize;
-
- PeriodicRects[4].top = 26 + 1*BoxSize; PeriodicRects[4].bottom = 26 + 2*BoxSize;
- PeriodicRects[12].top = 26 + 2*BoxSize; PeriodicRects[12].bottom = 26 + 3*BoxSize;
- PeriodicRects[20].top = 26 + 3*BoxSize; PeriodicRects[20].bottom = 26 + 4*BoxSize;
- PeriodicRects[38].top = 26 + 4*BoxSize; PeriodicRects[38].bottom = 26 + 5*BoxSize;
- PeriodicRects[56].top = 26 + 5*BoxSize; PeriodicRects[56].bottom = 26 + 6*BoxSize;
- PeriodicRects[88].top = 26 + 6*BoxSize; PeriodicRects[88].bottom = 26 + 7*BoxSize;
-
- /*
- • And column 3
- */
-
- PeriodicRects[21].left = 26 + 2*BoxSize; PeriodicRects[21].right = 26 + 3*BoxSize;
- PeriodicRects[39].left = 26 + 2*BoxSize; PeriodicRects[39].right = 26 + 3*BoxSize;
- PeriodicRects[57].left = 26 + 2*BoxSize; PeriodicRects[57].right = 26 + 3*BoxSize;
- PeriodicRects[89].left = 26 + 2*BoxSize; PeriodicRects[89].right = 26 + 3*BoxSize;
-
- PeriodicRects[21].top = 26 + 3*BoxSize; PeriodicRects[21].bottom = 26 + 4*BoxSize;
- PeriodicRects[39].top = 26 + 4*BoxSize; PeriodicRects[39].bottom = 26 + 5*BoxSize;
- PeriodicRects[57].top = 26 + 5*BoxSize; PeriodicRects[57].bottom = 26 + 6*BoxSize;
- PeriodicRects[89].top = 26 + 6*BoxSize; PeriodicRects[89].bottom = 26 + 7*BoxSize;
-
- /*
- • Now we use a for loop to construct the block of elements 22 thru 30,
- • 40 thru 48, 72 thru 80, and 104 thru 112.
- •
- */
- for (loop1=0;loop1<9;loop1++)
- {
- PeriodicRects[loop1+22].left = 26 + (3+loop1)*BoxSize;
- PeriodicRects[loop1+40].left = PeriodicRects[loop1+22].left;
- PeriodicRects[loop1+72].left = PeriodicRects[loop1+22].left;
- PeriodicRects[loop1+104].left = PeriodicRects[loop1+22].left;
-
- PeriodicRects[loop1+22].right = PeriodicRects[loop1+22].left + BoxSize;
- PeriodicRects[loop1+40].right = PeriodicRects[loop1+40].left + BoxSize;
- PeriodicRects[loop1+72].right = PeriodicRects[loop1+72].left + BoxSize;
- PeriodicRects[loop1+104].right = PeriodicRects[loop1+72].left + BoxSize;
-
- PeriodicRects[loop1+22].top = 26 + 3*BoxSize;
- PeriodicRects[loop1+40].top = 26 + 4*BoxSize;
- PeriodicRects[loop1+72].top = 26 + 5*BoxSize;
- PeriodicRects[loop1+104].top = 26 + 6*BoxSize;
-
- PeriodicRects[loop1+22].bottom = 26 + 4*BoxSize;
- PeriodicRects[loop1+40].bottom = 26 + 5*BoxSize;
- PeriodicRects[loop1+72].bottom = 26 + 6*BoxSize;
- PeriodicRects[loop1+104].bottom = 26 + 7*BoxSize;
- }
-
- /*
- • Don't forget Helium!
- */
-
- PeriodicRects[2].left = 26 + 17*BoxSize;
- PeriodicRects[2].right = PeriodicRects[2].left + BoxSize;
- PeriodicRects[2].top = 26;
- PeriodicRects[2].bottom = 26 + BoxSize;
-
- /*
- • And now for the rightmost six columns (starting with Boron and ending
- • with Radon).
- */
-
- for (loop1=0;loop1<6;loop1++)
- {
- PeriodicRects[loop1+5].left = 26 + (12+loop1)*BoxSize;
- PeriodicRects[loop1+13].left = PeriodicRects[loop1+5].left;
- PeriodicRects[loop1+31].left = PeriodicRects[loop1+5].left;
- PeriodicRects[loop1+49].left = PeriodicRects[loop1+5].left;
- PeriodicRects[loop1+81].left = PeriodicRects[loop1+5].left;
-
- PeriodicRects[loop1+5].right = PeriodicRects[loop1+5].left + BoxSize;
- PeriodicRects[loop1+13].right = PeriodicRects[loop1+13].left + BoxSize;
- PeriodicRects[loop1+31].right = PeriodicRects[loop1+31].left + BoxSize;
- PeriodicRects[loop1+49].right = PeriodicRects[loop1+49].left + BoxSize;
- PeriodicRects[loop1+81].right = PeriodicRects[loop1+81].left + BoxSize;
-
- PeriodicRects[loop1+5].top = 26 + 1*BoxSize;
- PeriodicRects[loop1+13].top = 26 + 2*BoxSize;
- PeriodicRects[loop1+31].top = 26 + 3*BoxSize;
- PeriodicRects[loop1+49].top = 26 + 4*BoxSize;
- PeriodicRects[loop1+81].top = 26 + 5*BoxSize;
-
- PeriodicRects[loop1+5].bottom = 26 + 2*BoxSize;
- PeriodicRects[loop1+13].bottom = 26 + 3*BoxSize;
- PeriodicRects[loop1+31].bottom = 26 + 4*BoxSize;
- PeriodicRects[loop1+49].bottom = 26 + 5*BoxSize;
- PeriodicRects[loop1+81].bottom = 26 + 6*BoxSize;
- }
-
- /*
- • And at last Cerium thru Lutetuim and Thorium thru Lawrencium.
- */
-
- for (loop1=0;loop1<14;loop1++)
- {
- PeriodicRects[loop1+58].left = 26 + (3+loop1)*BoxSize;
- PeriodicRects[loop1+90].left = PeriodicRects[loop1+58].left;
-
- PeriodicRects[loop1+58].right = PeriodicRects[loop1+58].left + BoxSize;
- PeriodicRects[loop1+90].right = PeriodicRects[loop1+90].left + BoxSize;
-
- PeriodicRects[loop1+58].top = 26 + 8*BoxSize;
- PeriodicRects[loop1+90].top = 26 + 9*BoxSize;
-
- PeriodicRects[loop1+58].bottom = 26 + 9*BoxSize;
- PeriodicRects[loop1+90].bottom = 26 + 10*BoxSize;
- }
-
- /*
- • Now our scheme requires one small adjustment to the rects we have
- • constructed so our periodic chart will look right when we draw it.
- • QuickDraw coordinates are pure mathematical coordinates. That is,
- • a QuickDraw point is a mathematically pure, dimensionless point. It is
- • _not_ a pixel, which has a width (of 1) and height (of 1). FrameRect
- • does all of its drawing _inside_ the coordinates you specify. So we will
- • draw the row of pixels _above_ the boundary separating Hydrogen and Lithium
- • when we draw our Hydrogen box, and below the boundary when we
- • draw our Lithium box. That would make a line two pixels wide, which we
- • do not want. So we just increase the right and bottom of each rectangle
- • by one to make everything draw correctly.
- •
- */
-
- for (loop1=1;loop1<=Max_NumElements;loop1++)
- {
- PeriodicRects[loop1].right++;
- PeriodicRects[loop1].bottom++;
- }
-
- }
-
- /*end Init_Variables()*/
-
-
-
-
-
-
-
-
-
-
-
-