This article contains the following executables: CCDEMO.C CCDEMO.H
Stan Krute, when not serving as DDJ's new Mac columnist, is an artist, programmer, writer, and teacher. You can reach him via MCI Mail, Delphi (STANKRUTE), and CompuServe (73137,2121) and also by mail at 18617 Camp Creek Rd., Hornbrook, CA 96044--eds.
It's been four years now since the popped into view, heralded by that great Ridley Scott Superbowl commercial. Though there was obvious brilliance to the design, there were also strong whiffs of arrogance and hype. But, hey, I've been accused of the latter myself. There was nothing to do but pop out of the hills and take a closer look.
I cruised over the Siskiyous to see my Apple dealer buddy, John Manzer. I got to the store, chewed the fat a few minutes, scanned the marketing propaganda, nosed the technical specs, then plunked down at the machine. Cynical musings twisted my mind, but what the hell, let's start 'er up.
I didn't like the locked hardware. I didn't like the one drive. I didn't like the lack of a hard disk. I didn't like the price. I didn't like the (hah hah) wide selection of printers. I didn't like the yuppistic overtones.
But I loved the machine. It was fun to use. Oh, there were flaws, but they seemed minor compared to what the creators got right. Above all, the interface snapped. It didn't have a speed snap--not yet--but rather the feel-of-a-fine-tool kind of snap. The Macintosh communicates via clean visual metaphors, and that's a channel with a lot of bandwidth. Something about the Macintosh interface just feels good, like soft light filtering through a redwood forest or playful kittens careening and bouncing about the world. People needed to use computers like this. I needed to program computers like this. Yow!
Four years of good nurturing has led to some lovely growth. We've got a wide array of languages, detailed system documentation, the LaserWriter, Mac IIs, Hypercard, MultiFinder, and some remarkable application software. The platform's been consolidated, and the best is yet to come. Happy birthday, Mac! Blow out those candles, eat up that cake, chug down the Jolt, get crazy with your buddies. Hell, Maddie Hayes's got you in her office: Y'all done good.
And so Dr. Dobb's gets a Mac column. What'll I do here?
It's a lot of hard work to get a book or software product on the market. I feel a special obligation to creators to be scrupulously fair with any review/comments/criticism of a work. Print's powerful stuff. If I think something is seriously flawed, I won't even bother to mention it here; I prefer to send a quiet note detailing my qualms directly to the publisher. I'd rather put this column's energy into feeding awareness of the good stuff.
A note on objectivity: I'm lucky enough to know and/or have worked with some of the people whose products I may mention. But it does nobody any good if I let that shade my opinions. On the other hand, I don't want to ignore a good product just because I've had something to do with it. So I'll always mention any close connections I've got to a particular item in an objectivity note. Just know that it's done to help you weigh my opinions, not as name dropping.
The code samples I'll be showing aren't for raw beginners. This is DDJ, after all. But it's easier to get up to Mac programming speed now than it was in the early days. A lot of resources are available to help you cruise the learning curve. Here's a minimal list:
After all, I'm a language junkie; one of the perks of this gig is feeding the addiction guilt-free.
Choosing development tools is pretty personal. With one exception (now justly dead in the market), I haven't hit a Mac programming tool that someone wouldn't find useful in some context. The Mac environment must have some kind of inspirational effect. You'll have to follow the usual path to find personally amenable tools: talk to friends, read reviews, scan the ads, ask questions on the networks, play around. For what it's worth, here's a commented list of what I currently find myself using; note that most of my Mac work is done in C, with 68000 assembly language for speed tweaks and writing specialized code resources.
When I'm not traipsing around civilization as a cybernetic nomad, I live in the middle of nowhere, so I have to rely on mail-order sources to get programming books, software, and miscellaneous supplies. I've found a couple of good ones I'm happy to share with you.
For books, I use Computer Literacy. This bookstore carries just about everything, takes credit cards, and ships UPS the day you order. For software and supplies, I use Computerware. It specializes in the Mac, also takes credit cards and ships UPS quickly, and has an 800 phone number. Both these places have retail outlets well worth a visit if you're in Silicon Valley.
Apple's Macintosh upgrade path has been a little bumpy. A lot of folks still have 512s, possibly upgraded from 1285, and wonder whether it's worthwhile doing any further upgrading. Here's what I did: got Apple's 800K drive/128K ROM upgrade ($300 at an Apple dealer), then added SuperMac's Enhance board ($500 installed at Fry's Electronics). Enhance brings your Mac up to 2 megabytes (expandable to 6.5 with high-capacity SIMMs), gives you a slight speed increase, and adds a SCSI port and a small internal fan.
I had one problem with the parasitic clip that plugs Enhance into the motherboard's 68000, but SuperMac kept Federal Expressing me replacements until we had the problem licked--no problems since then. I get a big grin on my face when my original 128K Mac comes up on a speedy hard disk with megabytes of RAM at its disposal.
It's hard to hold in my feelings on this product. Let's just say this: I love it. Michael Kalil and the rest of the Think Technologies crew have given us something wonderful. This thing is fast-makes me want to stick some flame decals on the Mac. Working in the LSC environment's a tasty treat, and I for one refuse to go back to anything slower or less capable.
Producing robust Mac code is (for me, at least) a highly iterative process. It bears repeating: Mac software exists in a very dynamic universe. The slightest coding miscue quickly propagates into screen-twisting madness. Debugging is tricky--well worth avoiding--so I like to write my code in snippets, testing and debugging each piece thoroughly before moving ahead. Lightspeed C, with its blazing turnaround speed, lets me do this painlessly.
A lot of attention's been paid to the product's details. Work goes on in a well-integrated project environment. Nitty little maintenance details are automated. The language, libraries, and header files hold closely to the relevant standards (Unix, Kernighan & Ritchie, Harbison & Steele, the evolving ANSI C, and Inside Macintosh). The editor's good--not quite so feature--laden as QUED/M but good-with powerful grep capabilities. The compiler puts out code that's fast and compact. It's easy to produce the various sorts of Macintosh code, with global and static variables available in each: double-clickable applications, desk accessories, device drivers, and code resources. In-line assembly-language code's allowed, with full access to the C name spaces. Resource file management is completely automated. Register variables are maximal: five data registers and three address registers. HFS and MultiFinder are well supported.
I recently had the pleasure of spending a September afternoon at Think headquarters doing free-form nerd talk with Michael Kahl (the prime Lightspeed C programmer), Andrew Singer (head of Think and co-conceiver of Lightspeed), and Doreen Duplin (marketing/communications whiz). These are nice people in whom the joy of the great hack runs deep. Interesting backgrounds: Michael was a philosophy grad student before succumbing to the lure of machine logic. Andrew's known to many of us for his classic (and, sadly, out of print) Sherlock Holmes pastiche programming books Elementary BASIC and Elementary Pascal.
Recent releases of both Lightspeed C and Lightspeed Pascal (2.13 and 1.11A, respectively, as this column is written) have been maintenance releases, keeping the languages current with the latest Mac machinery and system software. In the works, though, are major new releases of both languages. Look for greater speed and, for Lightspeed C, powerful debugging capabilities. "It's time for another dose of the spectacular," quoth the Singer.
I wish I had room to give you a complete transcript of the afternoon's conversations. The Thinkers said a lot of smart stuff. Maybe in a future column. Meanwhile, take this as a bottom line: if you program the Mac in C, check out Lightspeed.
All right, time to get down to a little code hacking. My first project involves writing and using a custom control definition. Because of space constraints, I'll describe the project in two phases, continuing the discussion in next month's column.
Macintosh applications are rife with controls: buttons, scroll bars, check boxes, radio buttons, et al. Definitions for standard controls are built into the Mac ROM/OS--there's the standard button, for example. Clicking a standard button with the mouse makes things happen. Standard buttons have three basic states, each with a corresponding visual metaphor: inactive, when the button won't respond to a mouse click; active, when the button will respond to a mouse click; and highlighted, when the button's in the midst of being clicked. Figure 1, page 94, shows a standard button in each of these three states.
Figure 1: Standard button in its three highlighted states--inactive, active, and highlighted.
But you also have the ability to define, via a CDEF code resource, your own buttons. The CDEF resource can then be incorporated into an application and can be called upon whenever the application wants to put a button on the screen. Custom CDEFs are not very difficult to write and can provide a lot of flexibility at low memory cost. The CDEF I'll be showing you in this column, for example, is less than 1,400 bytes long yet it provides 16 new types of buttons--that's less than 88 bytes per button variation. Such a deal.
I wrote my CDEF, called rectCDEF, in assembly language using MDS 2.1. That's because I wanted high execution speed and small code size. I wrote a demo application in C that shows off the 16 button types using Lightspeed C 2.11. Resources for the application were put together with ResEdit 1.1B1. PICTures for particular buttons were drawn in SuperPaint 1.0p, then transferred into ResEdit via the Scrapbook.
I first got the demo application up and running, albeit with just one control, that being of button variation 0 (see later). Then I started work on the CDEF. As I worked on the CDEF, I used an Exec JOB file to assemble the code, link it, turn it into a resource, then merge that resource into the demo application for testing. I worked on one variation at a time, adding a control of that type to the demo application, then fixing the CDEF to cover that case.
Any particular CDEF can have up to 16 variations. (Actually, you can hack in a few thousand, but that technique's for another article.) I used all 16 in rectCDEF. The rectCDEF buttons live in a rectangular world. A particular button variation can contain text, a picture, or an icon. Text can be in any font/size/style combination the Mac's capable of. A button variation can have a simple outline, a shadowed outline, or (unless it's a text variation) no outline. A button variation can indicate highlighting via inversion or a change of content.
Figure 2, page 94, details the 16 rectCDEF button variations. Figure 3, page 98, shows examples of each variation, with pictures of the active and highlighted states.
The demo program, imaginatively named custom controls demo, puts up a modal dialog containing examples of rectCDEF buttons, then responds to button clicks. Figure 4, page 100, is a screen snapshot of the program's modal dialog. Using a modal dialog simplified the program's event-handling logic; it's a nice technique for bench testing new routines.
Figure 5, page 100, shows the files involved in the demo program. Custom controls demo PROJ is an LSC project file that contains the C source code file; custom controls demo.c (see Listing One, page 54); and MacTraps, the LSC library that hooks code into the Mac ROM/OS (see Figure 6, page 100). Custom controls demo.h, in Listing Two, page 64, is a file of private definitions for custom controls demo.c. Custom controls demo PROJ.rsrc (available on Compuserve and the DDJ listings disk) is a collection of program resources, including rectCDEF, that gets bound into the final application. It was put together with ResEdit. Finally, custom controls demo is the double-clickable final application.
Although its small and simple, custom controls demo.c follows the classic pattern of Macintosh programs. First come a few setup activities. Then the program sits in a main event loop, waiting for events of interest. When such an event occurs, the program figures out what's up, acts appropriately, then returns to the main event loop. At some point an event occurs that tells the program to pop out of the main event loop. Then come a few cleanup activities, followed by an exit to the OS shell.
Each control button in custom control demo's main modal dialog has a corresponding item number in the DITL resource that supplies the dialog. Figure 7, page 101, matches each item with its DITL item number. These numbers are given symbolic names in the header file custom control demo.h. The same numbers are used for the CNTL template resources that each DITL item points to.
Figure 3: Samples of the 16 rectCDEF variations in active and highlighted states
Figure 4: Custom control demo's main modal dialog
Figure 5: Five files used by Lightspeed C to build the program custom controls demo
Figure 6: The LSC project file used to build custom controls
Figure 7: DITL item numbers for each of the controls in custom control demo's main modal dialog
If you've done your homework, the demo program should seem trivially simple, so I won't go into massive descriptive detail. That'll get saved for next month when it's time to cruise the rectCDEF assembly-language code. Here are a few notes:
main--Sets up the Mac managers, gets the modal dialog going, runs the main event loop, then cleans up and exits when all is done. Note the substitution of the ROM call ModalDialog for the usual GetNextEvent as the heart of the program's main event loop.
inititializeManagers--Grabs some master pointers, forces the heap to grow and clean itself, gets the ROM/OS managers up and running, flushes the event queue, and brings up the standard arrow cursor.
studyAndSetEnvironment--Figures out the size of the screen and menu bar. This information is used later on to position windows neatly.
getThatDialogCookin--Brings the main modal dialog into memory, positions it on the screen, sets its font to Geneva 12, then makes it visible. Note well: it's a good idea to use dialog and window templates that come up invisibly. Then you can bring them into memory, pull off any adjustments in private, and use ShowWindow to make them appear.
dealWithDialogItem--Just a big switch statement to case out on the button that got clicked in the main event loop. The top layers of a Mac application are usually filled with such switch statements as the program zeroes in on exactly what kind of event occurred and what to do about it. Note how the quitItem button controls the main event loop via the global Boolean variable finished.
The following nine routines deal with the clicks of specific buttons:
doOrwellItem--The orwellItem button stays highlighted while the ronItem button fades in and out.
doSnapshotItem--The doSnapshotItem button lets you take action pictures of the demo program via a call to a Camera desk accessory. If you don't have such a DA in your system file, the OpenDeskAcc call returns without crashing.
doMushroomItem--Similar to doOrwellItem. This time the bumperStickersItem fades in and out of view.
doOpenItem--Calls on the standard file-opening routine, then does nothing with the routine's result.
doSaveAsItem--Calls on the standard file-saving routine, then does nothing with the routine's result.
doFlipItem--Takes a list of contentchanging buttons, then runs them through a little animation routine by turning highlighting on and off.
doSomeOffItem--Takes a list of buttons and makes them inactive.
doSomeOnItem--Takes the same list of buttons passed to doSomeOffItem and makes them active.
doCopyrightItem-Brings up a modal dialog that expresses the author's interest in legal protection for works of art.
figureCenteredRectTLC--I don't know about you, but I go nuts over programs that don't know how to position things on different-size screens. This little routine shows how simple it is to be tidy.
To be continued next month.
Special thanks go to the following for thoughts and actions that made this month's column possible: Tom Atkinson of Orchard Computer, Cynthia Bruschi of ICOM Simulations, Dan Cochran of Apple, Doreen Duplin of Think Technologies, Bruce Hammond of Starpoint Software, Michael Kahl of Think Technologies, Jerry Lewak of Paragon Concepts, John Mitchell of Apple, David Perlman of Action Graphics, Andrew Singer of Think Technologies, Nathan Slemmer of interstate Computer Bank, Tyler Sperry of DDJ, Mike Swaine of DDJ, Levi Thomas, and Dan Weston of Nerdworks.
This is the first of my DDJ Mac columns. Feedback pro and con will be much appreciated; my access information is at the end of the column. Hot tips, keen insights, funny problems, and review copies of books and software are also solicited.
Next month for sure: assembly-language source for rectCDEF along with copious explanation and the rest of custom control demo's resources.
Next month maybe (depending on time, space, and circumstance): hypertalk secrets, living with multiFinder, macdraw with a brain, parasitic desk accessories, talks with various programming luminaries, and Microsoft madness revealed.
APDA
Apple Programmer's and Developer's Association 290 S.W. 43rd St. Renton, WA 98055 (800) 426-3667 In WA (800) 527-7562 In Canada (800) 237-4644, (206) 251-5222
Apple Certified Developer Program
Developer Programs Apple Computer Inc. 20525 Mariani Ave. Mailstop 27-W Cupertino, CA 95014 (408) 996-1010
Computer Literacy Bookshops
2590 N. First St. San Jose, CA 95131 (408) 435-1118 (seven days a week)
Computerware
350 Cambridge Ave. Palo Alto, CA 94306 (800) 235-1155 In CA (800) 323-1133
Consulair 68000 Development System
Consulair 140 Campo Dr. Portola Valley, CA 94025 (415) 851-3272 Reader Service No. 29
Enhance Expansion Board
SuperMac Technology 295 North Bernardo Mountain View, CA 94043 (415) 964-8884 Reader Service No. 30
Fedit Plus
MacMaster Systems 108 E. Fremont Ave., Ste. 37 Sunnyvale, CA 94087 (408) 773-9834 Reader Service No. 31
Fry's Electronics
541 Lakeside Dr. Sunnyvale, CA 94086 (408) 662-3566
Lightspeed C
Think Technologies 135 South Rd. Bedford, MA 01730 (800) 643-4465 (617) 275-4800 Reader Service No. 32
MacNosy
Jasik Designs 343 Thenton Wy. Menlo Park, CA 94025 (415) 322-1386 Reader Service No. 33
MacTutor
MacTutor P.O. Box 400 Placentia, CA 92670 (714) 630-3730 Reader Service No. 34
QUED/M 2.04
Paragon Concepts Inc. 4954 Sun Valley Rd. Del Mar, CA 94014 (619) 481-1477 Reader Service No. 35
ResEdit
Available through APDA (see above) or via one of the many on-line services.
Signetics Corp.
Publication Services Mailstop 27 P.O. Box 3409 Sunnyvale, CA 94088-3409 (408) 991-3620
TMON 2.8
ICOM Simulations Inc. 648 S. Wheeling Rd. Wheeling, IL 60090 (312) 520-4440 Reader Service No. 36
Apple Computer Inc. Inside Macintosh, 4 vols. Reading, Mass.: Addison-Wesley, 1985-1986.
Harbison, Samuel P.; and Steele, Guy L., Jr. C:A Reference Manual (2d ed.). Englewood Cliffs, N.J.: Prentice-Hall, 1987.
Knaster, Scott. How to Write Macintosh Software. Hasbrouck Heights, NJ.: Hayden, 1986.
Signetics Corp. Signetics $68000 User's Guide. Sunnyvale, Calif.: Signetics Corp., 1983.
Smith, David E. ed. The Best of MacTutor, Volume 1. Placentia, Calif.: MacTutor, 1986.
Smith, David E. ed. The Complete MacTutor, Volume 2. Placentia, Calif.: MacTutor, 1987.
Weston, Dan. The Complete Book of Macintosh Assembly Language Programming, 2 vols. Glenview, Ill.: Scott, Foresman, 1986.
_TO THE MACS_
by Stan Krute
[LISTING ONE]
Stan Krute
The Doc Gets a New Column
1. Take note of well-done applications and extensions to the user interface.
2. Review a wide array of Mac programming tools: software and works on paper.
3. Talk with, and about the work of, innovative Mac programmers.
4. Discuss some of the more interesting algorithms and data structures contained in the Mac ROM/OS. This thing's a graduate course in programming, with interesting tidbits lurking between every LINK/UNLK pair.
5. Write some code. Mac programming's the most addictive fun I've had in the innards of a machine. The universe of the Mac ROM/OS is quite dynamic, so there's a premium--nay, an imperative--on programming that's clean, concise, and careful. Yield to that imperative, then combine it with an interface design that syncs with the Mac paradigm, and you get applications that not only work but that are also fun, easy to use, empower your users, and smack of elegance.
6. Provide access details. I'll always give you a box (see page 106, for example) filled with information that'll help you get hold of items mentioned in that month's column.
Rewiews, Criticism, Objectivity
Getting Up To Speed
1. Join APDA, the Apple Programmer's and Developer's Association. Godchild of Dan Cochran and Dave Lingwood, this is a one-stop source for draft and finished copies of Apple documentation and development tools as well as a wide variety of third-party products. Dues are a reasonable $20 per year, it has an 800 phone number, and you can charge to plastic.
2. If you're developing commercial products, try to become a certified Apple developer. Most important, this gives you access to Apple's electronic-mail technical support. Within the corporate constraints, the remarkable tech support humans will help you work through most any problem. Answers come within 24 hours. Other certified developer pluses: marketing assistance, developers' conferences, discounts on development hardware, and a tinge of credibility.
3. Get Inside Macintosh and its descendants. If the Pulitzers had a technical writing category, Inside Mac would own a prize. Caroline Rose and her cohorts and descendants have given us the most comprehensive insight into a complex cybernetic system yet seen. This is the starting point for all Macintosh programming. Take a look at the APDA newsletter for the latest volume count. The only flaw is a lack of practical examples, but other folks have filled that gap (see next item).
4. Add at least the following five books to your library: Scott Knaster's How to Write Macintosh Software, Dan Weston's The Complete Book of Macintosh Assembly Language Programming (Volumes I and II), The Best of MacTutor, and The Complete MacTutor. Other fine Mac programming books are available, but these five are classics. They give you the practical examples that Inside Macintosh lacks. And, if you share my lack of photographic memory, you'll also want some language references. I like the handy little Signetics 568000 User's Guide for 68000 assembly language and Harbison and Steele's C: A Reference Manual. (Objectivity note: Dan Weston is a longtime friend and fellow traveler.)
5. After you've reupped with DDJ, subscribe to MacTutor. It's one great Macintosh programming magazine, filled each month with nerdly little programming goodies.
6. Put together an array of development tools. Plenty of good ones are available, and every now and then, I'll review some here in the column.
7. Get a Mac with a hard drive and as much memory as you can afford. Anything less will drive you nutso fast. Hey, I oughtta know: I did my first Mac programming in assembly language on a 128K one-drive machine with 8-minute turnarounds. With a language such as Lightspeed C or Turbo Pascal on a multimeg SCSI machine, turnarounds drop down into the sub-30-second range.
Useful Mail-Order Sources
Don't Trash Your Old Mac
Lightspeed C
Code Corner
Development Details
Figure 2: RectCDEF's 16 button variations
variation content border highlighting via
0 text outlined inversion
1 text outlined content change
2 text shadowed inversion
3 text shadowed content change
4 PICT bare inversion
5 PICT bare content change
6 PICT outlined inversion
7 PICT outlined content change
8 PICT shadowed inversion
9 PICT shadowed content change
10 ICON bare inversion
11 ICON bare content change
12 ICON outlined inversion
13 ICON outlined content change
14 ICON shadowed inversion
15 ICON shadowed content change
An Overview
A Few Function Notes
Wrap Up
Vendors
Bibliography
/*------------------------------ file information -----------------------------*/
/*
custom controls demo.c
c source code file for a minimal Mac program that demonstrates
controls drawn with a custom CDEF resource
the custom CDEF resource that's demonstrated provides 16 button variations
the buttons I
! I live in a rectangular space
! I can be outlined, shadowed, or bare
! I can contain text in any font-style-size, an icon, or a picture
! I can indicate highlighting via inversion or a change of content
edited and compiled with Lightspeed C 2.13
written and )1987 by Stan Krute. all rights reserved. no part of this file,
or the object code it leads to, may be reproduced, in any form or by any means,
without the express written permission of the author and copyright holder.
timestamp: 3:49 pm PST November 16, 1987
spacestamp: 18617 Camp Creek Road Hornbrook, California 96044
this file looks good in 9 point Courier, LSC tabs set to 3
*/
/*--------------------------------- include files -----------------------------*/
/* definitions for Mac OS managers used herein */
#include "ControlMgr.h"
#include "DialogMgr.h"
#include "EventMgr.h"
#include "FontMgr.h"
#include "MenuMgr.h"
#include "Quickdraw.h"
#include "StdFilePkg.h"
/* our stuff */
#include "custom controls demo.h" /* private definitions for this file */
/*----------------------------- main program block ----------------------------*/
void main()
{
/* local variable */
int theItem ;
/* initialize Mac OS managers */
initializeManagers() ;
/* see what the world is like */
studyAndSetEnvironment () ;
/* set up and draw a (dummy) title menu */
InsertMenu( GetMenu(titleMenuID), append ) ;
DrawMenuBar() ;
/* set up and draw a modal dialog window */
getThatDialogCookin () ;
/* initalize our doneness indicator */
finished = false ;
/* run the main event loop */
do
{
ModalDialog (noFilterProcedure, &theItem) ;
dealWithDialogItem (theItem) ;
}
while
( ! finished ) ;
/* leave neatly when done */
DisposDialog (ourDialog) ; /* bye bye to dialog */
ExitToShell() ; /* bye bye to program */
}
/*---------------------------- initializeManagers -----------------------------*/
/* initialize the heap, cursor, and Mac Operating System managers */
void initializeManagers()
{
/* local variable */
Handle someDay ;
/* get some space */
MoreMasters() ; /* get some master pointers */
if (someDay = NewHandle(humungousBlock)) /* grow a maximal heap by */
DisposHandle (someDay) ; /* asking for the future */
/* get those managers going */
InitGraf(&thePort) ; /* set up Quickdraw */
InitFonts(); /* set up the Font Manager */
InitWindows(); /* set up the Window Manager */
InitMenus(); /* set up the Menu Manager */
TEInit(); /* set up Text Edit */
InitDialogs (noResumeProcedure) ; /* set up the Dialog Manager */
/* final adjustments */
FlushEvents (everyEvent, dontStop ) ; /* clear the event queue */
InitCursor(); /* turn the cursor on */
}
/*---------------------------- studyAndSetEnvironment -------------------------*/
/* check out screens, machines, ROMs, et al */
void studyAndSetEnvironment ()
{
/* check out the screen */
screenRect = screenBits.bounds ;
screenHeight = screenRect.bottom - screenRect.top ;
screenWidth = screenRect.right - screenRect.left ;
/* determine height of the menu bar */
if ( ROM85 & 0x8000 )
menuBarHeight = stdMBarHeight ; /* for 64K ROMs */
else
menuBarHeight = MBarHeight ; /* for newer ROMs */
}
/*------------------------------- getThatDialogCookin -------------------------*/
/* set up and draw our main modal dialog window */
void getThatDialogCookin ()
{
/* local variables */
Point tempPoint ;
Rect scratch ;
ControlHandle theButton ;
/* get the dialog window */
ourDialog = GetNewDialog (ourDialogID, storeInHeap, inFront) ;
/* adjust its position */
MoveWindow ( ourDialog,
(tempPoint = figureCenteredRectTLC (&(*ourDialog).portRect)).h,
tempPoint.v, inFront ) ;
/* make dialog window the current grafPort so we can change its font */
SetPort (ourDialog) ;
/* change its font to Geneva 12 */
TextFont (geneva) ;
TextSize (12) ;
/* show the dialog */
ShowWindow (ourDialog ) ;
}
/*-------------------------------- dealWithDialogItem -------------------------*/
/* deal with the hit item */
void dealWithDialogItem (theItem)
int theItem ;
{
/* local constants */
#define oolSize 6
/* local variables */
static short onOffList[oolSize] = { orwellItem, hupCoupleItem,
ronItem, saveAsItem,
pinheadItem, duplicateItem} ;
/* case out on the item */
switch (theItem)
{
case quitItem:
finished = true ;
break ;
case orwellItem:
doOrwellItem () ;
break ;
case snapshotItem:
doSnapshotItem () ;
break ;
case mushroomItem:
doMushroomItem () ;
break ;
case openItem:
doOpenItem () ;
break ;
case saveAsItem:
doSaveAsItem () ;
break ;
case flipItem:
doFlipItem () ;
break ;
case someOffItem:
doSomeOffItem (onOffList, oolSize) ;
break ;
case someOnItem:
doSomeOnItem (onOffList, oolSize) ;
break ;
case copyrightItem:
doCopyrightItem () ;
break ;
default:
break ;
}
/* remove local constants */
#undef oolSize
}
/*--------------------------------- doOrwellItem ------------------------------*/
/* deal with a click of the orwellItem button */
void doOrwellItem ()
{
/* local constants */
#define cyclesDesired 4
#define delayTicksOne 20
#define delayTicksTwo 10
/* local variables */
Rect scratch ;
ControlHandle theItemHandle ;
short cycleCounter ;
ControlHandle ronItemHandle ;
/* get a handle to the button */
GetDItem ( ourDialog, orwellItem, &scratch, &theItemHandle, &scratch) ;
/* hilite the button */
HiliteControl (theItemHandle, hilitedHS ) ;
/* get a handle to the ronItem button */
GetDItem ( ourDialog, ronItem, &scratch, &ronItemHandle, &scratch) ;
/* run several fade cycles on the ronItem button */
for ( cycleCounter = 0; cycleCounter < cyclesDesired; cycleCounter++)
{
/* fade out */
HiliteControl (ronItemHandle, inactiveHS ) ;
/* wait a while */
Delay (delayTicksOne, &scratch) ;
/* back into view */
HiliteControl (ronItemHandle, activeHS ) ;
/* wait a while */
Delay (delayTicksTwo, &scratch) ;
}
/* unhilite the button */
HiliteControl (theItemHandle, activeHS ) ;
/* remove local constants */
#undef cyclesDesired
#undef delayTicksOne
#undef delayTicksTwo
}
/*--------------------------------- doSnapshotItem ----------------------------*/
/* deal with a click of the snapshotItem button */
void doSnapshotItem ()
{
/* local variables */
ControlHandle theItemHandle ;
Rect scratch ;
GrafPtr entryGrafPort ;
/* get a handle to the button */
GetDItem ( ourDialog, snapshotItem, &scratch, &theItemHandle, &scratch) ;
/* hilite the button */
HiliteControl (theItemHandle, hilitedHS ) ;
/* save a pointer to the grafPort */
GetPort(&entryGrafPort) ;
/* this lets me take some snapshots */
/* has no effect unless you have a desk accessory named Camera */
OpenDeskAcc ("\007\000Camera") ;
/* restore the grafPort */
SetPort(entryGrafPort) ;
/* unhilite the button */
HiliteControl (theItemHandle, activeHS ) ;
}
/*--------------------------------- doMushroomItem ----------------------------*/
/* deal with a click of the mushroomItem button */
void doMushroomItem ()
{
/* local constants */
#define cyclesDesired 4
#define delayTicks 30
/* local variables */
Rect scratch ;
short cycleCounter ;
ControlHandle itemHandleOne ;
ControlHandle itemHandleTwo ;
/* get a handle to the button */
GetDItem ( ourDialog, mushroomItem, &scratch, &itemHandleOne, &scratch) ;
/* hilite the button */
HiliteControl (itemHandleOne, hilitedHS ) ;
/* get a handle to the bumperStickersItem button */
GetDItem ( ourDialog, bumperStickersItem, &scratch,
&itemHandleTwo, &scratch) ;
/* run several fade cycles on the bumperStickersItem button */
for ( cycleCounter = 0; cycleCounter < cyclesDesired; cycleCounter++)
{
/* fade out */
HiliteControl (itemHandleTwo, hilitedHS ) ;
/* wait a while */
Delay (delayTicks, &scratch) ;
/* back into view */
HiliteControl (itemHandleTwo, activeHS ) ;
/* wait a while */
Delay (delayTicks, &scratch) ;
}
/* unhilite the button */
HiliteControl (itemHandleOne, activeHS ) ;
/* remove local constants */
#undef cyclesDesired
#undef delayTicks
}
/*----------------------------------- doOpenItem ------------------------------*/
/* deal with a click of the openItem button */
void doOpenItem ()
{
/* local variables */
Rect scratch ;
ControlHandle theItemHandle ;
DialogTHndl theDLOGHandle ;
SFReply dummyReply ;
/* get a handle to the button */
GetDItem ( ourDialog, openItem, &scratch, &theItemHandle, &scratch) ;
/* hilite the button */
HiliteControl (theItemHandle, hilitedHS ) ;
/* run the standard file open dialog */
theDLOGHandle = (DialogTHndl) GetResource ('DLOG', getDlgID) ;
SFGetFile (figureCenteredRectTLC (&(**theDLOGHandle).boundsRect),
nil, nil, allTypes, nil, nil, &dummyReply ) ;
/* unhilite the button */
HiliteControl (theItemHandle, activeHS ) ;
}
/*-------------------------------- doSaveAsItem -------------------------------*/
/* deal with a click of the saveAsItem button */
void doSaveAsItem ()
{
/* local variables */
Rect scratch ;
ControlHandle theItemHandle ;
DialogTHndl theDLOGHandle ;
SFReply dummyReply ;
/* get a handle to the button */
GetDItem ( ourDialog, saveAsItem, &scratch, &theItemHandle, &scratch) ;
/* hilite the button */
HiliteControl (theItemHandle, hilitedHS ) ;
/* run the standard file open dialog */
theDLOGHandle = (DialogTHndl) GetResource ('DLOG', putDlgID) ;
SFPutFile (figureCenteredRectTLC (&(**theDLOGHandle).boundsRect),
"\015Save file as:", "\021Current File Name",
nil, &dummyReply ) ;
/* unhilite the button */
HiliteControl (theItemHandle, activeHS ) ;
}
/*--------------------------------- doFlipItem --------------------------------*/
/* deal with a click of the flipItem button */
void doFlipItem ()
{
/* local constants */
#define numButtons 9
#define cyclesDesired 4
#define delayTicks 15
/* local variables */
static short flipList [numButtons] = { hupCoupleItem, mouthOpensItem,
trashItem, melancholyItem,
eeekShrinkItem, mushroomItem,
duplicateItem, orwellItem,
bumperStickersItem } ;
Rect scratch ;
ControlHandle theItemHandle ;
short cycleCounter ;
short index ;
ControlHandle tempItemHandle ;
/* get a handle to the button */
GetDItem ( ourDialog, flipItem, &scratch, &theItemHandle, &scratch) ;
/* hilite the button */
HiliteControl (theItemHandle, hilitedHS ) ;
/* run several animation cycles on a group of content-changing buttons */
for ( cycleCounter = 0; cycleCounter < cyclesDesired; cycleCounter++)
{
/* hilite all the buttons in the group */
for (index = 0 ; index < numButtons ; index++)
{
GetDItem ( ourDialog, flipList[index], &scratch,
&tempItemHandle, &scratch) ;
HiliteControl (tempItemHandle, hilitedHS ) ;
}
/* wait a while */
Delay (delayTicks, &scratch) ;
/* unhilite all the buttons in the group */
for (index = 0 ; index < numButtons ; index++)
{
GetDItem ( ourDialog, flipList[index], &scratch,
&tempItemHandle, &scratch) ;
HiliteControl (tempItemHandle, activeHS ) ;
}
/* wait a while */
Delay (delayTicks, &scratch) ;
}
/* unhilite the button */
HiliteControl (theItemHandle, activeHS ) ;
/* remove local constants */
#undef numButtons
#undef cyclesDesired
#undef delayTicks
}
/*------------------------------- doSomeOffItem -------------------------------*/
/* deal with a click of the someOffItem button */
void doSomeOffItem (theOffList, listSize)
short theOffList[] ;
short listSize ;
{
/* local variables */
short index ;
Rect scratch ;
ControlHandle theItemHandle ;
ControlHandle tempItemHandle ;
/* get a handle to the button */
GetDItem ( ourDialog, someOffItem, &scratch, &theItemHandle, &scratch) ;
/* hilite the button */
HiliteControl (theItemHandle, hilitedHS ) ;
/* for each item in the list */
for (index = 0 ; index < listSize ; index++)
{
/* get a handle to the item */
GetDItem ( ourDialog, theOffList[index], &scratch,
&tempItemHandle, &scratch) ;
/* inactivate the item */
HiliteControl (tempItemHandle, inactiveHS ) ;
}
/* unhilite the someOffItem button */
HiliteControl (theItemHandle, activeHS ) ;
}
/*-------------------------------- doSomeOnItem -------------------------------*/
/* deal with a hit of the someOnItem button */
void doSomeOnItem (theOnList, listSize)
short theOnList[] ;
short listSize ;
{
/* local variables */
short index ;
Rect scratch ;
ControlHandle theItemHandle ;
ControlHandle tempItemHandle ;
/* get a handle to the button */
GetDItem ( ourDialog, someOnItem, &scratch, &theItemHandle, &scratch) ;
/* hilite the button */
HiliteControl (theItemHandle, hilitedHS ) ;
/* for each item in the list */
for (index = 0 ; index < listSize ; index++)
{
/* get a handle to the item */
GetDItem ( ourDialog, theOnList[index], &scratch,
&tempItemHandle, &scratch) ;
/* activate the item */
HiliteControl (tempItemHandle, activeHS ) ;
}
/* unhilite the someOnItem button */
HiliteControl (theItemHandle, activeHS ) ;
}
/*--------------------------------doCopyrightItem -----------------------------*/
/* deal with a hit on the copyrightItem button */
void doCopyrightItem ()
{
/* local variables */
Rect scratch ;
ControlHandle theItemHandle ;
DialogPtr copyrightDlog ;
Point tempPoint ;
/* get a handle to the button */
GetDItem ( ourDialog, copyrightItem, &scratch, &theItemHandle, &scratch) ;
/* hilite the button */
HiliteControl (theItemHandle, hilitedHS ) ;
/* pull in the copyright notice modal dialog */
copyrightDlog = GetNewDialog (copyrightDlogID, storeInHeap, inFront) ;
/* center it on the screen */
MoveWindow ( copyrightDlog,
(tempPoint = figureCenteredRectTLC (&(*copyrightDlog).portRect)).h,
tempPoint.v, inFront ) ;
/* show the copyright notice */
ShowWindow (copyrightDlog ) ;
/* wait until the user clicks the mouse in the dialog */
ModalDialog (noFilterProcedure, &scratch) ;
/* get rid of the dialog */
DisposDialog (copyrightDlog) ;
/* unhilite the button */
HiliteControl (theItemHandle, activeHS ) ;
}
/*--------------------------- figureCenteredRectTLC ---------------------------*/
/* given a rectangle, returns the top left corner position that will
center the rectangle inside screen area that's below the menu bar */
Point figureCenteredRectTLC (theRect)
Rect *theRect ;
{
/* local variable */
Point theResult ;
/* figure the vertical position */
theResult.v = menuBarHeight + ((screenHeight - menuBarHeight) -
(theRect->bottom - theRect->top) ) / 2 ;
/* figure the horizontal position */
theResult.h = ( screenWidth - (theRect->right - theRect->left) ) / 2 ;
/* done, so return the point */
return (theResult) ;
}
[LISTING TWO]
/*------------------------------ file information -----------------------------*/
/*
custom controls demo.h
private definitions for custom controls demo.c
edited and compiled with Lightspeed C 2.13
)1987 by Stan Krute -- all rights reserved
timestamp: 5:56 pm PST November 16, 1987
spacestamp: 18617 Camp Creek Road Hornbrook, California 96044
this file looks good in 9 point Courier, LSC tabs set to 3
*/
/*-------------------------------- constants ----------------------------------*/
/* booleans */
#define true 1
#define false 0
/* parameters */
#define humungousBlock 0x8FFFFFFF
#define noResumeProcedure 0
#define dontStop 0
#define nil 0
#define allTypes -1
/* control stuff */
#define activeHS 0 /* three hilite states */
#define inactiveHS 255
#define hilitedHS 10
/* menu stuff */
#define stdMBarHeight 20
#define titleMenuID 1
#define append 0
/* dialog stuff */
#define storeInHeap 0
#define inFront -1
#define ourDialogID 1
#define copyrightDlogID 210
#define noFilterProcedure 0
#define quitItem 1
#define orwellItem 2
#define snapshotItem 3
#define bumperStickersItem 4
#define mushroomItem 5
#define hupCoupleItem 6
#define openItem 7
#define ronItem 8
#define saveAsItem 9
#define duplicateItem 10
#define woozyItem 11
#define trashItem 12
#define flipItem 13
#define mouthOpensItem 14
#define copyItem 15
#define melancholyItem 16
#define pinheadItem 17
#define someOffItem 18
#define someOnItem 19
#define eeekShrinkItem 20
#define copyrightItem 21
/*-------------------------------- type definitions ---------------------------*/
typedef short boolean;
/*---------------------------- function prototypes ----------------------------*/
void main (void) ;
void initializeManagers (void) ;
void studyAndSetEnvironment (void) ;
void getThatDialogCookin (void) ;
void dealWithDialogItem (int theItem) ;
void doSnapshotItem (void) ;
void doOrwellItem (void) ;
void doMushroomItem (void) ;
void doOpenItem (void) ;
void doSaveAsItem (void) ;
void doFlipItem (void) ;
void doSomeOffItem (short theOffList[], short listSize);
void doSomeOnItem (short theOnList[], short listSize);
void doCopyrightItem (void) ;
Point figureCenteredRectTLC (Rect *theRect) ;
/*------------------------------ global variables -----------------------------*/
boolean finished ; /* indicates when the program should end */
DialogPtr ourDialog ; /* points to our main dialog */
int menuBarHeight ; /* know your environmentI */
int screenHeight ;
int screenWidth ;
Rect screenRect ;