Dr. Dobb's is part of the Informa Tech Division of Informa PLC

This site is operated by a business or businesses owned by Informa PLC and all copyright resides with them. Informa PLC's registered office is 5 Howick Place, London SW1P 1WG. Registered in England and Wales. Number 8860726.


Channels ▼
RSS

Design

To the Macs


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]

<a name="008e_0015">

/* 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"
};


<a name="008e_0016"><a name="008e_0016">
<a name="008e_0017">
[LISTING TWO]
<a name="008e_0017">

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



<a name="008e_0018"><a name="008e_0018">
<a name="008e_0019">
[LISTING THREE]
<a name="008e_0019">

*----------------------------------- 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



<a name="008e_001a"><a name="008e_001a">
<a name="008e_001b">
[LISTING FOUR]
<a name="008e_001b">

*----------------------------------- 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



<a name="008e_001c"><a name="008e_001c">
<a name="008e_001d">
[LISTING FIVE]
<a name="008e_001d">

;       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



<a name="008e_001e"><a name="008e_001e">
<a name="008e_001f">
[LISTING SIX]
<a name="008e_001f">

*    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



<a name="008e_0020"><a name="008e_0020">
<a name="008e_0021">
[LISTING SEVEN]
<a name="008e_0021">


*    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




<a name="008e_0022"><a name="008e_0022">
<a name="008e_0023">
[LISTING EIGHT]
<a name="008e_0023">

*    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





Related Reading


More Insights






Currently we allow the following HTML tags in comments:

Single tags

These tags can be used alone and don't need an ending tag.

<br> Defines a single line break

<hr> Defines a horizontal line

Matching tags

These require an ending tag - e.g. <i>italic text</i>

<a> Defines an anchor

<b> Defines bold text

<big> Defines big text

<blockquote> Defines a long quotation

<caption> Defines a table caption

<cite> Defines a citation

<code> Defines computer code text

<em> Defines emphasized text

<fieldset> Defines a border around elements in a form

<h1> This is heading 1

<h2> This is heading 2

<h3> This is heading 3

<h4> This is heading 4

<h5> This is heading 5

<h6> This is heading 6

<i> Defines italic text

<p> Defines a paragraph

<pre> Defines preformatted text

<q> Defines a short quotation

<samp> Defines sample computer code text

<small> Defines small text

<span> Defines a section in a document

<s> Defines strikethrough text

<strike> Defines strikethrough text

<strong> Defines strong text

<sub> Defines subscripted text

<sup> Defines superscripted text

<u> Defines underlined text

Dr. Dobb's encourages readers to engage in spirited, healthy debate, including taking us to task. However, Dr. Dobb's moderates all comments posted to our site, and reserves the right to modify or remove any content that it determines to be derogatory, offensive, inflammatory, vulgar, irrelevant/off-topic, racist or obvious marketing or spam. Dr. Dobb's further reserves the right to disable the profile of any commenter participating in said activities.

 
Disqus Tips To upload an avatar photo, first complete your Disqus profile. | View the list of supported HTML tags you can use to style comments. | Please read our commenting policy.