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....
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:
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.
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.
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.
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.
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.
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.
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.
A C function prototype for a CDEF looks like this:
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.
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.
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.
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.
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.
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.
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.
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]MultiFinder Arrives
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.).
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?
HyperCard, Too
One of the Great Docs
Code Corner: Cutom CDEFs, Part 2
Resource Revelations
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
Custom Controls Demo's Resources
CDEF Development Files
CDEF Refresher
pascal long someCDEF ( int varCode, ControlHandle theControl, int message, long param );
The Clean Nut Can't Shut Up
Handling Controls
Outlines and Interior Clip Regions
Drawing Button Interiors
Procedural wrap up
Bibliography
Vendors
<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