FREE Subscription to Dr. Dobb’s Digest: Same Great Content, New Digital Edition
Site Archive (Complete)
Email
Print
Reprint

add to:
Del.icio.us
Digg
Google
Furl
Slashdot
Y! MyWeb
Blink
March 01, 1988

To the Macs

(Page 6 of 6)

MAR88: TO THE MACS

TO THE MACS

MultiFinder, HyperCard, and More on Custom CDEFs

Stan Krute

In case you missed January's introductory column: I'm here to talk Mac. Mostly I'll provide code samples. There'll also be discussion of programming tools, notable applications, conversations with programmers, and interesting ROM/OS topics.

This month I discuss three goodies from Apple (MultiFinder, HyperCard, and a manual) and finish up the custom control definition (CDEF) code project I began last time. If you hadn't noticed, the Macintosh universe is going supernova. Time to surf the flash....

MultiFinder Arrives

Official production copies of MultiFinder flew into my mountain aerie this month. (Although you will read this in February I wrote it in October-November.) Bottom line: I love it. A clean bit of work. Thanks, Apple people. I run it almost all the time. The exception, when I'm print spooling to my Imagewriter. That'll change, probably by the time you read this.

Meantime, a few thoughts:

  • Two Mbyte becomes the new minimum Mac RAM configuration. Think of the momentum Apple'd have if this chip-war silliness weren't happening....
  • There may finally be a blessed stop to "What would you do with (fill in the blank) Mbyte of memory, anyway?" questions, now that the obvious answer becomes even more so: Fill it with programs and data that work intelligently with one another so as to further enlarge the universe of possible computer users.
  • The major hole in this first version of MultiFinder: interprocess communication facilities. To raise the intelligence of the computing environment, applications must be able to talk to one another, both demo- and autocratically. Apple seems to understand this, so I figure this feature will show up in the next release. Plan ahead.
  • Desk accessories, though supported, have lost a big chunk of their prime raison d'etre: pseudomultitasking. We've now got semimultitasking, with the full thing lurking a few months ahead.
By the way, in case you don't know: with this release of MultiFinder, holding down the Option key when invoking a DA opens it in the foreground application's world rather than MultiFinder's separate desk accessory layer-useful for various parasitic DAs (spelling checkers, macro engines, et al.).

  • The obvious hook into multitasking Get NextEvent, has been used. If you guessed this three years ago, give yourself a no-prize.
  • Following my favorite thread in the Apple mythos, MultiFinder favors decentralization of control. Rather than use a central multiprocessing dictator, MultiFinder gives applications the major say in CPU allocation. I like the implications.
  • If you haven't already, say goodbye to low memory. Say goodbye to tricky OS hacks. Say goodbye to direct writing to the screen. Say goodbye to event and window manager fiddling. Assume as little as possible about anything. Don't play no dirty tricks.
Of course, capital-G Good Mac programmers never did funniness for fun, anyways. They did it to get around some performance hole in the environment. So, OK, we won't do that anymore. But now it's up to Apple to make sure the necessary functionality gets built into the official toolkit. And while you're at it, Cupertinians, how about a beefed-up Quickdraw/PostScript on a chip, please?

It's not too hard to get your programs "MultiFinder friendly." Call or write APDA for technical documentation, sample code, interfaces, and so on. I'll also try to work up something unique for the column. The docs I've got now are preliminary; by the time you read this, a lot more should be available.

HyperCard, Too

Another official arrival these past months: HyperCard. I like it a lot. We need a speedy and standard engine for accessing the gargantuan lased-out data piles that are looming, and HyperCard will do just fine.

HyperTalk, the HyperCard programming language, is simple, elegant, and powerful. Hits me as a nice synthesis of the history and state of computer languages, a sort of where-we-stand-today for the masses. I've done a fair amount of teaching programming, and this is a language I could use with all levels of learners.

There's a lot of the Atkinson snap to HyperCard, along with his attention to user interface and small implementation details. Quibbles? Well, there are the obvious things: You can only open a stack at a time. You can't resize the HyperCard window. There's a fair amount of modality and isolation from other applications. I have the feeling these things will get fixed.

Want to program the sucker? Play with it. Examine/modily scripts. Scan Apple's HyperCard User Guide. Then go on to Danny Goodman's fine work, The Complete HyperCard Handbook and APDA's HyperCard Technical Reference Package. Also, Peter Olson, proprietor of Delphi's ICONtact Mac forum, has written a sweet piece of shareware, Stackware Detective, to let you peek a little deeper. Check it out, and remember to pay if you end up using it.

My own HyperCard explorations are traveling along the XCMD and XFCN paths. These are hooks that let you add your own commands to HyperTalk. You can find details in the APDA docs. I'll be bringing you some samples in the near future. As PEEK and POKE are to BASIC, and slots are to the Apple II, the X twins are to HyperCard.

One of the Great Docs

Apple's always impressed me with its documentation, especially the programmers docs. I think of the early Applesoft manuals, the Apple Pascal books, Inside Macintosh, Bo Three Bob's tech notes, and on and on. There have always been individuals at the company who care about this sort of thing, realizing its importance, and they've been able to marshal resources and produce.

There's an especially nice document out that you may have missed. It's one that all Mac programmers should read, ponder, and pay attention to. It's title is Human Interface Guidelines: The Apple Desktop Interface, and it's available from APDA.

I know, the title's not particularly exciting. But the content is, to me at least. Some of the more frustrating discussions I've had are with programmers who just don't understand why they should follow Apple's interface guidelines as closely as makes sense for their application. It's not because all Apple's decisions are cosmically correct. They're not. But they're darn close. And above all quibbling, consistent operational similarities between applications empower users. Period.

Please read this book, folks.

Code Corner: Cutom CDEFs, Part 2

Resource Revelations

Minor problem: As noted last month, I use ResEdit to build most of my Macintosh program resources. It's so Mac-like. But there's no text file script left over. So how can I reveal custom controls demo's resources to you?

Macintosh Programmer's Workshop (MPW) includes a Mac resource decompiler, DeRez. It takes a file's resources and produces a text file script description. It'd do the trick. But I don't use MPW (yet?). Cruising Delphi, my favorite on-line hangout, I found a fine solution: Alan Dahlbom's ResTools 2.01. It does much of what MPW's Rez and DeRez do, uses a compatible C-like syntax, and is free. A big thanks to Alan for a fine piece of programming and a public service.

So, Listing One, starting on page 64, shows the ResTools 2.01 decompilation of most of the resources included in custom controls demo PROJ.rsrc. The syntax, as mentioned earlier, is C-like; if it doesn't make immediate sense, go to Inside Mac and stare at the particular resource's description.

I've removed the decompilation of the file's PICTs, ICONs, and ICN#s; these graphic resources look kind of silly as streams of hexits. Table 1, below, indicates where you can find images of the PICTs as well as the size of their bounding rectangles. Figure 1, also below, shows some of the PICT images. Figure 2, page 100, shows the ICONs and ICN#s. I also removed the CDEF from the decompilation (more meaningless hex). It's produced by compiling rectCDEFAsm, as described later.

Table 1: Finding the PICTs, and their bounding rectangles, from custom controls demo

PICTs 50 and 51 -- see last month's Figure 3, variation 5--rect: 0,0,57,61
PICTs 60 and 61 -- see last month's Figure 3, variation 7--rect: 0,0,68,69
PICT 70 -- see last month's Figures 4 nad 7, DITL item 7--rect: 0,0,34,32
PICT 80 -- see last month's Figures 4 and 7, DITL item 8--rect: 0,3,196,116
PICT 90 -- see last month's Figures 4 and 7, DITL item 9--rect: 0,12,35,73
PICTs 100 and 101 -- see last month's Figure 3, variation 9--rect: 0,0,56,70
PICTs 170 and 171 -- see last month's Figures 4 and 7, DITL item 17, and
     this month's Figure 2--rect: 0, 0, 187, 85
PICTs 200 and 201 -- see last month's Figures 4 and 7, DITL item 20, and
     this month's Figure 2--rect: 0, 0, 24, 24
PICT 210 -- see last month's Figure 2--rect: 1, 14, 168, 290

Figure 1: Selected PICTs from custom controls demo

Figure 2: ICONs and ICN#s from custom controls demo

Lightspeed C takes the resources in custom controls demo PROJ.rsrc and binds them into the finished custom controls demo application. Other Mac development systems let you perform congruent conjunctions.

Custom Controls Demo's Resources

BNDL 128--This BuNDLe resource connects the program to its Finder icon.

CDEF 40--The Control Definition code.

CNTLs 1 thru 21--CoNTroL specifications for each of the program main modal dialog's 21 custom control items.

CuCo 0--The application s version data/signature/creator resource: a Pascal-type string providing its name, version, and date.

DITL 1--DIalog ITem List for the program's main modal dialog.

DITL 210--DIalog ITem List for the copyright button's modal dialog.

DLOG 1-DiaLOG tempiate for the program's main modal dialog.

DLOG 210--DiaLOG template for the copyright button's modal dialog.

FREF 128--File REFerence that hooks the program up with its Finder icon.

ICN# 128--The program's Finder icon.

ICONs 30, 110, 120, 121, 130, 140, 141, 150, 160, and 161--ICONs used with various control items in the main modal dialog. Numbering convention: the control's item number in the dialog times 10, with 1 added for a content-change image.

MENU 1--Menu template for the program's menu.

PICTs 50, 51, 60, 61, 70, 80, 90, 100, 101, 170, 171, 200, and 201-PICTures used with various control items in the main modal dialog. Numbering convention: the control's item number in the dialog times 10, with 1 added for a content-change image.

PICT 210--PICTure used with the copyright button's modal dialog.

STRs 20 and 40-STRings used for content changing with the modal dialog's control items 2 and 4.

CDEF Development Files

As mentioned in last column, the CDEF is written in assembly language, using MDS 2.1. Figure 3, page 100, shows the files involved in its development. rectCDEFAsm and rectCDEFEqu.Txt are the critical files; the others are either MDS-specific or adjuncts to development. Here are brief descriptions:

rectCDEF.Job--An MDS executive file used to control one iteration of the development cycle. After compiling the CDEF, it turns it into a code resource, puts the resource into several files, and ends up in custom controls demo, ready for testing. See Listing Two, page 65.

rectCDEFAsm--Assembly-language source code for the CDEF. Entry point is at the beginning of the code. See Listing Three, page 66.

rectCDEFEqu.Txt--Private definitions for rectCDEFAsm. See Listing Four , page 83.

rectCDEF.Link--Instructions for the MDS linker. Takes the .Rel file produced by assembling rectCDEFAsm and produces a standard Mac CODE resource. See Listing Five, page 83.

rectCDEF.R0--Instructions for the MDS resource compiler. Takes the standard CODE resource produced by the linker and turns it into a CDEF. See Listing Six, page 84.

rectCDEF.R1--Instructions for the MDS resource compiler. Takes the CDEF produced via rectCDEF.R0 and adds it to custom controls demo. See Listing Seven, page 84.

rectCDEF.R2--Instructions for the MDS resource compiler. Takes the CDEF produced via rectCDEF.R0 and adds it to custom controls demo PROJ.rsrc. See Listing Eight, page 84.

rectCDEF.Rel-The .Rel file produced by assembling rectCDEFAsm.

rectCDEF--The file produced by linking rectCDEF.Rel under the control of rectCDEF.Link.

rectCDEF.Rsrc--The file produced by the MDS resource compiler that contains the CDEF resource.

custom controls demo--My application, to which the MDS resource compiler adds the CDEF for easy testing during CDEF development.

custom controls demo PROJ.rsrc--My application's resources, to which the MDS resource compiler adds the CDEF.

CDEF Refresher

A C function prototype for a CDEF looks like this:

pascal long someCDEF ( int varCode, ControlHandle theControl, int message, long param );

varCode indicates which of the CDEF's variations to use; theControl gives you a handle to the calling control's control record; and message tells the CDEF what to do. There are nine possible messages-- see Inside Macintosh and/or the message jump table in the source code. Finally, param is 4 byte of data that vary according to the message passed to the function. The meaning of the function result also varies with the message; the default return value is 0.

The pascal keyword in the prototype indicates that the function is called with Pascal calling conventions. In rectCDEFAsm, I use LINK/UNLK to create space for some local variables. Figure 4, page 101, shows how the stack looks after the CDEF is called and the LINK A6 command's been given.

The Clean Nut Can't Shut Up

As I never tire of pointing out: good Mac programming has to play by the rules. Keep it clean, and your programs will run now and in the future. What entaileth clean? Deal with error codes. Lock down heap objects only when you must, and let them float whenever you can. Assume nothing. Steer clear of low memory. Follow Inside Mac and Apple's Mac Tech Notes religiously. Etc., etc., etc.

Gawd, I sound like the Ms. Grundy of cybernetics. But, hey, cleanliness works. Clean Mac programs I wrote three years ago function on Mac IIs under MultiFinder.

Take a look at the rectCDEF code, for example. The control that's calling the CDEF gets its control record locked down during any call to the CDEF )see the CDEF's entry area). The same with the control's auxiliary data block (also in the CDEF's entry area). Both of these data objects are released at the end of the CDEF call. Calls to the resource and memory managers (in doInitCntl) are carefully checked for success/failure, and the program tries to respond reasonably to the results see doInitCntl and doDispCntl).

This is a 680x0 we've got, with all those lovely registers waiting to serve. So pack 'em up. That's what I try to do in the CDEF. Here's the scoop: Three address registers are taken up right off the bat. A7 points to the stack, A6 points to a stack frame, and A5 fingers application and Quickdraw globals. Then, I use A2 to point to the control's record and A3 to point to its auxiliary data block. That leaves A4 free for temporary usage and A0 and A1 for event more temporary usage. That's because ROM/OS calls generally trash A0 and A1.

Data registers next: I use D3 to hold the function parameter param. D4 holds flags that let me quickly scoot around the code based on which of the 16 CDEF variations is in effect. That leaves D5, D6, and D7 free for temporary usage and D0, D1, and D2 for even more temporary usage. Again, that's because ROM/OS calls can trash D0, D1, and D2.

On entry to the CDEF, I build a stack frame, save registers that I'll use, grab some parameters grab a set of variation flags, set a default function result, lock the control record down, point to any auxiliary data block and lock it down (if it exists), then jump to a specific routine based on the function's message parameter.

Returning fmm that specific routine, I make a somewhat symmetric exit: unlock any auxiliary data block, unlock the control record, restore saved registers, remove the stack frame, and jump on back to the CDEF's caller.

The CDEF handles 16 control variations. I use an 8-bit flag to signal eight qualities of a given control variation. This simplifies the rest of the CDEF function's control logic. Take a look at varFlagTable for details.

Handling Controls

doInitCntl takes care of any special initialization needs. For this CDEF, I need an auxiliary data block to hold handles to variant-specific resources. So doInitCntl first tries to get a block. If it succeeds, it then looks to see what resources are needed, tries to load them in, and then stores the resulting handles in the auxiliary data block.

Figure 4: The state of the stack upon entry to rectCDEFProc, just after the LINK instruction>

Note the code's provisions for failure. No memory available for the auxiliary data block? You jump nimbly out of the initialization, leaving a tell-tale NIL handle behind in the control record. Resources can't be loaded? Again, NIL handles are stored to tell the tale. Other routines in the CDEF check for all these NIL handles and and react appropriately.

doDispCntl takes care of any special control disposal operations. In this case, I need to release any variant-specific resources that were loaded during initialization, then get rid of any auxiliary data block. The code's pretty straightforward. Note how I check for, and dance around, any NIL pointers/handles.

doDrawCntl is invoked when the CDEF's asked to draw the control. It first saves grafport features it may change. Then it cases out on the type of outline the control needs: shadowed, simple, or none. After drawing the outline, it sets the grafport's clipping region to the control's interior. Then there's another case-out, this time to draw the button's interior, which can contain text, icon, or a picture. Alter the interior's drawn, the routine restores the saved grafport features and ends.

Outlines and Interior Clip Regions

It's fairly simple stuff, really. rectCDEF supports two styles of outlining: shadowed and simple. doShadOutline draws shadowed outlines. First it draws two shadow ines, then a rectangle. doSimpOutline draws simple outlines by drawing a rectangle.

Interior clip regions make sure that button contents don't spill out of bounds. For outlined buttons, you just take a rectangle slightly inset from the button's bounding rectangle. Naked buttons use the bounding rectangle.

Drawing Button Interiors

doTextInterior draws text button interiors. It starts by figuring out which font the button's text will get drawn in and sets the gralport accordingly. Then it uses the font information to figure out vertical positioning for the text.

Next, the routine gets a pointer to the text string it'll draw. This can be the control's title or, in the case of a content-changing button, a STR re source. If it's a STR resource, the resource gets locked down. The routine uses the text string to figure out horizontal positioning. Now that the vertical and horizontal positions are known, doTextInterior moves the grafport's drawing pen into place to start drawing the string.

Depending on the button's type and state, the interior is painted white or black. Based on the same information, the routine draws the text string in black or white. If a STR resource supplied the text, it's now unlocked. And, if the button's in an inactive state, black text gets turned to gray. The routine restores the graiport and exits.

doPictInterior draws picture button interiors. First it figures out the PICT resource to use, then it locks the PICT down. It uses the PI CT's height and width to set up a destination rectangle that'll center the picture in the button's interior. If the picture's too big, the destination rectangle is set so its upper-left corner matches the button interior's upper-left corner. Then the routine erases the button's interior and draws the PICT, and the PICT gets unlocked. Finally, a separate routine, interiorAdjust, is called to deal with any necessary image inverting or graying out.

Very similarly, doIconInterior draws iconic button interiors. It uses the standard height and width of an icon to set up a destination rectangle that'll center the icon in the button's interior. If the icon's too big, the destination rectangle is set so its upper-left corner matches the button interior's upper-left corner. Then the routine erases the button's interior and draws the icon. Finally, interiorAdjust is called to deal with any necessary image inverting or graying out.

Procedural wrap up

doTestCntl tests a point to see if it's contained within an active control. If it is, the routine sets the CDEF's function result to the control's part number. If the point isn't in the control, or if the control's inactive, the routine sets an appropriate result code.

Because my controls are rectangular, the containment test is simple: just call on the Mac's PtInRect function, which checks for a point's containment within a rectangle.

DoCalcCCntl is deliciously trivial. Somebody wants the control described as a region? You just send the control's rectangle to the Mac's Rectflgn procedure, which takes a rectangle and produces a corresponding region description.

There are four messages to the CDEF that you ignore, relying instead on the Mac's default behaviors. So doPosCntl, doThumbCntl, doDragCntl, and doAutoTrack are just stubs.

Bibliography

Apple Computer Inc. Human Interface Guidelines: The Apple Desktop Interface. Human Interface and Technical Publications Groups APDA #KNBHIG.

Goodman, Danny. The Complete HyperCard Handbook.: Bantam, 1987. MultiFinder Development Package Version 1.0. Available from APDA #KMSMSD, $12.50.

Vendors

APDA (Apple Programmer's and Developer's Assoc.) 290 S.W. 43rd St. Renton, WA 98055 (800) 426-3667 In Wash. (800) 527-7562 In Canada (800) 237-4644 (206) 251-5222

HyperCard Available from Apple dealers

HyperCard Technical Reference Package APDA #KMBHTL Available from APDA (see above)

MultiFinder 1.0 (plus System 42 and Finder 6.0) Available from Apple dealers

ResTools 2.01 Alan Dahlbom Downloadable from Delphi, GENie, and other on-line services

Stack Detective Peter Olson Shareware available from Delphi and other shareware sources

[LISTING ONE]


/* resource decompilation of custom controls PROJ.Rsrc */ /* decompilation performed by Alan Dahlbom's ResTools 2.01 */ /* PICTs, ICONs, ICN#s, and CDEFs not included */

resource 'BNDL' (128) { 'CuCo', 0, { 'FREF', {0, 128}; 'ICN#', {0, 128} } };

resource 'CNTL' (1) { {0, 0, 28, 130}, 0, visible, 0, 0, 640, 0x0, "Press Me To Quit" };

resource 'CNTL' (2) { {0, 0, 36, 145}, 6, visible, 18, 0, 643, 0x140000, "War Is Peace" };

resource 'CNTL' (3) { {0, 0, 40, 40}, 0, visible, 0, 0, 652, 0x1e, "" };

resource 'CNTL' (4) { {0, 0, 30, 178}, 2, visible, 14, 768, 641, 0x280000, "Evolve Or Dissolve" };

resource 'CNTL' (5) { {240, 300, 297, 361}, 0, visible, 0, 0, 645, 0x330032, "\0x00" };

resource 'CNTL' (6) { {0, 0, 78, 80}, 0, visible, 0, 0, 647, 0x3d003c, "" };

resource 'CNTL' (7) { {0, 0, 50, 45}, 0, visible, 0, 0, 646, 0x46, "" };

resource 'CNTL' (8) { {0, 0, 200, 116}, 0, visible, 0, 0, 644, 0x50, "" };

resource 'CNTL' (9) { {0, 12, 50, 100}, 0, visible, 0, 0, 648, 0x5a, "" };

resource 'CNTL' (10) { {0, 0, 66, 80}, 0, visible, 0, 0, 649, 0x650064, "" };

resource 'CNTL' (11) { {0, 0, 36, 36}, 0, visible, 0, 0, 650, 0x6e, "" };

resource 'CNTL' (12) { {0, 0, 32, 32}, 0, visible, 0, 0, 651, 0x790078, "" };

resource 'CNTL' (13) { {0, 0, 40, 38}, 0, visible, 0, 0, 652, 0x82, "\0x00" };

resource 'CNTL' (14) { {240, 300, 277, 337}, 0, visible, 0, 0, 653, 0x8d008c, "\0x00" };

resource 'CNTL' (15) { {0, 0, 40, 40}, 0, visible, 0, 0, 654, 0x96, "\0x00" };

resource 'CNTL' (16) { {0, 0, 40, 40}, 0, visible, 0, 0, 655, 0xa100a0, "\0x00" };

resource 'CNTL' (17) { {0, 0, 187, 85}, 0, visible, 0, 0, 645, 0xab00aa, "" };

resource 'CNTL' (18) { {0, 0, 18, 120}, 3, visible, 10, 0, 640, 0x0, "Turn Off Some Buttons" };

resource 'CNTL' (19) { {0, 0, 18, 150}, 3, visible, 12, 0, 642, 0x0, "Turn On Some Buttons" };

resource 'CNTL' (20) { {0, 0, 24, 24}, 0, visible, 0, 0, 645, 0xc900c8, "" };

resource 'CNTL' (21) { {0, 0, 12, 90}, 3, visible, 9, 0, 640, 0x0, "\0xa91987 Stan Krute" };

userdefined resource 'CuCo' (0) { pstring: "custom controls demo version 1.0 c1987 by Stan Krute all rights reserved" };

resource 'DITL' (1) { { {238, 326, 266, 456}, Control {enabled, 1}; {7, 6, 43, 151}, Control {enabled, 2}; {129, 285, 169, 325}, Control {enabled, 3}; {174, 197, 204, 375}, Control {enabled, 4}; {201, 120, 258, 181}, Control {enabled, 5}; {4, 230, 82, 310}, Control {enabled, 6}; {12, 169, 62, 214}, Control {enabled, 7}; {71, 0, 271, 116}, Control {enabled, 8}; {213, 199, 263, 287}, Control {enabled, 9}; {69, 141, 135, 221}, Control {enabled, 10}; {5, 320, 41, 356}, Control {enabled, 11}; {138, 226, 170, 258}, Control {enabled, 12}; {90, 238, 130, 276}, Control {enabled, 13}; {105, 335, 142, 372}, Control {enabled, 14}; {47, 330, 87, 370}, Control {enabled, 15}; {148, 142, 188, 182}, Control {enabled, 16}; {22, 377, 209, 462}, Control {enabled, 17}; {49, 18, 67, 138}, Control {enabled, 18}; {214, 305, 232, 455}, Control {enabled, 19}; {101, 297, 125, 321}, Control {enabled, 20}; {5, 364, 17, 454}, Control {enabled, 21} } };

resource 'DITL' (210) { { {10, 10, 178, 286}, Picture {enabled, 210} } };

resource 'DLOG' (1) { {0, 0, 272, 462}, 1, invisible, noGoAway, 0x0, 1, "New Dialog" };

resource 'DLOG' (210) { {0, 0, 188, 296}, 1, invisible, noGoAway, 0x0, 210, "New Dialog" };

resource 'FREF' (128) { 'APPL', 0, "" };

resource 'MENU' (1) { 1, 0, 0xffffffff, enabled, "custom controls demo", {

} };

resource 'STR ' (20) { "Peace Is War" };

resource 'STR ' (40) { "Grow Up Or Blow Up" };

[LISTING TWO]

rectCDEF.Job asm rectCDEF.asm Exec QUED/M 2.04 link rectCDEF.link Exec QUED/M 2.04 RMaker rectCDEF.R0 Exec QUED/M 2.04 RMaker rectCDEF.R1 Exec QUED/M 2.04 RMaker rectCDEF.R2 custom controls demo QUED/M 2.04

[LISTING THREE]

*----------------------------------- file information ----------------------------- * * * * rectCDEF.Asm * * * * * * Assembly language source code for several styles of rectangular button controls * * The assembled code is meant to be a CDEF resource * * * * The CDEF provides 16 styles of rectangular button controls * * A button can: (1) contain text, an ICON, or a PICT * * (2) can be outlined, or shadow-outlined * * (3) if it contains an ICON or a PICT, can be bare * * (4) indicate pressing via inversion or a content change * * * * Edited with QUED/M 2.04 * * Compiled under MDS 2.01 * * * * 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: 6:51 pm PST November 16, 1987 * * Spacestamp: 18617 Camp Creek Road Hornbrook, California 96044 * * * * This file looks good in 9 point Courier, QUED/M 2.04 tabs set to 3 * * * *------------------------------------------------------------------ *

* --------------------------------- using the CDEF -------------------------------- *

; details on using the CDEF resource produced by this code

; a custom control is most easily specified via a CNTL resource, ; which uses a procID to indicate the CDEF resource ID and variation code

; for this CDEF, the resource ID is 40

; there are 16 variations, as follows:

; variation content border highlighting via proc ID ; --------- ------- -------- ---------------- ----- ; 0 text outlined inversion 640 ; 1 text outlined content change 641 ; 2 text shadowed inversion 642 ; 3 text shadowed content change 643

; 4 PICT bare inversion 644 ; 5 PICT bare content change 645 ; 6 PICT outlined inversion 646 ; 7 PICT outlined content change 647 ; 8 PICT shadowed inversion 648 ; 9 PICT shadowed content change 649

; 10 ICON bare inversion 650 ; 11 ICON bare content change 651 ; 12 ICON outlined inversion 652 ; 13 ICON outlined content change 653 ; 14 ICON shadowed inversion 654 ; 15 ICON shadowed content change 655

; for TEXT variations, select a font via the CNTL's contrlValue field: ; -1 for the window's font, 0 for the System font, 1 for the application font, ; anything else a standard Mac font number ; in the case of a font other than the System or window's font, store the font ; style in the high byte of the contrlMin field, and the font size in the ; low byte of the contrlMax field

; for PICT and ICON variations, store a resource ID indicating the PICT or ICON ; resource in the low word of the CNTL's refCon field

; for variations that indicate highlighting via a content change, store a resource ; ID indicating the highlighted-state STR (for text variations), PICT, or ; ICON resource in the high word of the CNTL's refCon field

; when figuring the size of a text button's boundsRect : ; be sure to size the button so the text will fit. Successive approximation works ; well. Rule of thumb for an initial height: 8 greater than the font size.

; when figuring the size of a PICTure button's CNTL's boundsRect : ; if the picture has no outline, try a boundsRect that matches the PICT's boundsRect ; if the picture is outlined, try a boundsRect that's at least 4 wider and 4 ; higher -- that size lets the picture perfectly match the button's interior ; pictures in larger boundsRects get centered

; when figuring the size of a ICON button's CNTL's boundsRect : ; if the icon has no outline, try a boundsRect that matches the ICON's boundsRect ; if the icon is outlined, try a boundsRect that's at least 4 wider and 4 ; higher -- that size lets the icon perfectly match the button's interior ; icons in larger boundsRects get centered

*----------------------------------- include files ---------------------------------*

; standard Mac definitions Include MacTraps.D ; condensed trap file Include SysEqu.D ; system equates Include ToolEqu.D ; toolbox equates Include QuickEqu.D ; toolbox equates

; our stuff Include rectCDEFEqu.Txt ; private definitions for this file

*------------------------------- common entry point --------------------------------*

rectCDEFProc

; provide a stack frame LINK A6,#-autoBytes ; set frame pointer, with enough ; bytes for automatic variables ; save some registers MOVEM.L D3-D7/A2-A4,-(SP) ; save some work registers

; the key to 68000 code : fill those registers LEA param(A6),A0 ; A0 points to param MOVE.L (A0)+,D3 ; D3 holds param ( usage varies ) MOVE.W (A0)+,D7 ; D7 holds message (operation selector ) MOVEA.L (A0)+,A2 ; A2 holds theControl (control record handle ) CLR.L D4 ; clear out D4 MOVE.W (A0)+,D4 ; varCode into D4 MOVE.B varFlagTable(D4),D4 ; D4 holds a byte of variation flags

; set a default function result of 0, while A0's pointing in the right direction CLR.L (A0)

; lock the control record down MOVEA.L A2,A0 _HLock

; get a pointer to the control record MOVE.L (A2),A2

; lock down and get a pointer to any control data block MOVE.L contrlData(A2),D0 ; grab the handle BEQ caseOut ; jump if NIL handle

; we've got a control data block, so lock it MOVEA.L D0,A3 ; copy the handle MOVEA.L A3,A0 ; lock the block _HLock MOVEA.L (A3),A3 ; get a pointer to the control data block

caseOut ; case out on the message ; just jump off a table of routine offsets ADD.W D7, D7 ; double the message integer MOVE.W messageTable(D7),D0 ; grab a table offset JSR messageTable(D0) ; jump to messageTable + offset

; clean up and go home ; if there was a control data block, unlock it MOVE.L contrlData(A2),D0 ; grab the handle BEQ unlockRec ; jump if NIL handle

; we've got a control data block, so unlock it MOVEA.L D0,A0 ; move the block's handle into place _HUnlock ; and unlock it

unlockRec ; unlock the control record MOVEA.L theControl(A6),A0 _HUnlock

; restore the saved registers MOVEM.L (SP)+,D3-D7/A2-A4

; remove the stack frame UNLK A6

; fetch the return address MOVEA.L (SP)+, A0

; set stack pointer to the function result ADD.L #theResult-param,SP

; we're outta here JMP (A0)

*---------------------------- the message jump table ------------------------------ *

; there are nine possible messages

messageTable DC.W doDrawCntl-messageTable ; draw the control DC.W doTestCntl-messageTable ; test the control DC.W doCalcCCntl-messageTable ; calculate the control's region DC.W doInitCntl-messageTable ; do control initialization chores DC.W doDispCntl-messageTable ; do control disposal chores DC.W doPosCntl-messageTable ; reposition & update the control DC.W doThumbCntl-messageTable ; calculate control dragging params DC.W doDragCntl-messageTable ; drag the control DC.W doAutoTrack-messageTable ; do the control's action proc

*---------------------------- the variations flag table --------------------------- *

; there are sixteen control variations

; eight bits are used to flag eight qualities of a control variation ; from bit 7 (hi) to bit 0 (lo), the bits are symbolically named : ; textBit - pictBit - iconBit - outBit - shadBit - bareBit - invBit - chngBit

varFlagTable DC.B %10010010 ; text - outlined - invert DC.B %10010001 ; text - outlined - content change DC.B %10011010 ; text - shadowed - invert DC.B %10011001 ; text - shadowed - content change

DC.B %01000110 ; pict - bare - invert DC.B %01000101 ; pict - bare - content change DC.B %01010010 ; pict - outlined - invert DC.B %01010001 ; pict - outlined - content change DC.B %01011010 ; pict - shadowed - invert DC.B %01011001 ; pict - shadowed - content change

DC.B %00100110 ; icon - bare - invert DC.B %00100101 ; icon - bare - content change DC.B %00110010 ; icon - outlined - invert DC.B %00110001 ; icon - outlined - content change DC.B %00111010 ; icon - shadowed - invert DC.B %00111001 ; icon - shadowed - content change

*---------------------------------- doDrawCntl -------------------------------------*

; the application wants the CDEF to draw the control

doDrawCntl ; if control is invisible, do nothing TST.B contrlVis(A2) ; non-zero if the control is visible BEQ drawn ; it's invisible, so no need to draw it

; save the entry pen state PEA entryPenState(A6) _GetPenState

; normalize the pen state _PenNormal

; save a copy of the entry clip region CLR.L -(SP) ; get a new region _NewRgn MOVE.L (SP),entryClipRgnCopy(A6) ; copy the entry clip region into it _GetClip

shadOutTest ; see if a shadowed outline is to be drawn BTST #shadBit,D4 ; check it out BEQ simpOutTest ; no shadowed outline, on to the simple outline test BSR doShadOutline ; draw a shadowed outline BSR shadOutIntClip ; set a clip rect for the interior BRA interiorDrawing ; on to the interior tests

simpOutTest ; see if a simple outline is to be drawn BTST #outBit,D4 ; check it out BEQ noOutNoTest ; if not, on to the no-outline duties BSR doSimpOutline ; draw a simple outline BSR simpOutIntClip ; set a clip rect for the interior BRA interiorDrawing ; on to the interior tests

noOutNoTest BSR noOutIntClip ; set a clip rect for the interior

interiorDrawing textTest ; see if the button will contain text BTST #textBit,D4 ; text ? BEQ pictTest ; no, so next test BSR doTextInterior ; yes, so draw text interior BRA drawn ; and jump on

pictTest BTST #pictBit,D4 ; pict ? BEQ iconNoTest ; no, so it's an icon button BSR doPictInterior ; yes, so draw pict interior BRA drawn ; and jump on

iconNoTest BSR doIconInterior ; draw icon interior

drawn ; all drawn

; restore the entry pen state PEA entryPenState(A6) _SetPenState

; restore the entry clipping region, and get rid of its holder MOVE.L entryClipRgnCopy(A6),-(SP) MOVE.L (SP),-(SP) _SetClip _DisposRgn

; so long RTS

*--------------------------------- doShadOutline ---------------------------------- *

; draw a shadowed outline for a button

doShadOutline ; move to the starting point for the horizontal shadow line: (left+2,bottom) MOVE.W contrlRect+left(A2),-(SP) ADDQ.W #0002,(SP) MOVE.W contrlRect+bottom(A2),-(SP) _MoveTo

; draw a line to the right side of the horizontal shadow line: (right,bottom) MOVE.W contrlRect+right(A2),-(SP) MOVE.W contrlRect+bottom(A2),-(SP) _LineTo

; draw a line to the top of the vertical shadow line: (right,top+2) MOVE.W contrlRect+right(A2),-(SP) MOVE.W contrlRect+top(A2),-(SP) ADDQ.W #$0002,(SP) _LineTo

; now draw an outline rect at (top,left,bottom,right) PEA contrlRect(A2) _FrameRect

; all done RTS

*-------------------------------- doSimpOutline ----------------------------------- *

; draw a simple outline for a button

doSimpOutline

; draw an outline rect at (top,left,bottom,right) PEA contrlRect(A2) _FrameRect

; all done RTS

*--------------------------------- shadOutIntClip --------------------------------- *

; set the clip region for the interior of a shadowed outlined button

shadOutIntClip

; use the rect at (top+2,left+2,bottom-2,right-2) MOVE.L contrlRect+top(A2),interiorClipRect+top(A6) MOVE.L contrlRect+bottom(A2),interiorClipRect+bottom(A6) ADDI.L #$00020002,interiorClipRect+top(A6) SUBI.L #$00020002,interiorClipRect+bottom(A6) PEA interiorClipRect(A6) _ClipRect

; all done RTS

*------------------------------- simpOutIntClip ----------------------------------- *

; set the clip region for the interior of a simply outlined button

simpOutIntClip

; use the rect at (top+2,left+2,bottom-2,right-2) MOVE.L contrlRect+top(A2),interiorClipRect+top(A6) MOVE.L contrlRect+bottom(A2),interiorClipRect+bottom(A6) ADDI.L #$00020002,interiorClipRect+top(A6) SUBI.L #$00020002,interiorClipRect+bottom(A6) PEA interiorClipRect(A6) _ClipRect

; all done RTS

*-------------------------------- noOutIntClip ------------------------------------ *

; set the clip region for the interior of a non-outlined button

noOutIntClip

; use the rect at (top,left,bottom,right) MOVE.L contrlRect+top(A2),interiorClipRect+top(A6) MOVE.L contrlRect+bottom(A2),interiorClipRect+bottom(A6) PEA interiorClipRect(A6) _ClipRect

; done RTS

*-------------------------------- doTextInterior ---------------------------------- *

; draw a button's text content, in an indicated font

doTextInterior

; get a pointer to the current grafPort into A4 PEA currentGrafPort(A6) _GetPort MOVEA.L currentGrafPort(A6),A4

; we may be using a font other than the window's, so save current font settings MOVE.L txFont(A4),curFontAndFace(A6) ; font and style MOVE.W txSize(A4),curSize(A6) ; size

; the control's font is indicated by a value in the control record's contrlValue ; field: -1 for the window's font, 0 for the System font, 1 for the application ; font, anything else a standard Mac font number

; in the case of a font other than the System or window's font, the font style ; is in the ContrlMin field, and the font size is in the contrlMax field

; case out on the font MOVE.W contrlValue(A2),D0 BMI useWindowsFont

; see if the new font is the System font or something else TST.W D0 BEQ useSystemFont

useCustomFont ; set the font, style, and size for a font other than the System or window's font MOVE.W D0,txFont(A4) ; set the font MOVE.W contrlMin(A2),txFace(A4) ; set the style MOVE.W contrlMax(A2),txSize(A4) ; set the size BRA figgerFontInfo

useSystemFont ; set the font, style, and size for the system font (all zeroes will do it) CLR.L txFont(A4) ; set the font and style CLR.W txSize(A4) ; set the size

useWindowsFont ; we're using the window's current font, so font number, size, and style already set

figgerFontInfo ; so the font's set up -- let's get some more information PEA fontInfo(A6) _GetFontInfo

; from that info, we can figure the vertical positioning for the button's text ; the equation: vertPos = rectBottom - (fontDescent + ((rectHeight-fontHeight)/2)) MOVE.W interiorClipRect+bottom(A6),D0 ; bottom - top gives rectHeight MOVE.L D0,D7 ; bottom will be used again SUB.W interiorClipRect+top(A6),D0 SUB.W fontInfo+ascent(A6),D0 ; then subtract fontHeight SUB.W fontInfo+descent(A6),D0 ASR.W #1,D0 ; divide what's left by 2 ADD.W fontInfo+descent(A6),D0 ; add it to the descent SUB.W D0,D7 ; then subtract it from bottom

; determine whether we'll be drawing the control's title or a STR resource ; if the control hilites via a content change and is hilited and there's a handle to ; the STR resource, we draw the STR resource ; content change ?

BTST #chngBit,D4 BEQ useTitle ; no content change, so use control's title ; control hilites via a content change ; is it hilited ? MOVE.B contrlHilite(A2),D0 BEQ useTitle ; 0=active, not hilited, so use control's title ADDQ.B #1,D0 BEQ useTitle ; 255=inactive, not hilited, so use control's title

; control hiltes via a content change, and it's hilited ; do we have a handle to the content change string ? MOVE.L firstRsrcHndl(A3),D5 BEQ useTitle ; no, so use control's title

useSTR ; okay, we'll be using a content change STR resource, and we have a non-NIL handle ; lock the resource, put a pointer to it into D5, and set a flag MOVEA.L D5,A0 ; handle into A0 _HLock ; lock the STR resource MOVEA.L D5,A0 MOVE.L (A0),D5 ; set a pointer to it MOVE.W #1,usingCCRsrc(A6) ; set a flag BRA setStrWidth

useTitle ; using the control's title ; put a pointer to the string into D5, and clear a flag LEA contrlTitle(A2),A0 MOVE.L A0,D5 ; set a pointer CLR.W usingCCRsrc(A6) ; clear a flag

setStrWidth ; now, the horizontal positioning: start by getting the button's string's width SUBQ.L #2,SP ; room for function result MOVE.L D5,-(SP) ; the string _StringWidth

; now, use that width to get the horizontal positioning ; the equation: horzPos = rectLeft + ( (rectWidth-stringWidth) / 2) MOVE.W interiorClipRect+right(A6),D0 ; get rect width ( right - left ) SUB.W interiorClipRect+left(A6),D0 SUB.W (SP)+,D0 ; subtract string width ASR.W #1,D0 ; divide what's left by two ADD.W interiorClipRect+left(A6),D0 ; and add in the left side of rect

; now we have the horizontal and vertical starting position for string drawing ; let's move there ... MOVE.W D0,-(SP) ; the horizontal starting position MOVE.W D7,-(SP) ; the vertical starting position _MoveTo

; paint a button's interior's background, according to the button's hilite state ; and whether we're drawing a content change

; test for content change imminent TST.W usingCCRsrc(A6) ; remember, we just set or cleared this flag above BNE cCImminent1 ; flag set, content change imminent

; test the hilite state bkgHSTest1 MOVE.B contrlHilite(A2),D7 BEQ hSActive1 ; 0 indicates an active button

bkgHSTest2 ADDQ.B #1,D7 ; 255 indicates an inactive button BNE hSHilit1 ; 1-253 indicates a highlighted button

hSInactive1 ; the button is inactive, so paint interior background white

cCImminent1 ; a content change is imminent, so paint interior background white

hSActive1 ; the button is active, so paint interior background white MOVEA.L (A5),A0 PEA white(A0) _PenPat

hSHilit1 ; the button is highlighted, so paint interior background black

paintInteriorBkg ; paint the button's interior background PEA interiorClipRect(A6) _PaintRect

; draw the button's interior's text, according to the button's hilite state ; and whether we're drawing a content change

; clear a flag that, if set, signals an inactive button CLR.B D6

; test for content change imminent TST.W usingCCRsrc(A6) BNE cCImminent2 ; flag set, content change imminent

; test the hilite state txtIntHSTest1 MOVE.B contrlHilite(A2),D7 BEQ txtHSActive2 ; 0 indicates an active button

txtIntHSTest2 ADDQ.B #1,D7 ; 255 indicates an inactive button BNE txtHSHilit2 ; 1-253 indicates a highlighted button

txtHSInactive2 ; the button is inactive, so we'll draw the text in black, then gray it out ADDQ.B #1,D6 ; set the inactive flag BRA drawText ; and jump to draw

txtHSHilit2 ; the button is highlighted, so we'll draw the text in white MOVE.W #srcBic,txMode(A4) ; so the black bits show as white

cCImminent2 ; a content change is imminent, so we'll draw the text in black

txtHSActive2 ; the button is active, so we'll draw the text in black

drawText ; draw the button's string MOVE.L D5,-(SP) ; pointer to the string to draw _DrawString

; if we used a STR resource, unlock it TST.W usingCCRsrc(A6) ; are we using ? BNE grayTest ; no, so jump ahead MOVE.L firstRsrcHndl(A3),A0 ; yes, so get the handle _HUnlock ; and unlock it

grayTest ; see if we've got an inactive button, in which case we gray the text out TST.B D6 BEQ txtGetNorm ; not inactive, so no graying out

; yup, we're inactive, so let's get grayed out ; set the pen pattern to gray MOVEA.L (A5),A0 PEA gray(A0) _PenPat

; and set pattern transfer mode to ANDing with the inverse of the pattern MOVE #PatBic,-(SP) _PenMode

; now paint the clip rectangle gray PEA interiorClipRect(A6) _PaintRect

txtGetNorm ; get things back to normal, in case they were changed ; normalize the text transfer mode MOVE.W #srcOr,txMode(A4)

;restore the window's prior font settings MOVE.L curFontAndFace(A6),txFont(A4) MOVE.W curSize(A6),txSize(A4)

textDrawn ; text button interior's all drawn RTS

*------------------------------------ doPictInterior -------------------------------*

; draw a PICTure interior for the button

doPictInterior ; we'll draw in the picture's bounding rectangle ; we'll try to center this rectangle horizontally and vertically ; in the control's clipping rectangle ; if the control's too small, the icon lines up against ; the top and or left sides of the control's clipping rectangle

; move clipping rectangle's top and left coords into place MOVE.L interiorClipRect+top(A6),pictRect+top(A6)

; get the clipping rect's width into D6 MOVE.W interiorClipRect+right(A6),D6 SUB.W interiorClipRect+left(A6),D6

; get a pointer and handle to the picture ; if the control hilites via a content change and is hilited, we draw the secondary ; PICT resource ; does the control hilite via a content change ? BTST #chngBit,D4 BEQ usePictOne ; doesn't hilite via a content change

; is the control hilited ? MOVE.B contrlHilite(A2),D1 BEQ usePictOne ; 0=active, not hilited ADDQ.B #1,D1 BEQ usePictOne ; 255=inactive, not hilited

usePictTwo ; we'll be using the secondary PICT resource ; get a handle and set a flag MOVE.L secondRsrcHndl(A3),D5 ; the handle BEQ usePictOne ; in case of a NIL handle MOVE.W #1,usingCCRsrc(A6) ; the flag BRA getPictPointer

usePictOne ; we'll be using the primary PICT resource ; get a handle and set a flag MOVE.L firstRsrcHndl(A3),D5 ; the handle BEQ pictDrawn ; in case of a NIL handle CLR.W usingCCRsrc(A6) ; the flag

getPictPointer ; lock the PICT, and get a pointer to it MOVEA.L D5,A0 _HLock ; lock the PICT MOVEA.L D5,A4 MOVEA.L (A4),A4 ; get a pointer

figPicWidth ; figure the picture's width MOVE.W picFrame+right(A4),D1 SUB.W picFrame+left(A4),D1

; now subtract the picture's width from the clip width SUB.W D1,D6

; if 2 or more, divide by 2 and use as horizontal offset ; if it's < 2, no horizontal offset CMPI.W #2,D6 BLT.S doPictRight

; divide and add the offset in ASR.W #1,D6 ; divide by two ADD.W D6,pictRect+left(A6)

; now do the right coord of rectangle doPictRight MOVE.W pictRect+left(A6),pictRect+right(A6) ADD.W D1,pictRect+right(A6)

; get the clipping rect's height MOVE.W interiorClipRect+bottom(A6),D0 ; get clip height into D0 SUB.W interiorClipRect+top(A6),D0

; figure the picture's height MOVE.W picFrame+bottom(A4),D1 SUB.W picFrame+top(A4),D1

; now subtract the picture's height from the clip height SUB.W D1,D0

; if 2 or more, divide by 2 and use as vertical offset ; if it's < 2, no vertical offset CMPI.W #2,D0 BLT.S doPictBottom

; divide and add the offset in ASR.W #1,D0 ; divide by two ADD.W D0,pictRect+top(A6)

; now do the bottom coord of rectangle doPictBottom MOVE.W pictRect+top(A6),pictRect+bottom(A6) ADD.W D1,pictRect+bottom(A6)

; clear the background PEA interiorClipRect(A6) _EraseRect

; okay, let's draw the picture (remember, it gets clipped to the button's interior ) MOVE.L D5,-(SP) ; the picture's handle PEA pictRect(A6) ; the destination rectangle _DrawPicture

; unlock the PICT MOVEA.L D5,A0 ; handle's still here _HUnlock

; now, adjust the picture for buttons that are hilited inverts or inactive BSR interiorAdjust

pictDrawn ; pict button interior's all drawn RTS

*---------------------------------- interiorAdjust----------------------------------*

; adjust a button's interior for buttons that are hilited inverts or inactive

interiorAdjust

; test the hilite state hiliteTest1 MOVE.B contrlHilite(A2),D0 BEQ intAdjDone ; 0 indicates a non-hilited active button

hiliteTest2 ADDQ.B #1,D0 ; 255 indicates an inactive button BNE itsHilited ; 1-253 indicates a highlighted button

itsInactive ; the button is inactive, so we'll gray it out ; set the pen pattern to gray MOVEA.L (A5),A0 PEA gray(A0) _PenPat

; and set pattern transfer mode to ANDing with the inverse of the pattern MOVE #PatBic,-(SP) _PenMode

; now paint the clip rectangle gray PEA interiorClipRect(A6) _PaintRect

; leave BRA intAdjDone

itsHilited ; the button is highlighted ; test for being an invert TST.W usingCCRsrc(A6) BNE intAdjDone ; a content changer, so we done

; the button's a hilited invert, so invert it PEA interiorClipRect(A6) _InverRect

intAdjDone ; all done with the adjustment RTS

*---------------------------------- doIconInterior ---------------------------------*

; draw an ICONic interior for the button

doIconInterior

; we'll draw in an icon-sized rectangle ; we'll try to center this rectangle horizontally and vertically ; in the control's clipping rectangle ; if the control's too small, the icon lines up against ; the top and or left sides of the control's clipping rectangle

; move clipping rectangle's top and left coords into place MOVE.L interiorClipRect+top(A6),iconRect+top(A6)

; get the clipping rect's width into D0 MOVE.W interiorClipRect+right(A6),D0 SUB.W interiorClipRect+left(A6),D0

; now subtract the icon width SUBI.W #iconSize,D0

; if 2 or more, divide by 2 and use as horizontal offset ; if it's < 2, no horizontal offset CMPI.W #2,D0 BLT.S doIconRight

; divide and add the offset in ASR.W #1,D0 ; divide by two ADD.W D0,iconRect+left(A6)

; now do the right coord of rectangle doIconRight MOVE.W iconRect+left(A6),iconRect+right(A6) ADD.W #iconSize,iconRect+right(A6)

; get the clipping rect's height MOVE.W interiorClipRect+bottom(A6),D0 ; get clip height into D0 SUB.W interiorClipRect+top(A6),D0

; now subtract the icon height from the clip height SUBI.W #iconSize,D0

; if 2 or more, divide by 2 and use as vertical offset ; if it's < 2, no vertical offset CMPI.W #2,D0 BLT.S doIconBottom

; divide and add the offset in ASR.W #1,D0 ; divide by two ADD.W D0,iconRect+top(A6)

; now do the bottom coord of rectangle doIconBottom MOVE.W iconRect+top(A6),iconRect+bottom(A6) ADD.W #iconSize,iconRect+bottom(A6)

; get a handle to the icon ; if the control hilites via a content change and is hilited, we draw the secondary ; ICON resource ; content change ? BTST #chngBit,D4 BEQ useIconOne ; hilited ? MOVE.B contrlHilite(A2),D1 BEQ useIconOne ADDQ.B #1,D1 BEQ useIconOne

useIconTwo ; we'll be using the secondary ICON resource ; get a handle and set a flag MOVE.L secondRsrcHndl(A3),D5 ; the handle BEQ useIconOne ; in case of a NIL handle MOVE.W #1,usingCCRsrc(A6) ; the flag BRA clearBack ; and jump ahead

useIconOne ; we'll be using the primary ICON resource ; get a handle and set a flag MOVE.L firstRsrcHndl(A3),D5 ; the handle BEQ iconDrawn ; in case of a NIL handle CLR.W usingCCRsrc(A6) ; the flag

clearBack ; clear the background PEA interiorClipRect(A6) _EraseRect

; okay, let's draw the icon (remember, it gets clipped to the button's interior ) PEA iconRect(A6) ; the destination rectangle MOVE.L D5,-(SP) ; the icon's handle _PlotIcon

; now, adjust the icon for buttons that are hilited inverts or inactive BSR interiorAdjust

iconDrawn ; icon button interior's all drawn RTS

*----------------------------------------- doTestCntl ----------------------------- *

; the application wants CDEF to test a point to see if it's in an active control

doTestCntl

; first, see if the control's even active, or if it's in one of the two inactive modes MOVE.B contrlHilite(A2),D7 ;inactive control ? CMPI.B #inact254,D7 BEQ setReturn2 ;yes, so return appropriate code CMPI.B #inact255,D7 BEQ setReturn3 ;yes, so return appropriate code

; the control has been found to be active ; now find out if the supplied point is in the control's rectangle

SUBQ.L #2,SP ; room for the function result MOVE.L D3,-(SP) ; param holds the mouse coords PEA contrlRect(A2) ; pointer to the control's rectangle _PtInRect TST.B (SP)+ ; scan result while chucking BEQ setReturn3 ; if point isn't in the control ...

; okay, the control's active, and the mouse point's in it ; the control has no separate parts, so we return the whole control's part number setReturn1 MOVE.L #wholePartNumber,theResult(A6) RTS

; and here are the return codes for the other possibilities setReturn2 MOVE.L #254,theResult(A6) ;inactive type 254 RTS

setReturn3 MOVE.L #0,theResult(A6) ;inactive type 255 RTS ;or not in control

*------------------------------------- doCalcCCntl -------------------------------- *

; calculate the control's region

doCalcCCntl MOVE.L D3,-(SP) ; param holds the waiting handle PEA contrlRect(A2) ; use the control's rectangle _RectRgn RTS

*------------------------------------- doInitCntl --------------------------------- *

; do any special initialization of the control

; in this case, set up a control data block ; then load in any necessary resources, and store handles in the control data block

doInitCntl

; try to get a control data block MOVE.L #cntlDataBlokSize,D0 _NewHandle,CLEAR

; store the result MOVE.L A0,contrlData(A2) BEQ initDone ; if we got no block, leave

lockDataBlock MOVEA.L A0,A3 ; save a copy of the block's handle _Hlock ; lock the block down MOVEA.L (A3),A3 ; get a pointer to the locked block

initTextTest ; is this is a text control ? BTST #textBit,D4 ; the test BEQ initPictTest ; no, so jump on

;got a text control - does it indicate hiliting via a content change ? BTST #chngBit,D4 ; the test BEQ initPictTest ; no, so jump on

; got a text control with content change hiliting, so load in the string resource and lock it SUBQ.L #4,SP ; room for a handle MOVE.L #'STR ',-(SP) ; the resource type MOVE.W contrlRFcon(A2),-(SP) ; the resource id _GetResource ; try to grab that resource MOVE.L (SP)+,firstRsrcHndl(A3) ; store its handle (possibly NIL) BRA initDone

initPictTest ; see if this is a PICT control BTST #pictBit,D4 ; the test BEQ initIconTest ; no, so jump on

; got a pict control, so load in the main PICT SUBQ.L #4,SP ; room for a handle MOVE.L #'PICT',-(SP) ; the resource type MOVE.W contrlRFcon+2(A2),-(SP) ; the resource id _GetResource ; try to grab that resource MOVE.L (SP)+,firstRsrcHndl(A3) ; store its handle (possibly NIL)

isPictCC ; does it indicate hiliting via a content change ? BTST #chngBit,D4 ; the test BEQ initDone ; no, so done

; got a pict control with content change, so load in the secondary PICT SUBQ.L #4,SP ; room for a handle MOVE.L #'PICT',-(SP) ; the resource type MOVE.W contrlRFcon(A2),-(SP) ; the resource id _GetResource ; try to grab that resource MOVE.L (SP)+,secondRsrcHndl(A3) ; store its handle (possibly NIL) BRA initDone ; done

initIconTest ; no test needed; we have an icon control, so load in the main ICON SUBQ.L #4,SP ; room for a handle MOVE.L #'ICON',-(SP) ; the resource type MOVE.W contrlRFcon+2(A2),-(SP) ; the resource id _GetResource ; try to grab that resource MOVE.L (SP)+,firstRsrcHndl(A3) ; store its handle (possibly NIL)

isIconCC ; does it indicate hiliting via a content change ? BTST #chngBit,D4 ; the test BEQ initDone ; no, so done

; got an icon control with content change, so load in the secondary ICON SUBQ.L #4,SP ; room for a handle MOVE.L #'ICON',-(SP) ; the resource type MOVE.W contrlRFcon(A2),-(SP) ; the resource id _GetResource ; try to grab that resource MOVE.L (SP)+,secondRsrcHndl(A3) ; store its handle (possibly NIL)

initDone ; that's it for initialization RTS

*------------------------------------- doDispCntl --------------------------------- *

; do any special disposal operations for the control

; in this case, release resources whose handles are stored in the control's ; data block, then release that block

doDispCntl ; see if we ever got a control data block TST.L contrlData(A2) BEQ dispDone ; no block, so leave

checkFirst ; see if there's a handle in the first slot TST.L firstRsrcHndl(A3) ; got a real handle ? BEQ checkSecond ; no, it's NIL, so jump ahead MOVE.L firstRsrcHndl(A3),-(SP) ; handle okay, so let go of that resource _ReleaseResource

checkSecond ; see if there's a handle in the second slot TST.L secondRsrcHndl(A3) ; got a real handle ? BEQ dropDataBlock ; no, it's NIL, so jump ahead MOVE.L secondRsrcHndl(A3),-(SP) ; handle okay, so let go of that resource _ReleaseResource

dropDataBlock ; now get rid of the control's data block MOVEA.L contrlData(A2),A0 _DisposHandle

dispDone RTS

*------------------------------------- doPosCntl ---------------------------------- *

; the position routine ; in this case, do nothing

doPosCntl RTS

*------------------------------------ doThumbCntl --------------------------------- *

; the thumb routine ; in this case, do nothing

doThumbCntl RTS

*------------------------------------ doDragCntl ---------------------------------- *

; the drag routine ; in this case, do nothing

doDragCntl RTS

*------------------------------------ doAutoTrack --------------------------------- *

; the track routine ; in this case, do nothing

doAutoTrack RTS

[LISTING FOUR]

*----------------------------------- file information ----------------------------- * * * * rectCDEFEqu.Txt * * * * * * Private definitions for rectCDEF.Asm * * * * Edited with QUED/M 2.04 * * Compiled under MDS 2.01 * * * * 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: 1:56 am EST September 29, 1987 * * Spacestamp: 21E Halcyon Drive West Yarmouth, Massacusetts 02673 * * * * This file looks good in 9 point Courier, QUED/M 2.04 tabs set to 3 * * * *---------------------------------------------------------------- *

*----------------------------------- equates -------------------------------------- *

; stack frame offsets for function parameters returnAddress EQU 4 ; return address' offset in frame param EQU 8 ; for long-word-size parameter message EQU 12 ; control message identifies desired operation theControl EQU 14 ; calling control's handle's offset in frame varCode EQU 18 ; which variation of the control theResult EQU 20 ; function result offset in frame

; stack frame offsets for automatic (local) variables entryPenState EQU -18 ; room to hold entry pen state (18 bytes) currentGrafPort EQU -22 ; pointer to current grafPort ( 4 bytes ) curFontAndFace EQU -26 ; saved font number and style ( 4 bytes ) curSize EQU -28 ; saved font size ( 2 bytes ) fontInfo EQU -36 ; information about current font ( 8 bytes ) entryClipRgnCopy EQU -40 ; handle to copy of entry clip region (4 bytes) interiorClipRect EQU -48 ; a clipping rectangle (8 bytes) pictRect EQU -56 ; a PICTure bounding rectangle (8 bytes) iconRect EQU -56 ; an ICON bounding rectangle (8 bytes) usingCCRsrc EQU -58 ; flags use ofcontent change resource (2 bytes) autoBytes EQU 58 ; size in bytes of automatic variable area

; hilite codes inact254 EQU 254 ; hilite code to inactivate control inact255 EQU 255 ; hilite code to inactivate control

; icon stuff iconSize EQU 32 ; width and height of icon

; id's for our control definition wholePartNumber EQU 10 ; part number for our whole control

; test bits textBit EQU 7 ; if set, it's a text button pictBit EQU 6 ; if set, it's a PICT button iconBit EQU 5 ; if set, it's an ICON button outBit EQU 4 ; if set, the button is outlined shadBit EQU 3 ; if set, the button's outline is shadowed bareBit EQU 2 ; if set, the button has no outline invBit EQU 1 ; if set, the button shows hiliting via inversion chngBit EQU 0 ; if set, the button shows hiliting via content change

; the control's data block cntlDataBlokSize EQU 8 ; size of the control's data block firstRsrcHndl EQU 0 ; offset of first data block field

[LISTING FIVE]

; this file is called rectCDEF.Link

; )1987 by Stan Krute -- all rights reserved

; timestamp: 12:57 am EST September 26, 1987 ; spacestamp: 21E Halcyon Drive West Yarmouth, Mass. 02673

; turn off code listing to map file ]

; the name of the input file is rectCDEF.Rel rectCDEF.REL

; the name of the output file is rectCDEF /Output rectCDEF

; set a type and creator for the output file /Type '????' '????'

; end of Linker control file

[LISTING SIX]

* this file is called rectCDEF.R0

* )1987 by Stan Krute -- all rights reserved

* timestamp: 12:53 am EST September 26, 1987 * spacestamp: 21E Halcyon Drive West Yarmouth, Mass. 02673

* name of the output file is rectCDEF.Rsrc, type is RSRC, creator is RSED rectCDEF.Rsrc RSRCRSED

* grab code resource 1 and turn it into a purgeable control definition TYPE CDEF = PROC ,40 (32) rectCDEF

[LISTING SEVEN]

* this file is called rectCDEF.R1

* )1987 by Stan Krute -- all rights reserved

* timestamp: 12:41 am EST September 26, 1987 * spacestamp: 21E Halcyon Drive West Yarmouth, Mass. 02673

* name of the input and output file is custom controls demo !:custom controls demo

* grab CDEF resource from rectCDEF.Rsrc INCLUDE :rectCDEF.Rsrc

[LISTING EIGHT]

* this file is called rectCDEF.R2

* 1987 by Stan Krute -- all rights reserved

* timestamp: 12:41 am EST September 26, 1987 * spacestamp: 21E Halcyon Drive West Yarmouth, Mass. 02673

* name of the input and output file is custom controls demo PROJ.rsrc !:custom controls demo PROJ.rsrc

* grab CDEF resource from rectCDEF.Rsrc INCLUDE :rectCDEF.Rsrc

Previous Page | 1 | 2 | 3 | 4 | 5 | 6
TOP 5 ARTICLES
No Top Articles.
DR. DOBB'S CAREER CENTER
Ready to take that job and shove it? open | close
Search jobs on Dr. Dobb's TechCareers
Function:

Keyword(s):

State:  
  • Post Your Resume
  • Employers Area
  • News & Features
  • Blogs & Forums
  • Career Resources

    Browse By:
    Location | Employer | City
  • Most Recent Posts:
    MEDIA CENTER  more
    NetSeminar
    Modernize your Development by Moving Build and Code Quality Upstream
    Moderated by Jon Erickson, Editor-in-Chief of Dr. Dobb's, this interactive panel discussion brings industry experts Anders Wallgren, CTO of Electric Cloud and Gwyn Fisher, CTO of Klocwork together for a candid discussion of the cost savings, productivity and quality benefits that can be achieved by stabilizing builds and code quality as early in the development cycle as possible.

    The reality of today's development environment - geographically distributed teams, the use of Agile development practices, increasing application complexity, etc. - is straining the viability of the traditional coding, build and release process. To stay ahead of the curve, development teams are modernizing their approach to dealing with these issues, and as a result are achieving new levels of development productivity. Register for the webcast.
    Date: Wednesday, July 15, 2009
    Time: 11 am PT/2 pm ET
    Modernize your Development by Moving Build and Code Quality Upstream
    Moderated by Jon Erickson, Editor-in-Chief of Dr. Dobb's, this interactive panel discussion brings industry experts Anders Wallgren, CTO of Electric Cloud and Gwyn Fisher, CTO of Klocwork together for a candid discussion of the cost savings, productivity and quality benefits that can be achieved by stabilizing builds and code quality as early in the development cycle as possible.

    The reality of today's development environment - geographically distributed teams, the use of Agile development practices, increasing application complexity, etc. - is straining the viability of the traditional coding, build and release process. To stay ahead of the curve, development teams are modernizing their approach to dealing with these issues, and as a result are achieving new levels of development productivity. Register for the webcast.
    Date: Wednesday, July 15, 2009
    Time: 11 am PT/2 pm ET
                                   
    INFO-LINK

    Resource Links: