TweetFollow Us on Twitter

Bitmaps with ResEdit
Volume Number:6
Issue Number:2
Column Tag:Resource Roundup

Related Info: Resource Manager Picture Utilities Quickdraw
List Manager

BitMaps with ResEdit

By Mike Scanlin, T/Maker Company, Mountain View, CA

Note: Source code files accompanying article are located on MacTech CD-ROM or source code disks.

This article consists of two parts: first, a custom resource type modeled after the BitMap data structure (along with a couple of MPW C and Lightspeed Pascal routines to manipulate these custom resources) and second, an editor you can paste into ResEdit that allows you to do fatBit editing of these custom resource types.

AN OVERSIGHT

It has always seemed to me that the people at Apple were too preoccupied with feeding the dogs and cows the day they decided on the standard resource types because they forgot one. They gave us ‘ICON’, ‘SICN’, ‘CURS’, and other types of resources to represent bit images, but nothing general purpose for plain old BitMaps. It seems like it’s a bit overkill to use a ‘PICT’ to represent a BitMap. We have the BitMap data type, but no corresponding resource type. What follows is a description of one possible structure of a resource type for BitMaps.

(Note: all of the C source code in this article is MPW C 3.0 and assumes ‘short’ is a 16 bit quantity. You shouldn’t have to change anything if you’re using Lightspeed C. The Pascal source code is Lightspeed Pascal and should be compatible with other Pascals.)

Most of you are probably familiar with the BitMap data type (see Inside Macintosh, vol. I, pgs. 144-5 for more information):

In C:

/* 1 */

struct BitMap {
    Ptr baseAddr;/* ptr to bit image */
    short rowBytes;/* row width */
    Rectbounds;  /* boundary rect */
};

In Pascal:

{2}

TYPE BitMap = RECORD
 baseAddr:Ptr;
 rowBytes:integer;
 bounds:Rect;
END;

where baseAddr is a pointer to a variable length, non-relocatable bit image; rowBytes is the number of bytes in a single row (which must be even); and bounds is the rectangle enclosing the active area (the dimensions of which don’t have to be a multiple of 16, 8 or anything else). Also, the upper left corner of bounds does not have to be (0,0); it can be anything you want it to be.

Each row of a bit image is some multiple of 16 bits wide (but not all of the bits have to be used -- there can be extra bits along the right side). Because rowBytes must be even it can be thought of as twice the number of 16 bit words in a row. So a bit image with a width of 16 pixels or less has a rowBytes equal to 2; a width of 17 to 32 pixels has a rowBytes of 4; etc. The formula for converting a BitMap’s width into rowBytes is: rowBytes = (((bounds.right - bounds.left - 1) DIV 16) + 1) * 2.

Now, if we expand on the BitMap structure a little and put the bit image at the end we get the structure for the BTMP resource type:

In C:

/* 3 */

struct BTMP {
    Ptr baseAddr;/* ptr to bit image */
    short rowBytes;/* row width */
    Rectbounds;  /* boundary rect */
 short  bitImage[];/* bit image */
};

Because the syntax bitImage[] declares a variable length array that isn’t known at compile time, the compiler allocates zero bytes for that field; hence, the values sizeof(BitMap) and sizeof(BTMP) are the same.

The actual size (in bytes) of the bitImage field is calculated from its height and rowBytes and is equal to ((bounds.bottom - bounds.top) * rowBytes). So the total size of a BTMP is equal to sizeof(BTMP) + ((bounds.bottom - bounds.top) * rowBytes).

Because a BTMP inherits all of the BitMap fields, some people probably prefer this alternate definition for BTMP:

/* 4 */

struct BTMP {
 BitMap map;
   shortbitImage[];
};

This is analogous to a GrafPort being the first field of a WindowRecord. While this second definition is the theoretically preferred definition, I personally prefer not to have to go through the map field to get at the baseAddr, rowBytes and bounds fields, but there’s no reason why my parents dropping me on my head when I was little should affect the kind reader. Use which ever definition makes you happy; the data is exactly the same. If you choose the second definition, you will have to make slight changes to the source code. Change references to the BitMap fields from something like “theBitMapPtr->baseAddr” to “theBitMapPtr->map.baseAddr”.

In Pascal, the structure is:

{5}

TYPE BTMP = RECORD
 baseAddr:Ptr;
 rowBytes:integer;
 bounds:Rect;
 bitImage:array [0..0] of integer;
END;

or, for those with healthy psychological records:

{6}

TYPE BTMP = RECORD
 map:   BitMap;
 bitImage:array [0..0] of integer;
END;

In Pascal the syntax “array [0..0] of integer” will cause the compiler to allocate space for one integer, which is not what we want. The values sizeof(BitMap) and sizeof(BTMP) are no longer the same. This is why you will find a “- sizeof(integer)” in the Pascal sources (PascalDemoBTMP.p) in two places. For example, the total size of a BTMP in Pascal is equal to sizeof(BTMP) - sizeof(integer) + ((bounds.bottom - bounds.top) * rowBytes).

HOW TO USE A ‘BTMP’ RESOURCE

You can use a ‘BTMP’ resource anywhere you would use a BitMap as long as you remember two important points: it must be a locked object when you use it as a BitMap (if you pass it to something like CopyBits()) and you must be sure that the baseAddr field is set up correctly before using it.

The function PlotBitMap() (see ResBTMPEd.c for the C version or PascalDemoBTMP.p for the Pascal version) works just like PlotIcon() except that it has one more parameter. You pass it a pointer to a Rect, a handle to a ‘BTMP’ and a transfer mode. It will lock the object (if it’s not already), set up the baseAddr field, transfer the bits via PlotPBitMap(), clear the baseAddr field and then restore the handle’s locked bit state. The reason the baseAddr field is cleared is for cleanliness only. I like to have my place holder fields set to zero when they’re not being used to make sure I know they’re uninitialized when I’m debugging.

The lower level function PlotPBitMap() takes a pointer to a Rect, a pointer to a locked ‘BTMP’ resource and a transfer mode. It uses CopyBits() to transfer the bits so the image will be scaled (i.e. the Rect you pass it doesn’t have to equal the bounds Rect). The transfer mode is a parameter rather than a hardcoded value in the call to CopyBits() to allow for greater flexibility. This allows you to overlay BitMaps (with srcOr) or punch holes in BitMaps (with srcBic) or do whatever other creative things you can think of doing with the transfer modes. Of course, there is no reason why the transfer mode has to be a parameter and if you’re always going to use srcCopy and want a slightly leaner version of PlotBitMap() then you can remove mode from the parameter list and hardcode srcCopy as the transfer mode.

To lock the ‘BTMP’ and set up the baseAddr field before calling PlotPBitMap(), PlotBitMap() calls the LockBitMap() function. LockBitMap() locks the BitMap object and sets the baseAddr field to point to the beginning of the bit image. It returns the original value of the byte containing the flags of the object’s master pointer.

Besides being called by PlotBitMap() there are times when you might want to call LockBitMap() directly. An example would be to create a region from a ‘BTMP’. The Toolbox routine BitMapRgn() takes a BitMap and region handle as parameters. You can pass the address of a ‘BTMP’ to BitMapRgn() after the ‘BTMP’ is locked down and the baseAddr field has been set up.

(BitMapRgn() is described in Macintosh Technical Note #193: So many BitMaps, so little time. It is available as part of the 32-Bit QuickDraw INIT, or is available from Apple Software Licensing. Also, Ted Cohen’s article “Convert PICTs to Regions”, MacTutor, June 1989, contains a function MakeRgn() which converts a BitMap image into a QuickDraw region.)

CREATING A ‘BTMP’ RESOURCE

Okay, great. But now you are probably asking yourself “Where do I get these ‘BTMP’s?” Do I order them through the APDA catalog? Are they available in the delicatessen section of my local super market? Or the frozen code section?

This issue of MacTutor provides two ways to create ‘BTMP’ resources. Michael Ogawa’s article “Creating Resources From SuperPaint” explains how you can create a plug-in command for SuperPaint 2.0 that will create ‘BTMP’s from the selection area. Alternatively, follow along through the rest of this article to learn how to create ‘BTMP’ resources via a custom ResEdit editor.

‘BTMP’ RESOURCES

In Rez format, the definition of the ‘BTMP’ resource is:

/* 7 */
type ‘BTMP’ {
 unsigned hex longint = 0;
rowBytes:
 unsigned integer;
 rect;
 array { hex string [$$Word(rowBytes)];
 };
};

An example of what a ‘BTMP’ in Rez format looks like is this:

/* 8 */

resource ‘BTMP’ (129) {
 4,
 {0, 0, 14, 19},
 { $”04 30 00 00",
 $”0A 50 00 00",
 $”0B 90 00 00",
 $”08 20 00 00",
 $”12 20 00 00",
 $”20 20 00 00",
 $”40 10 00 00",
 $”80 0C 00 00",
 $”80 03 E0 00",
 $”7E 00 60 00",
 $”01 00 20 00",
 $”01 00 00 00",
 $”00 C0 00 00",
 $”00 E0 00 00"
 }
};

But since it is physically unsatisfying to create and edit BitMaps as a series of hex strings there is a need for a better way. Wouldn’t it be nice if you could edit the above data like this instead:

Wouldn’t it be nice if you could change the size of the fatBits from 8 to 3 pixels or invert the image with a single menu command to get this:

Wouldn’t it be nice if I’d get on with it

SLICED BREAD

Like the bank accounts of the Las Vegas casinos, ResEdit was designed to be extensible. There is even a nice document that comes with ResEdit v.1.2 (APDA #M0015LL/B, $30) that describes this extensibility (which you really ought to read if you want to understand the code below). When you buy ResEdit you will get some example code for a sample “picker” and a sample “editor.” There are also some other files included with the ResEdit package that you will need in order to make use of the editor code presented here (specifically: RSSC.a, ResDisp.a and ResEd.h). If they didn’t consist of several hundred lines of code I would list them here.

If you find the following discussion and source listing a little confusing, don’t worry. You can still make use of the ‘BTMP’ resource by ordering the source code disk for this month. On it you will find the set of resources needed to paste into ResEdit that will allow you to edit ‘BTMP’s.

EXTENDING RESEDIT

The easiest way to extend ResEdit is to make a ‘TMPL’ resource for some custom resource you might have. However, there are limits to what a template can do. For instance, you cannot use a template to do fatBit editing of BitMaps. That sort of thing requires a special ResEdit editor which is an external piece of code that gets pasted into ResEdit (as an ‘RSSC’ resource whose name is the type of resource it edits. ‘RSSC’ stands for ReSource Standard Code segment). This second method is the one given here.

PICKING

A ResEdit “picker” is the thing that displays all of the resources of a given type in a ResEdit window. Usually, this is just a List Manager list of cells that say something like ‘MENU “File” ID = 2' but a picker can choose any format it wants to display the resources. The ‘ICON’ picker, for example, shows you ‘ICON’s in graphic form (as does the ‘PICT’ picker). Since the main point of this article is not picking custom resources but editing custom resources I have chosen to use the standard picker for ‘BTMP’ resources.

When the user double-clicks (or chooses “Open”) on a resource from a picker window, the picker will call the editor for that type of resource, if it exists. If no suitable editor exists then ResEdit calls its standard editor which creates a window with a hex dump of the given resource. I always thought it would be nice for ResEdit to boldface those resource types that it knows how to edit so you’d know in advance if you had the necessary ‘RSSC’ or ‘TMPL’ resource to edit a certain type (instead of figuring it out once you saw the hex dump). This sort of feedback is consistent with the current method of showing a small icon with little lines in it for files that have resource forks and a plain document small icon for files without resource forks.

EDITING

Okay, so lets say the standard ResEdit picker has had a request to open a ‘BTMP’ resource. The picker looks for an ‘RSSC’ resource with name “@BTMP” and finds it. What happens next? About the only thing that happens right away is that the ‘RSSC’ resource is loaded and the EditBirth() function (see ResBTMPEd.c) is called to set things up. EditBirth() creates a window for editing the ‘BTMP’ resource it’s passed and initializes all of the links between data objects. If the size of the resource passed to an editor is zero then the user has chosen New from the File menu and the editor should create some default resource. You can set the size of the default ‘BTMP’ resource by changing the values of DFLT_BTMP_WIDTH and DFLT_BTMP_HEIGHT.

The main data structure is the rBTMPrec. The ResEdit documentation refers to this as the “son” and the thing that caused its creation as the “father.” It is passed between almost every function in the editor and contains all the vital information:

/* 9 */

typedef struct rBTMPRec {
 ParentHandle  father; 
 Str64  name;
 WindowPtrwindPtr;
 Booleanrebuild;
 BTMPHndl hBTMP;
 short  fatBitSize;
} rBTMPRec, *rBTMPRecPtr, **rBTMPHndl;

The first four fields are the same for every type of picker or editor. The remainder of the structure can be of any format that a particular picker or editor wants it to be. The father field is a handle back to the thing that called the editor. This may seem unnecessary because you might assume that the ‘BTMP’ editor was called by either the ‘BTMP’ picker (if it exists) or the generic ResEdit picker, but that isn’t a valid assumption. ResEdit is set up so that anybody can call anybody. For example, the current ResEdit ‘DLOG’ editor calls the ‘DITL’ editor when you double-click the ‘DLOG’ that owns the ‘DITL.’ So a “father” can be a picker or an editor.

The name field is the name of the father, which will become part of the son’s window title. The windPtr is a pointer to the father’s window. The rebuild field is used to tell the picker that the list of resources needs to be rebuilt (if, for example, the user Reverts the resource, changes its ID, etc.).

The last two fields are custom to the BTMP editor. The hBTMP field is a handle to the actual ‘BTMP’ resource. FatBitSize is the current number of pixels per fatBit plus 1. When I set out to write this editor I wanted to try and stay consistent with whatever the other ResEdit fatBit editors had done. Then I discovered that the ‘ICON’ editor uses 6 pixels per fatBit, the ‘SICN’ editor uses 4 and the ‘CURS’ editor uses 7. Feeling especially consistent that day I added this dialog to the ‘BTMP’ editor:

LIFE AFTER LAUNCH

Back to what’s going on in the editor. Once the window has been created and variables initialized, the handling of the editor is much like that of any other application only there is much less work to do because ResEdit takes care of most of the uninteresting parts for us. There is a DoEvent() function where we handle mouseDowns, activates, deactivates and updates.

The handling of mouseDown events is well defined for a fatBits editor: when the use clicks on a fatBit we should flip its state and if the user holds the mouse down and moves it around then flip all the fatBits whose state is the same as the first fatBit before it was flipped (so the user can draw squiggles and such).

Update events are easy, too: just draw the fatBits. I must admit that I didn’t spend any time on trying to come up with the fastest fatBit update function. In fact, for medium to large BitMaps (3000+ pixels) the function DrawBTMPWindow() is fairly slow updating. The editor is, however, very fast when you’re clicking around drawing because it is using a different function for that.

Activate and deactivate events are interesting only in the sense that the ‘BTMP’ menu comes and goes as a ‘BTMP’ window is activated or deactivated:

Choosing Change Bounds allows you to edit the size of the BitMap:

You won’t loose any bits when you change bounds unless you make the BitMap smaller. If you make it larger, the editor will add rows and/or columns of white pixels to the existing BitMap. If you change the coordinate system of the bounds (i.e. you do the functional equivalent to OffsetRect(&bounds, dh, dv)) but otherwise leave it the same size then the pixels won’t change at all. In any case, the rowBytes field is calculated for you.

The Fill and Invert items do what you’d expect them to. The Nudge items are something that I’ve always wanted on the ‘ICON’ editor. They will shift the whole BitMap 1 pixel up, down, left or right without changing the size of the BitMap. You will loose 1 row of pixels by doing this and if you Nudge the other way they won’t come back (“Oh, please, come back little pixels. I’ll be nice.” “No way, pal, we’re outta here. See ya.”).

In addition to its own menu, the editor needs to handle some of the standard ResEdit menu items. The function DoMenu() handles Close, Save, Revert and Get Info. Nothing from the Edit menu has been implemented here.

CORE CALLING

It is possible for an editor or picker to call functions from the core of ResEdit. The available functions are described in detail in the ResEdit documentation. Several of them imitate ToolBox calls but do a little extra processing because either ResEdit requires it or to make life a little easier for programmers. The functions that the ‘BTMP’ editor makes use of are: SetETitle (concats a resource’s ID with its name), EditorWindSetup (create a window), FixHand (similar to SetHandleSize), BubbleUp (similar to MoveHHi), SetResChanged (similar to ChangedResource), ResEditRes (returns the ID of ResEdit’s resource file), WindReturn (called when you close a window. It tells ResEdit that the window position is available), PassMenu (pass a menu command to another object), ShowInfo (put up a GetInfo window for an object) and CallInfoUpdate (tells the picker that something has changed and the picker list needs to be rebuilt).

MAKING THE EDITOR

The file BuildEditor is the file you want execute to build the ‘RSSC’ resource. The file MakeEditor is set up to automatically paste the ‘RSSC’ resource and a few other resources used by the editor into ResEdit after a successful compile and link. To make this work you need the following directory structure for this project: in your “{boot}” directory create a folder called “ResEdit” that contains a folder “BTMPeditor” and a copy of ResEdit. The BTMPeditor folder contains BuildEditor, MakeEditor and the folders “obj” and “source.” The source folder contains ResBTMPEd.c, ResBTMP.r, ResIDs.h, ResEd.h, ResDisp.a and RSSC.a (remember that ResEd.h, ResDisp.a and RSSC.a aren’t listed in this article).

THE ‘BTM#’ RESOURCE TYPE

After using the ‘BTMP’ resources for some time I have found that they are easier to manage if they are all grouped together in one non-relocatable chuck of memory. It is nice to be able to load them in during program initialization, move them to high memory, lock them down, set up all of the baseAddr fields and then use them as needed. Hence, the need for the ‘BTM#’ resource.

A ‘BTM#’ is a list of ‘BTMP’s in the same way that a ‘STR#’ is a list of ‘STR ‘s. A ‘BTM#’ contains an integer equal to the number of ‘BTMP’s in it followed by a series of lengths and ‘BTMP’s. The lengths are equal to the size of the ‘BTMP’ that follows plus 2 (the 2 is so that the length includes itself). The lengths make it easy to jump from one ‘BTMP’ to the next when looking for the nth element in the list.

In Rez format, the definition of the ‘BTM#’ resource is a bit more complicated than the ‘BTMP’ resource because of the fact that it is a list of variable length items (many thanks to John Harvey at Apple MacDTS for this typedef):

/* 10 */

type ‘BTM#’ {
 integer = $$CountOf(BitMaps);array BitMaps {
   startBM:
 integer =
 (endBM[$$ARRAYINDEX(BitMaps)] -
 startBM[$$ARRAYINDEX(BitMaps)]) / 8;
 unsigned hex longint = 0;
   rowBytes:
 unsigned integer; rect;
 array PixMap {  hex string
 [$$Word(rowBytes[$$ARRAYINDEX(BitMaps)])];
 };
   endBM:
 };
};

An example of a ‘BTM#’ that contains two ‘BTMP’s in it in Rez format is:

/* 11 */

resource ‘BTM#’ (128)
{ {
 /* first ‘BTMP’ */
 2,
 {0, 0, 1, 14},  
 {$”22 22"};
 /* second ‘BTMP’ */
 4,
 {0, 0, 3, 30},  
 {$”44 44 44 44",
 $”55 55 55 55",
 $”66 66 66 66"};
}; };

Unfortunately, I don’t have a ResEdit editor for the ‘BTM#’ resource type (yet). The data generated by the sample resource using the ‘BTM#’ definition consists of these hex bytes:

Starting from the beginning, the $0002 says there are 2 items in this list; the $0012 is the length of the first ‘BTMP’ resource ($10 for the ‘BTMP’ + $02 for the size of the length field) followed by the actual ‘BTMP’; then a $001C for the length of the second ‘BTMP’ ($1A for the ‘BTMP’ + $02 for the length) followed by the second ‘BTMP’.

Once this resource is understood, it is not too difficult to write (read: it is left up to the reader to write) a routine that initializes all of the baseAddr fields and another routine to find and plot the individual ‘BTMP’s. PlotIndBitMap() is just like PlotBitMap() except that it should expect a handle to a ‘BTM#’ resource (rather than a handle to a ‘BTMP’ resource) and takes one additional parameter which is the index into the ‘BTM#’. Once it finds where the indexed ‘BTMP’ begins within the ‘BTM#’ it should call PlotPBitMap() to do the actual transfer.

Thanks to Andy Jeffrey and Michael Ogawa for suggestions, testing and proofing. Without their help this would have taken twice as long as it did. Thanks to Kevin Connor and Mr. “Goobs” Galvan for being annoying. Without their help this would have only taken half the time it did.

The code that follows is based on the skeleton sources provided by Apple Computer and is copyrighted by them.

Listing:  ResBTMPEd.c

/* File ResBTMPEd.c 
 *
 * ResEdit editor for ‘BTMP’ resources.
 * by Mike Scanlin  12 June 1989
 *
 * portions of this code are from the example
 * source ResXXXXEd.c which is provided by 
 * and copyrighted by Apple Computer, Inc.
 */

#include<types.h>
#include<memory.h>
#include<menus.h>
#include<resources.h>
#include<Packages.h>
#include“ResEd.h”
#include“ResIDs.h”

/* DFLT_FAT_BIT_SIZE must be [4..9] */
#define DFLT_FAT_BIT_SIZE 8
#define DFLT_BTMP_WIDTH 20
#define DFLT_BTMP_HEIGHT  10
#define DFLT_ROWBYTES
 ((((DFLT_BTMP_WIDTH-1)>>4)+1)<<1)
#define DFLT_BTMP_SIZE    
 (DFLT_BTMP_HEIGHT*DFLT_ROWBYTES)
#define kLockStateFlag    0x80

/* data structure for resource type ‘BTMP’ */
typedef struct BTMP {
 Ptr    baseAddr;
 short  rowBytes;
 Rect   bounds;
 short  bitImage[];
} BTMP, *BTMPPtr, **BTMPHndl;

/* our editor’s window’s refCon has a handle to this */
typedef struct rBTMPRec {
 ParentHandle  father;
 Str64  name;
 WindowPtrwindPtr;
 Booleanrebuild;
 BTMPHndl hBTMP;
 short  fatBitSize;
} rBTMPRec, *rBTMPRecPtr, **rBTMPHndl;


/* ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ */

pascal void PickBirth(ResType t, ParentHandle dad)
/* PickBirth is not used for editors */
{
 /* the following is to prevent 
  * compiler warnings about unused 
  * parameters */
 #pragmaunused (t, dad)
}

/* ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ */

static void CalcWindowSize(BTMPHndl theBitMapHndl,
 short fatBitSize, short *wWidth, short
 *wHeight)
{
 short  bitMapWidth, bitMapHeight;
 BTMPPtrtheBitMapPtr;
 
 theBitMapPtr = *theBitMapHndl;
 bitMapWidth = DFLT_BTMP_WIDTH;
 bitMapHeight = DFLT_BTMP_HEIGHT;
 /* if we’re not being called for the default
  * bitmap, then use the actual size */
 if(GetHandleSize((Handle)theBitMapHndl)!=0){
 bitMapWidth=theBitMapPtr->bounds.right - 
 theBitMapPtr->bounds.left;
 bitMapHeight=theBitMapPtr->bounds.bottom - 
 theBitMapPtr->bounds.top;
 }
 
 *wWidth = fatBitSize * bitMapWidth + 1;
 *wHeight = (fatBitSize+1)*bitMapHeight + 4;
}

/* ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ */

void GetNameAndTitle(StringPtr windowTitle,
 StringPtr windowName, Handle theBitMapHndl)
{
 strcpy(windowTitle, “\pBTMP”);
 SetETitle(theBitMapHndl, windowTitle);
 strncpy(windowName,windowTitle,*windowTitle+1;
}

/* ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ */

pascal void EditBirth(Handle theBitMapHndl, 
 ParentHandle dad)
{
 rBTMPHndlmyBTMP;
 rBTMPRecPtrpMyBTMP;
 WindowPtrmyWindow;
 Str255 windowTitle, newName;
 short  windowWidth, windowHeight;

 GetNameAndTitle(&windowTitle, &newName, theBitMapHndl);
 CalcWindowSize((BTMPHndl)theBitMapHndl, 
 DFLT_FAT_BIT_SIZE, &windowWidth, &windowHeight);

 myWindow = EditorWindSetup(false, windowWidth, 
 windowHeight, &windowTitle, &newName, true, dad);
 
 /* if we got a new window (i.e. no errors), 
  * then start up the editor */
 if (myWindow != NULL) {
 /* if theBitMapHndl is empty, then user  did ‘New’ */
 if ((GetHandleSize(theBitMapHndl)) == 0) { 
 BTMPPtrtheBitMapPtr;

 /* allocate some space for the default
  * bitmap */
 FixHand(sizeof(BTMP) + DFLT_BTMP_SIZE,
 theBitMapHndl);
 /* Note: FixHand has initialized the
  * structure with zeros */
 theBitMapPtr=*(BTMPHndl)theBitMapHndl;
 theBitMapPtr->rowBytes = DFLT_ROWBYTES;
 SetRect(&theBitMapPtr->bounds, 0, 0,
 DFLT_BTMP_WIDTH, DFLT_BTMP_HEIGHT);
 }

 /* create the instance record used to
  * communicate with ResEdit and initialize
  * fields */
 myBTMP = (rBTMPHndl)NewHandle (sizeof(rBTMPRec));

 pMyBTMP = *myBTMP;
 pMyBTMP->windPtr = myWindow;
 pMyBTMP->father = dad;
 BlockMove(&newName,&pMyBTMP->name, newName[0]+1);
 pMyBTMP->hBTMP = (BTMPHndl)theBitMapHndl;
 pMyBTMP->fatBitSize = DFLT_FAT_BIT_SIZE;
 ((WindowPeek)myWindow)->refCon = (long)myBTMP;
 ((WindowPeek)myWindow)->windowKind = ResEdID();
 }
}

/* ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ */

static void PlotPBitMap(Rect *r, BTMPPtr
 theBitMapPtr, short mode)
/* Analagous to PlotIcon except that you pass a
 * pointer instead of a handle. Mode specifies 
 * the transfer mode to use which can be any of
 * the source transfer modes. This routine assumes
 * that the handle to the bitMap is locked. */
{
 GrafPtrport;
 
 GetPort(&port);
 CopyBits((BitMap *)theBitMapPtr, &port->portBits,             
 &theBitMapPtr->bounds, r, mode, NULL);
}

/* ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ */

char LockBitMap(BTMPHndl theBitMapHndl)
/* LockBitMap() locks the BTMP specified by 
 * theBitMapHndl and sets the baseAddr field to
 * point to the bitImage field of the BTMP. 
 * The function’s return value is the original
 * value of the byte that contains the flags of
 * the master pointer for the given handle (as
 * returned by HGetState()).
 * Warning: If the handle is not locked on entry
 * then the OS routine MoveHHi() is called before
 * locking the handle. Therefore, this routine may
 * move or purge blocks in the heap.
 * m_o 07.09.89 */
{
 char   origState;
 BTMPPtrtheBitMapPtr;

 origState = HGetState((Handle)theBitMapHndl);
 if (!(origState & kLockStateFlag)) {
 MoveHHi((Handle)theBitMapHndl);
 HLock((Handle)theBitMapHndl);
 }
 theBitMapPtr = *theBitMapHndl;
 theBitMapPtr->baseAddr =
 (Ptr)&theBitMapPtr->bitImage;
 return(origState);
}

/* ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ */

static void PlotBitMap(Rect *r, BTMPHndl
 theBitMapHndl, short mode)
/* Analagous to PlotIcon but it works with BTMPs, 
 * not ICONs. Mode specifies the transfer mode to
 * use which can be any of the source transfer
 * modes. */
{
 char   origState;
 
 origState = LockBitMap(theBitMapHndl);
 PlotPBitMap(r, *theBitMapHndl, mode);
 (*theBitMapHndl)->baseAddr = NULL;
 HSetState((Handle)theBitMapHndl, origState);
}

/* ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ */

static void PlotCenteredBitMap(BTMPHndl
 theBitMapHndl)
/* Custom to this editor. This plots the current
 * BTMP in the top center of the editing window. */
{
 GrafPtrport;
 Rect   theRect;
 BTMPPtrtheBitMapPtr;
 
 theBitMapPtr = *theBitMapHndl;
 theRect = theBitMapPtr->bounds;
 GetPort(&port);
 OffsetRect(&theRect, ((port->portRect.right - 
 port->portRect.left -
 (theBitMapPtr->bounds.right - 
 theBitMapPtr->bounds.left)) >> 1) - 
 theBitMapPtr->bounds.left, 
 1 - theBitMapPtr->bounds.top);
 PlotBitMap(&theRect, theBitMapHndl, srcCopy);
}

/* ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ */

static void DrawBTMPWindow(rBTMPHndl myBTMP)
{
 rBTMPRecPtrpMyBTMP;
 BTMPPtrtheBitMapPtr;
 Rect   theRect;
 Rect   *r;
 short  i, j, nRows, nCols,
 fatBitSize, currentWord,
 nPixels;
 short  *bitImagePtr;
 
 BubbleUp((Handle)myBTMP);
 HLock((Handle)myBTMP);
 pMyBTMP = *myBTMP;
 
 BubbleUp((Handle)pMyBTMP->hBTMP);
 HLock((Handle)pMyBTMP->hBTMP);
 theBitMapPtr = *pMyBTMP->hBTMP;
 
 /* draw the actual size bitmap */
 PlotCenteredBitMap(pMyBTMP->hBTMP);
 
 /* draw the line above the fatBits */
 nRows = theBitMapPtr->bounds.bottom - 
 theBitMapPtr->bounds.top;
 MoveTo(0, i = nRows + 2);
 LineTo((pMyBTMP->windPtr)->portRect.right, i);

 /* draw the fat bits */
 fatBitSize = pMyBTMP->fatBitSize;
 nCols = theBitMapPtr->bounds.right - 
 theBitMapPtr->bounds.left;
 r = &theRect;
 r->bottom = (r->top = nRows + 4) +
 fatBitSize - 1;
 bitImagePtr = &theBitMapPtr->bitImage;
 
 for (i = 0; i < nRows; i++) {
 r->left = 1;
 r->right = fatBitSize;
 nPixels = 0;
 
 for (j = 0; j < nCols; j++) {
 if (nPixels == 0) {
 currentWord = *bitImagePtr;
 bitImagePtr++;
 nPixels = 16;
 }
 
 if (currentWord < 0)
 PaintRect(r);
 else
 EraseRect(r);
 
 currentWord <<= 1;
 nPixels--;
 
 r->left += fatBitSize;
 r->right += fatBitSize;
 }
 
 r->top += fatBitSize;
 r->bottom += fatBitSize;
 }

 HUnlock((Handle)myBTMP);
 HUnlock((Handle)pMyBTMP->hBTMP);
}

/* ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ */

static Boolean GetABit(BTMPPtr theBitMapPtr, short
 gridH, short gridV)
{
 short  *bitImagePtr;
 short  mask;
 
 bitImagePtr = &theBitMapPtr->bitImage + 
 ((theBitMapPtr->rowBytes >> 1) * gridV +
 (gridH >> 4));
 mask = (1 << (15 - (gridH % 16)));
 return(!!(*bitImagePtr & mask));
}

/* ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ */

static Boolean FlipABit(BTMPHndl theBitMapHndl, 
 short fatBitSize, short gridH, short gridV)
{
 Rect   theRect;
 Rect   *r;
 short  *bitImagePtr;
 short  mask;
 BTMPPtrtheBitMapPtr;
 
 theBitMapPtr = *theBitMapHndl;
 
 /* flip the bit in the bitMap */
 bitImagePtr = &theBitMapPtr->bitImage + 
 ((theBitMapPtr->rowBytes >> 1) * gridV +
 (gridH >> 4));
 *bitImagePtr ^= (mask = (1 << (15 - 
 (gridH % 16))));
 
 /* invert the corresponding fatBit */
 r = &theRect;
 r->bottom = (r->top = 4 +
 theBitMapPtr->bounds.bottom - 
 theBitMapPtr->bounds.top +
 gridV * fatBitSize) + 
 fatBitSize - 1;
 r->right = (r->left=gridH*fatBitSize + 1) + 
 fatBitSize - 1;
 InvertRect(r);

 PlotCenteredBitMap(theBitMapHndl);

 return(!!(*bitImagePtr & mask));
}

/* ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ */

static void DoClick(Point mousePoint, rBTMPHndl
 myBTMP)
{
 rBTMPRecPtrpMyBTMP;
 BTMPPtrtheBitMapPtr;
 short  gridH, gridV, h, fatBitSize;
 Booleanstate = 0, firstTime,
 atLeastOne;

 BubbleUp((Handle)myBTMP);
 HLock((Handle)myBTMP);
 pMyBTMP = *myBTMP;
 
 BubbleUp((Handle)pMyBTMP->hBTMP);
 HLock((Handle)pMyBTMP->hBTMP);
 theBitMapPtr = *pMyBTMP->hBTMP;
 
 atLeastOne = false;
 firstTime = true;
 fatBitSize = pMyBTMP->fatBitSize;
 
 while (StillDown() && (firstTime || atLeastOne)) {
 gridH = (mousePoint.h - 1) / fatBitSize;
 mousePoint.v -= (h = (theBitMapPtr->bounds.bottom - 
 theBitMapPtr->bounds.top) + 4);
 gridV = (mousePoint.v - 1) / fatBitSize;
 
 if ((mousePoint.v >= 0) && (gridV >= 0) &&
 (gridV < h) && (gridH >= 0) &&
 (gridH < theBitMapPtr->bounds.right - 
 theBitMapPtr->bounds.left) &&
 (firstTime || 
 (!firstTime &&
 (GetABit(theBitMapPtr, gridH, gridV) !=
 state)))) {
 state = FlipABit(pMyBTMP->hBTMP,
 fatBitSize, gridH, gridV);
 atLeastOne = true;
 }
 
 firstTime = false;
 GetMouse(&mousePoint);
 }
 
 if (atLeastOne)
 SetResChanged((Handle)pMyBTMP->hBTMP);

 HUnlock((Handle)myBTMP);
 HUnlock((Handle)pMyBTMP->hBTMP);  
}

/* ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ */
static void SetRadioButton(DialogPtr theDialog, 
 short minItemNum, short maxItemNum, short
 currentItemNum)
/* unsets a range of radio buttons except for 
 * item number currentItemNum which it sets 
 * (if it is within the range minItemNum to
 * maxItemNum). */
{
 short  i, itemType;
 Handle item;
 Rect   box;
 
 for (i = minItemNum; i <= maxItemNum; i++) {
 GetDItem(theDialog, i, &itemType, &item, &box);
 SetCtlValue((ControlHandle)item, i ==
 currentItemNum);
 }
}

/* ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ */
static void UpdateWindow(rBTMPHndl myBTMP)
{
 rBTMPRecPtrpMyBTMP;
 short  windowWidth, windowHeight;
 WindowPtrw;
 
 pMyBTMP = *myBTMP;
 CalcWindowSize(pMyBTMP->hBTMP, pMyBTMP->fatBitSize, 
 &windowWidth, &windowHeight);
 w = pMyBTMP->windPtr;
 SizeWindow(w, windowWidth, windowHeight, false);
 InvalRect(&w->portRect);
 EraseRect(&w->portRect);
 DrawBTMPWindow(myBTMP);
}

/* ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ */
#define OK_BUTTON1
#define LOWEST_ITEM_NO    3
#define HIGHEST_ITEM_NO   8
#define FIRST_PT_SIZE4
#define DIFF_BASE_TO_LOWEST
 (FIRST_PT_SIZE - LOWEST_ITEM_NO)

static void ResizeFatBits(rBTMPHndl myBTMP)
{
 short  saveRefNum, fatBitSize,
 oldFatBitSize, itemHit;
 DialogPtrtheDialog;
 
 saveRefNum = CurrentRes();
 UseResFile(ResEditRes());
 
 theDialog = GetNewDialog(FAT_BIT_SIZE_DLOG,
 NULL, (WindowPtr)-1);

 oldFatBitSize = fatBitSize =
 (*myBTMP)->fatBitSize;

 SetRadioButton(theDialog, LOWEST_ITEM_NO,
 HIGHEST_ITEM_NO, fatBitSize -
 DIFF_BASE_TO_LOWEST);

 do {
 ModalDialog(NULL, &itemHit);
 if ((itemHit >= LOWEST_ITEM_NO) &&
   (itemHit <= HIGHEST_ITEM_NO) && 
   (itemHit != (fatBitSize -
   DIFF_BASE_TO_LOWEST))) {
 fatBitSize = itemHit +
 DIFF_BASE_TO_LOWEST;
 SetRadioButton(theDialog,
 LOWEST_ITEM_NO, 
 HIGHEST_ITEM_NO,
 fatBitSize - 
 DIFF_BASE_TO_LOWEST);
 }
 } while (itemHit != OK_BUTTON);

 DisposDialog(theDialog);

 UseResFile(saveRefNum);

 if (fatBitSize != oldFatBitSize) {
 (*myBTMP)->fatBitSize = fatBitSize;
 UpdateWindow(myBTMP);
 }
}

/* ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ */
static void SetDItemText(DialogPtr theDialog,
 short itemNo, short value)
{
 Str255 theString;
 short  itemType;
 Handle item;
 Rect   box;

 GetDItem(theDialog, itemNo, &itemType, &item, &box);
 NumToString(value, &theString);
 SetIText(item, &theString);
}

/* ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ */
static short GetDItemText(DialogPtr theDialog, 
 short itemNo)
{
 Str255 theString;
 short  itemType;
 long   value;
 Handle item;
 Rect   box;
 
 GetDItem(theDialog, itemNo, &itemType, &item, &box);
 GetIText(item, &theString);
 StringToNum(&theString, &value);
 return(value);
}

/* ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ */
static void ResizeBTMP(BTMPHndl theBitMapHndl, 
 Rect *newBounds)
{
 BTMPPtrtheBitMapPtr, pOldtheBitMap;
 long   sizeOfBitMap;
 short  *bitImagePtr;
 short  i, j;
 Rect   intersectRect,
 copyOftheBitMapBounds;
 
 /* make a copy of the old bitMap */
 pOldtheBitMap = (BTMPPtr)NewPtr(sizeOfBitMap = 
 GetHandleSize((Handle)theBitMapHndl));
 theBitMapPtr = *theBitMapHndl;
 BlockMove((Ptr)theBitMapPtr,
 (Ptr)pOldtheBitMap, sizeOfBitMap);
 
 /* set up the new bitMap */
 theBitMapPtr->bounds = *newBounds;
 theBitMapPtr->rowBytes = (((newBounds->right - 
 newBounds->left - 1) >> 4) + 1) << 1;
 HUnlock((Handle)theBitMapHndl);
 sizeOfBitMap = theBitMapPtr->rowBytes * 
 (newBounds->bottom - newBounds->top);
 FixHand(sizeof(BTMP) + sizeOfBitMap,  
 (Handle)theBitMapHndl);
 BubbleUp((Handle)theBitMapHndl);
 HLock((Handle)theBitMapHndl);
 theBitMapPtr = *theBitMapHndl;
 
 /* clear the bitImage of the new bitMap */
 sizeOfBitMap >>= 1;
 bitImagePtr = &theBitMapPtr->bitImage;
 for (i = 0; i < sizeOfBitMap; i++)
 *bitImagePtr++ = 0;
 
 /* copy this old bits over to the new bitMap */
 copyOftheBitMapBounds = theBitMapPtr->bounds;
 i = pOldtheBitMap->bounds.left - 
 theBitMapPtr->bounds.left;
 j = pOldtheBitMap->bounds.top -
 theBitMapPtr->bounds.top;
 OffsetRect(&copyOftheBitMapBounds, i, j);
 SectRect(&copyOftheBitMapBounds,
 &pOldtheBitMap->bounds, &intersectRect);
 theBitMapPtr->baseAddr =
 (Ptr)&theBitMapPtr->bitImage;
 pOldtheBitMap->baseAddr =
 (Ptr)&pOldtheBitMap->bitImage;
 copyOftheBitMapBounds = intersectRect;
 OffsetRect(&copyOftheBitMapBounds, -i, -j);
 CopyBits((BitMap *)pOldtheBitMap,
 (BitMap *)theBitMapPtr, &intersectRect,
 &copyOftheBitMapBounds, srcCopy, NULL);
 theBitMapPtr->baseAddr = NULL;

 DisposPtr((Ptr)pOldtheBitMap);
 
 SetResChanged((Handle)theBitMapHndl);
}

/* ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ */
#define BOUNDS_TOP_ITEM   2
#define BOUNDS_LEFT_ITEM  3
#define BOUNDS_BOTTOM_ITEM4
#define BOUNDS_RIGHT_ITEM 5

static void ChangeBounds(rBTMPHndl myBTMP)
{
 BTMPHndl theBitMapHndl;
 BTMPPtrtheBitMapPtr;
 short  saveRefNum, itemHit,
 i, *iPtr;
 DialogPtrtheDialog;
 Rect   newBounds;

 BubbleUp((Handle)myBTMP);
 HLock((Handle)myBTMP);
 theBitMapHndl = (*myBTMP)->hBTMP;
 
 BubbleUp((Handle)theBitMapHndl);
 HLock((Handle)theBitMapHndl);
 theBitMapPtr = *theBitMapHndl;

 saveRefNum = CurrentRes();
 UseResFile(ResEditRes());

 theDialog = GetNewDialog(CHANGE_BOUNDS_DLOG,
 NULL, (WindowPtr)-1);

 iPtr = &theBitMapPtr->bounds.top;
 for (i = BOUNDS_TOP_ITEM;
   i <= BOUNDS_RIGHT_ITEM; i++)
 SetDItemText(theDialog, i, *iPtr++);

 do {
 ModalDialog(NULL, &itemHit);
 } while (itemHit != OK_BUTTON);
 
 iPtr = &newBounds.top;
 for (i = BOUNDS_TOP_ITEM;
   i <= BOUNDS_RIGHT_ITEM; i++)
 *iPtr++ = GetDItemText(theDialog, i);
 
 DisposDialog(theDialog);
 UseResFile(saveRefNum);
 
 if (theBitMapPtr->bounds != newBounds) {
 ResizeBTMP(theBitMapHndl, &newBounds);
 UpdateWindow(myBTMP);
 }
 
 HUnlock((Handle)myBTMP);
 HUnlock((Handle)theBitMapHndl);
}

/* ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ */
static void FillBitMap(short state, rBTMPHndl myBTMP)
{
 BTMPPtr  theBitMapPtr;
 short  i, sizeOfBitMap;
 short  *bitImagePtr;
 
 theBitMapPtr = *(*myBTMP)->hBTMP;
 bitImagePtr = &theBitMapPtr->bitImage;
 sizeOfBitMap = ((theBitMapPtr->bounds.bottom - 
 theBitMapPtr->bounds.top)*
 theBitMapPtr->rowBytes) >> 1;
 
 for (i = 0; i < sizeOfBitMap; i++) {
 switch (state) {
 case FILL_WHITE_ITEM:
 *bitImagePtr++ = 0;
 break;
 case FILL_BLACK_ITEM:
 *bitImagePtr++ = -1;
 break;
 case INVERT_ITEM:
 *bitImagePtr++ ^= 0xFFFF;
 break;
 }
 }
 
 InvalRect(&(*myBTMP)->windPtr->portRect);
 SetResChanged((Handle)(*myBTMP)->hBTMP);
}

/* ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ */
#define UP1
#define DOWN2
#define LEFT3
#define RIGHT  4

static void Nudge(short nudgeDir, short dh,
 short dv, Rect *sRect, rBTMPHndl myBTMP)
{
 rBTMPRecPtrpMyBTMP;
 BTMPPtrtheBitMapPtr;
 Rect   destRect;
 RgnHandletrashRgn;
 short  i, fatBitSize,
 rowWords, mask;
 short  *bitImagePtr;
 
 BubbleUp((Handle)myBTMP);
 HLock((Handle)myBTMP);
 pMyBTMP = *myBTMP;
 
 BubbleUp((Handle)pMyBTMP->hBTMP);
 HLock((Handle)pMyBTMP->hBTMP);
 theBitMapPtr = *pMyBTMP->hBTMP;
 
 /* copy the pixel map onto itself (offset
  * by 1 pixel in 1 direction) */
 destRect = theBitMapPtr->bounds;
 OffsetRect(&destRect, dh, dv);
 theBitMapPtr->baseAddr = (Ptr)&theBitMapPtr->bitImage;
 CopyBits((BitMap *)theBitMapPtr, (BitMap *)theBitMapPtr,
 &theBitMapPtr->bounds, &destRect, srcCopy, NULL);
 theBitMapPtr->baseAddr = NULL;

 /* scroll the screen to agree with the new bitMap */
 sRect->top += theBitMapPtr->bounds.bottom -
 theBitMapPtr->bounds.top + 3;
 ScrollRect(sRect, dh * (fatBitSize =
 pMyBTMP->fatBitSize), dv * fatBitSize,
 trashRgn = NewRgn());
 DisposeRgn(trashRgn);

 /* mask off one row or column of bits */
 bitImagePtr = &theBitMapPtr->bitImage;
 mask = 0x7FFF;
 rowWords = theBitMapPtr->rowBytes >> 1;

 switch (nudgeDir) {
 case UP:
 bitImagePtr +=
 ((theBitMapPtr->bounds.bottom - 
 theBitMapPtr->bounds.top - 1) *
 theBitMapPtr->rowBytes) >> 1;
 case DOWN:
 for (i = 0; i < rowWords; i++)
 *bitImagePtr++ = 0;
 break;
 case LEFT:
 bitImagePtr += rowWords - 1;
 mask = 0xFFFF ^ (1 << (15 - 
 ((theBitMapPtr->bounds.right -
 theBitMapPtr->bounds.left - 1) % 16)));
 case RIGHT:
 for (i = 0; i < 
   (theBitMapPtr->bounds.bottom -
 theBitMapPtr->bounds.top); i++) {
 *bitImagePtr &= mask;
 bitImagePtr += rowWords;
 }
 break;
 }
 
 PlotCenteredBitMap(pMyBTMP->hBTMP);
 
 SetResChanged((Handle)pMyBTMP->hBTMP);

 HUnlock((Handle)pMyBTMP->hBTMP);
 HUnlock((Handle)myBTMP);
}

/* ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ */
static void DoClose(rBTMPHndl myBTMP)
{
 CloseWindow((*myBTMP)->windPtr);
 WindReturn((*myBTMP)->windPtr);
 SetTheCursor(arrowCursor);
 
 DeleteMenu(BTMP_MENU_ID);
 DrawMenuBar();
 
 /* Release the resource if we were
  * launched from a picker. */
 if ((*(*myBTMP)->father)->name[1] !=
   editorNameChr)
 ReleaseResource((Handle)(*myBTMP)->hBTMP);

 DisposHandle((Handle)myBTMP);
}

/* ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ */
pascal void DoMenu(short menu, short item, 
 rBTMPHndl myBTMP)
{
 short  saveRefNum;
 Handle hTemp;
 Rect   sRect;

 BubbleUp((Handle)myBTMP);
 HLock((Handle)myBTMP);
 
 SetPort((*myBTMP)->windPtr);
 
 switch (menu) {
 case fileMenu:
 switch (item) {
 case closeItem:
 DoClose(myBTMP);
 return;
 
 case saveItem:
 PassMenu(fileMenu, saveItem,
 (ParentHandle)myBTMP);
 break;
 
 case revertItem:
 if (NeedToRevert((*myBTMP)->windPtr, 
 (Handle)(*myBTMP)->hBTMP)) {
 InvalRect(&(*myBTMP)->
 windPtr->portRect);
 
 saveRefNum = CurrentRes();
 UseResFile(HomeResFile((Handle)
 (*myBTMP)->hBTMP));
 
 if (!RevertResource((Handle)
   (*myBTMP)->hBTMP)) {
 hTemp = (Handle)(*myBTMP)->
 hBTMP;
 
 /* Make sure that the picker
  * list is rebuilt to remove
  * this item. */
 (*(*myBTMP)->father)->
 rebuild = true;
 
 DoClose(myBTMP);
 RemoveResource(hTemp);
 return;
 }
 
 UseResFile(saveRefNum);
 }
 break;
 
 case getInfoItem:
 ShowInfo((Handle)(*myBTMP)->hBTMP,
 (ParentHandle)myBTMP);
 break;
 
 default:
 break;
 }
 
 break;
 
 case BTMP_MENU_ID:
 sRect = (*myBTMP)->windPtr->portRect; 
 
 switch (item) {
 case CHANGE_BOUNDS_ITEM:
 ChangeBounds(myBTMP);
 break;
 
 case FATBIT_SIZE_ITEM:
 ResizeFatBits(myBTMP);
 break;
 
 case FILL_WHITE_ITEM:
 case FILL_BLACK_ITEM:
 case INVERT_ITEM:
 FillBitMap(item, myBTMP);
 break;
 
 case NUDGE_UP_ITEM: 
 Nudge(UP, 0, -1, &sRect, myBTMP);
 break;
 
 case NUDGE_DOWN_ITEM:
 Nudge(DOWN, 0, 1, &sRect, myBTMP);
 break;
 
 case NUDGE_LEFT_ITEM:
 Nudge(LEFT, -1, 0, &sRect, myBTMP);
 break;
 
 case NUDGE_RIGHT_ITEM:
 Nudge(RIGHT, 1, 0, &sRect, myBTMP);
 break;
 
 default:
 break;
 }
 
 break;
 
 default:
 break;
 }
}

/* ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ */
pascal void DoEvent(EventRecord *evt, 
 rBTMPHndl myBTMP)
{
 Point  mousePoint;
 short  saveRefNum;
 
 SetPort((*myBTMP)->windPtr);
 
 switch (evt->what) {
 case mouseDown:
 mousePoint = evt->where;
 GlobalToLocal(&mousePoint);
 DoClick(mousePoint, myBTMP);
 break;
 
 case activateEvt: 
 AbleMenu(fileMenu, fileTop);
 if (evt->modifiers & activeFlag) {
 saveRefNum = CurrentRes();
 UseResFile(HomeResFile((Handle)
 (*myBTMP)->hBTMP));
 InsertMenu(GetMenu(BTMP_MENU_ID), 0);
 UseResFile(saveRefNum);
 }
 else
 DeleteMenu(BTMP_MENU_ID);
 DrawMenuBar();
 break;

 case updateEvt:
 DrawBTMPWindow(myBTMP);
 break;

 default:
 break;
 }
}

/* ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ */
pascal void DoInfoUpdate(short oldID, short newID, 
 rBTMPHndl myBTMP)
{
 Str255 windowTitle, windowName;

 /* Since our ID has changed, we need to 
  * change our window title */
 GetNameAndTitle (&windowTitle, &windowName, 
 (Handle)(*myBTMP)->hBTMP);
 GetWindowTitle (&windowTitle, &windowName,
 true, (*myBTMP)->father);
 BlockMove(&windowName,&(*myBTMP)->
 name,windowName[0] + 1);
 SetWTitle((*myBTMP)->windPtr, &windowTitle);

 /* Now, let our father object know that 
  * our ID has been changed (set flag to
  * rebuild the picker list) */
 (*(*myBTMP)->father)->rebuild = true;
 CallInfoUpdate(oldID, newID, 
 (*(*myBTMP)->father)->wind->refCon,
 (*(*myBTMP)->father)->wind->windowKind);
}

Listing:  ResBTMP.r

/* File ResBTMPEd.r
 *
 * resources for ResBTMPEd.c
 */
 
#include “Types.r”
#include “ResIDs.h”

/* BTMP menu */
resource ‘MENU’ (BTMP_MENU_ID, “BTMP”)
{ BTMP_MENU_ID,
  textMenuProc,
  0b1111111111111111111111110111011,
  enabled,
  “BTMP”,
  { “Change Bounds ”,
 noIcon, noKey, noMark, plain;
    “Fatbit Size ”, 
 noIcon ,noKey, noMark, plain;
    “-”,
 noIcon, noKey, noMark, plain;
    “Fill With White”,
 noIcon, noKey, noMark, plain;
    “Fill With Black”,
 noIcon, noKey, noMark, plain;
    “Invert”,
 noIcon, noKey, noMark, plain;
    “-”,
 noIcon, noKey, noMark, plain;
    “Nudge Up”,
 noIcon, “8”, noMark, plain;
    “Nudge Down”,
 noIcon, “2”, noMark, plain;
    “Nudge Left”,
 noIcon, “4”, noMark, plain;
    “Nudge Right”,
 noIcon, “6”, noMark, plain}
};

/* fat bit size DLOG */
resource ‘DLOG’ (FAT_BIT_SIZE_DLOG, 
 “BTMP fat bits”)
{ {52, 176, 170, 400},
  dBoxProc,
  visible,
  noGoAway,
  0x0,
  FAT_BIT_SIZE_DITL,
  “” };

/* fat bit size DITL */
resource ‘DITL’ (FAT_BIT_SIZE_DITL, 
 “BTMP fat bits”)
{ { {89, 80, 109, 140}, Button
 {enabled,  “OK”};
    {16, 16, 32, 208}, StaticText 
 {enabled, “Number Of Pixels Per FatBit:”};
    {40, 40, 56, 72}, RadioButton
 {enabled,  “3”};
    {64, 40, 80, 72}, RadioButton
 {enabled,  “4”};
    {40, 96, 56, 128}, RadioButton
 {enabled,  “5”};
    {64, 96, 80, 128}, RadioButton
 {enabled,  “6”};
    {40, 152, 56, 184}, RadioButton
 {enabled,  “7”};
    {64, 152, 80, 184}, RadioButton
 {enabled,  “8”} } };

/* bounds DLOG */
resource ‘DLOG’ (CHANGE_BOUNDS_DLOG, 
 “BTMP change bounds”)
{ {62, 192, 192, 430},
  dBoxProc,
  visible,
  noGoAway,
  0x0,
  CHANGE_BOUNDS_DITL,
  “” };

/* bounds DITL */
resource ‘DITL’ (CHANGE_BOUNDS_DITL,
 “BTMP change bounds”)
{ { {96, 88, 116, 148}, Button
 {enabled,  “OK”};
    {40, 64, 56, 104}, EditText
 {enabled,  “”};
    {64, 64, 80, 104}, EditText
 {enabled,  “”};
    {40, 184, 56, 224}, EditText
 {enabled,  “”};
    {64, 184, 80, 224}, EditText
 {enabled,  “”};
    {8, 16, 24, 72}, StaticText
 {enabled,  “Bounds:”};
    {40, 24, 56, 56}, StaticText
 {enabled,  “Top”};
    {64, 24, 80, 56}, StaticText
 {enabled,  “Left”};
    {40, 120, 56, 176}, StaticText
 {enabled,  “Bottom”};
    {64, 120, 80, 168}, StaticText
 {enabled,  “Right”} } };
Listing:  ResIDs.h

/* File ResIDs.h
 *
 * header file included by ResBTMPEd.c 
 * and ResBTMPEd.r
 */

/* RSSCs
 * If you change the RSSC ID, you must modify 
 * the MakeFile to reflect the change */
#define BTMP_RSSC_ID 11000

/* MENUs */
#define BTMP_MENU_ID 11000
#define CHANGE_BOUNDS_ITEM1
#define FATBIT_SIZE_ITEM  2
#define FILL_WHITE_ITEM   4
#define FILL_BLACK_ITEM   5
#define INVERT_ITEM6
#define NUDGE_UP_ITEM8
#define NUDGE_DOWN_ITEM   9
#define NUDGE_LEFT_ITEM   10
#define NUDGE_RIGHT_ITEM  11

/* DLOGs & DITLs */
#define FAT_BIT_SIZE_DLOG 11000
#define FAT_BIT_SIZE_DITL 11000
#define CHANGE_BOUNDS_DLOG11001
#define CHANGE_BOUNDS_DITL11001
Listing:  MakeEditor

#File MakeEditor
#
#Copyright Apple Computer, Inc. 1986-1988
#All rights reserved.
#
#This makefile builds:
#The ResEdit editor for ‘BTMP’ resources 

cOptions = -b
OutFile = {buildToName}
obj = “{boot}ResEdit.BTMP:BTMPeditor:obj:”
source = “{boot}ResEdit.BTMP:BTMPeditor:Source:”

SysLibs = “{CLibraries}”StdCLib.o 
 “{CLibraries}”CRuntime.o 
 “{Libraries}”Interface.o
 
RSSCLibs ={obj}RSSC.a.o 
 {obj}ResDisp.a.o

“{OutFile}” ƒƒ {source}ResBTMPEd.r 
 {source}ResIDs.h
 Rez -a {source}ResBTMPEd.r -o “{OutFile}”

{obj}ResBTMPEd.c.o ƒƒ{source}ResBTMPEd.c 
 {source}ResIDs.h

“{OutFile}” ƒƒ {obj}ResBTMPEd.c.o 
 {RSSCLibs} {sysLibs}
 Link {RSSCLibs} 
 {obj}ResBTMPEd.c.o 
 {sysLibs} 
 -da -w -rt RSSC=11000 
 -sn Main=@BTMP   
 -o “{OutFile}”

{obj} ƒ {source}
Listing:  BuildEditor

#File BuildEditor
#
#This script builds the ResEdit editor for
#    ‘BTMP’ resources

directory “{boot}”ResEdit.BTMP:BTMPeditor:
set buildToName “{boot}ResEdit.BTMP:ResEdit”
Export buildToName

make -f MakeEditor > temp
temp
delete temp

Listing:  PascalDemoBTMP.p

(* File PascalDemoBTMP.p *)
(* A short example of how to use the ‘BTMP’ *)
(* data structure *)
(* in a Pascal program. *)

program PascalDemoBTMP;

 type
 BitMapPtr = ^BitMap;

 BTMP = record
 baseAddr: Ptr;
 rowBytes: integer;
 bounds: Rect;
 bitImage: array[0..0] of integer;
 end;
 BTMPPtr = ^BTMP;
 BTMPHndl = ^BTMPPtr;

 const
 DFLT_BTMP_HEIGHT = 10;
 DFLT_BTMP_WIDTH = 20;
 DFLT_ROWBYTES = ((((DFLT_BTMP_WIDTH - 1)
 div 16) + 1) * 2);
 DFLT_BTMP_SIZE = (DFLT_BTMP_HEIGHT *
 DFLT_ROWBYTES);
 kLockStateFlag = $80;

 var
 theRect: Rect;
 theBitMapHndl: BTMPHndl;

(**********************************************)
 function GetABTMP: BTMPHndl;
(* create a sample BTMP resource and fill it *)
(* with a striped pattern *)
 var
 h: BTMPHndl;
 p: BTMPPtr;
 bitsPtr: ^integer;
 i, nWords: integer;
 begin
 h := BTMPHndl(NewHandle(sizeof(BTMP) -
 sizeof(integer) + DFLT_BTMP_SIZE));
 p := h^;
 p^.baseAddr := nil;
 p^.rowbytes := DFLT_ROWBYTES;
 SetRect(p^.bounds, 0, 0, DFLT_BTMP_WIDTH,
 DFLT_BTMP_HEIGHT);
 bitsPtr := @p^.bitImage;
 nWords := DFLT_BTMP_SIZE div 2;
 for i := 1 to nWords do
 begin
 (* striped pattern *)
 bitsPtr^ := 2816; 
 bitsPtr:=Pointer(longint(bitsPtr) +
 sizeof(integer));
 end;
 GetABTMP := h;
 end;

(**********************************************)
 procedure PlotPBitMap (r: Rect; p: BTMPPtr;
   mode: integer);
 var
 port: GrafPtr;
 begin
 GetPort(port);
 CopyBits(BitMapPtr(p)^, port^.portBits,
 p^.bounds, r, mode, nil);
 end;

(**********************************************)
 function LockBitMap (h: BTMPHndl): SignedByte;
 var
 origState: SignedByte;
 theBitMapPtr: BTMPPtr;
 begin
 origState := HGetState(Handle(h));
 if (not Boolean(BitAnd(LONGINT(origState),
   kLockStateFlag))) then
 begin
 MoveHHi(Handle(h));
 HLock(Handle(h));
 end;
 theBitMapPtr := h^;
 theBitMapPtr^.baseAddr :=
 @theBitMapPtr^.bitImage;
 LockBitMap := origState;
 end;

(**********************************************)
 procedure PlotBitMap (r: Rect; h: BTMPHndl;
   mode: integer);
(* analogous to PlotIcon *)
 var
 origState: SignedByte;
 begin
 origState := LockBitMap(h);
 PlotPBitMap(r, h^, mode);
 h^^.baseAddr := nil;
 HSetState(Handle(h), origState);
 end;

(**********************************************)
begin
 SetRect(theRect, 0, 0, DFLT_BTMP_WIDTH, DFLT_BTMP_HEIGHT);
 theBitMapHndl := GetABTMP;
 PlotBitMap(theRect, theBitMapHndl, srcCopy);
end.

 

Community Search:
MacTech Search:

Software Updates via MacUpdate

Parallels Desktop 12.0.0 - Run Windows a...
Parallels allows you to run Windows and Mac applications side by side. Choose your view to make Windows invisible while still using its applications, or keep the familiar Windows background and... Read more
Spotify 1.0.36.124. - Stream music, crea...
Spotify is a streaming music service that gives you on-demand access to millions of songs. Whether you like driving rock, silky R&B, or grandiose classical music, Spotify's massive catalogue puts... Read more
Firefox 48.0.2 - Fast, safe Web browser.
Firefox offers a fast, safe Web browsing experience. Browse quickly, securely, and effortlessly. With its industry-leading features, Firefox is the choice of Web development professionals and casual... Read more
BBEdit 11.6.1 - Powerful text and HTML e...
BBEdit is the leading professional HTML and text editor for the Mac. Specifically crafted in response to the needs of Web authors and software developers, this award-winning product provides a... Read more
OmniGraffle Pro 6.6.1 - Create diagrams,...
OmniGraffle Pro helps you draw beautiful diagrams, family trees, flow charts, org charts, layouts, and (mathematically speaking) any other directed or non-directed graphs. We've had people use... Read more
OmniGraffle 6.6.1 - Create diagrams, flo...
OmniGraffle helps you draw beautiful diagrams, family trees, flow charts, org charts, layouts, and (mathematically speaking) any other directed or non-directed graphs. We've had people use Graffle to... Read more
Dropbox 8.4.21 - Cloud backup and synchr...
Dropbox is an application that creates a special Finder folder that automatically syncs online and between your computers. It allows you to both backup files and keep them up-to-date between systems... Read more
BetterTouchTool 1.84 - Customize Multi-T...
BetterTouchTool adds many new, fully customizable gestures to the Magic Mouse, Multi-Touch MacBook trackpad, and Magic Trackpad. These gestures are customizable: Magic Mouse: Pinch in / out (zoom... Read more
ScreenFlow 6.1 - Create screen recording...
ScreenFlow is powerful, easy-to-use screencasting software for the Mac. With ScreenFlow you can record the contents of your entire monitor while also capturing your video camera, microphone and your... Read more
f.lux 37.7 - Adjusts the color of your d...
f.lux makes the color of your computer's display adapt to the time of day, warm at night and like sunlight during the day. Ever notice how people texting at night have that eerie blue glow? Or wake... Read more

Become the King of Avalon in FunPlus’ la...
King Arthur is dead. Considering the legend dates back to the 5th century, it would be surprising if he wasn’t. But in the context of real-time MMO game King of Avalon: Dragon Warfare, Arthur’s death plunges the kingdom into chaos. Evil sorceress... | Read more »
Nightgate (Games)
Nightgate 1.0 Device: iOS Universal Category: Games Price: $2.99, Version: 1.0 (iTunes) Description: *** Launch Sale: 25% OFF for a limited time! *** In the year 2398, after a great war, a network of intelligent computers known as... | Read more »
3 best fantasy football apps to get you...
Last season didn't go the way you wanted it to in fantasy football. You were super happy following your drafts or auctions, convinced you had outsmarted everyone. You were all set to hustle on the waiver wire, work out some sweet trades, and make... | Read more »
Pokemon GO update: Take me to your leade...
The Team Leaders in Pokemon GO have had it pretty easy up until now. They show up when players reach level 5, make their cases for joining their respective teams, and that's pretty much it. Light work, as Floyd Mayweather might say. [Read more] | Read more »
Ruismaker FM (Music)
Ruismaker FM 1.0 Device: iOS Universal Category: Music Price: $4.99, Version: 1.0 (iTunes) Description: Following up on the success of Ruismaker, here's her crazy twin-sister, designed for people who want to design their own... | Read more »
Space Marshals 2 (Games)
Space Marshals 2 1.0.15 Device: iOS iPhone Category: Games Price: $5.99, Version: 1.0.15 (iTunes) Description: The sci-fi wild west adventure in outer space continues with Space Marshals 2. This tactical top-down shooter puts you in... | Read more »
Dungeon Warfare (Games)
Dungeon Warfare 1.0 Device: iOS Universal Category: Games Price: $3.99, Version: 1.0 (iTunes) Description: Dungeon Warfare is a challenging tower defense game where you become a dungeon lord to defend your dungeon against greedy... | Read more »
Solitairica (Games)
Solitairica 1.0.7 Device: iOS Universal Category: Games Price: $3.99, Version: 1.0.7 (iTunes) Description: Solitairica takes RPG combat and challenging rogue-like progression to a fresh new place—the world of solitaire! | Read more »
Bowmasters tips, tricks and hints
At least for this writer, archery was one of the more pleasant surprises of the 2016 Rio Olympics. As opposed to target shooting with guns, which was dreadfully boring, watching people shoot arrows at targets was pretty darn cool. [Read more] | Read more »
Best apps for watching live TV
The Olympics have come and gone, leaving nearly everyone in a temporary state of "What the heck am I going to watch on TV right now?" Besides old reruns of Golden Girls, but that goes without saying. [Read more] | Read more »

Price Scanner via MacPrices.net

BookBook Releases SurfacePad, BookBook &...
BookBook has released three new covers just for iPad Pro: SurfacePad, BookBook and BookBook Rutledge Edition. BookBook for iPad Pro is a gorgeous leather case reminiscent of a vintage sketchbook.... Read more
Clean Text 1.0 for iOS Reduces Text Cleanup a...
Apimac today announced availability of Clean Text for iOS, a tool for webmasters, graphic designers, developers and magazine editors to reduce text cleanup and editing time, and also for any iPhone... Read more
27-inch iMacs on sale for up to $220 off MSRP
B&H Photo has 27″ Apple iMacs on sale for up to $200 off MSRP including free shipping plus NY sales tax only: - 27″ 3.3GHz iMac 5K: $2099 $200 off MSRP - 27″ 3.2GHz/1TB Fusion iMac 5K: $1899 $100... Read more
Apple refurbished 13-inch MacBook Airs availa...
Apple has Certified Refurbished 2016 and 2015 13″ MacBook Airs now available starting at $849. An Apple one-year warranty is included with each MacBook, and shipping is free: - 2016 13″ 1.6GHz/8GB/... Read more
Apple refurbished iPad mini 2s available for...
Apple is offering Certified Refurbished iPad mini 2s for up to $80 off the cost of new minis. An Apple one-year warranty is included with each model, and shipping is free: - 16GB iPad mini 2 WiFi: $... Read more
Save up to $600 with Apple refurbished Mac Pr...
Apple has Certified Refurbished Mac Pros available for up to $600 off the cost of new models. An Apple one-year warranty is included with each Mac Pro, and shipping is free. The following... Read more
Mac Pros on sale for $200 off MSRP
B&H Photo has Mac Pros on sale for $200 off MSRP. Shipping is free, and B&H charges sales tax in NY only: - 3.7GHz 4-core Mac Pro: $2799, $200 off MSRP - 3.5GHz 6-core Mac Pro: $3799, $200... Read more
Will We See A 10.5″ iPad Pro in 2017? – The ‘...
A MacRumors report, cites a research note from KGI Securities analyst Ming-Chi Kuo, saying a new size iPad model is in the works. According to the highly respected Cho, who has a strong track record... Read more
IOGEAR USB-C Docking Station Transforms Lapto...
IOGEAR has announced the launch of its innovative USB-C Docking Station with Power Delivery which turns USB-C enabled laptops into desktop workstations. The new IOGEAR USB-C Docking Station features... Read more
12-inch Retina MacBooks on sale for up to $10...
Amazon has 2016 12″ Apple Retina MacBooks on sale for $100 off MSRP. Shipping is free: - 12″ 1.1GHz Space Gray Retina MacBook: $1199 $100 off MSRP - 12″ 1.1GHz Silver Retina MacBook: $1224.99 $75 off... Read more

Jobs Board

*Apple* Professional Learning Specialist - A...
# Apple Professional Learning Specialist Job Number: 51234243 Portland, Maine, Maine, United States Posted: Aug. 18, 2016 Weekly Hours: 40.00 **Job Summary** The Read more
*Apple* Mobile Master - Best Buy (United Sta...
What does a Best Buy Apple Mobile Master do? At Best Buy, our mission is to leverage the unique talents and passions of our employees to inspire, delight, and enrich Read more
*Apple* Retail - Multiple Positions Akron, O...
Job Description: Sales Specialist - Retail Customer Service and Sales Transform Apple Store visitors into loyal Apple customers. When customers enter the store, Read more
Simply Mac *Apple* Specialist- Repair Techn...
…The Technician is a master at working with our customers to diagnose and repair Apple devices in a manner that exceeds the expectations set forth by Apple Read more
*Apple* Retail - Multiple Positions Germanto...
Job Description: Sales Specialist - Retail Customer Service and Sales Transform Apple Store visitors into loyal Apple customers. When customers enter the store, Read more
All contents are Copyright 1984-2011 by Xplain Corporation. All rights reserved. Theme designed by Icreon.