TweetFollow Us on Twitter

More Event Trapping
Volume Number:6
Issue Number:6
Column Tag:XCMD Corner

Related Info: Event Manager Window Manager

More on Event Trapping

By Donald Koscheka, Ernst & Young, MacTutor Contributing Editor

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

This issue marks the second anniversary of this column and I thought it might be a good time to share my Macintosh wish list with you. My list doesn’t contain the usual items that you might expect of a programmer - a faster CPU, more RAM, larger disk space because I know these things are coming; our technology just keeps getting better and better.

I have only one wish and it is a rather strange one: I wish that the Macintosh will once again become a personal computer in the manner of its great ancestor, the Apple ][. Because of the Apple ][, we no longer think of computers as threatening monoliths, rather they are regarded as benign if not beneficial tools of civilization.

The Apple ][ accomplished this in two ways; it was affordable and “user friendly”. Affordability, it turns out, is the keystone to user friendliness. Apple wanted the Apple ][ to be a computer for the masses, not only would the hardware have to be cheap, but also the software, the training and the service. Every aspect of the Apple ][ experience was intended to be “friendly” -- from locally accessible support groups to the schematics that were published in the manuals so that real dilettantes could service their own equipment.

By contrast, the Macintosh has become the “Institutional Apple”. Apple no longer seems concerned with the individual successes of its users and everything about the Macintosh seems geared towards corporate sales and megalithic installations. The “user-friendly” infrastructure that Apple so carefully fostered with the Apple ][ has evaporated. Let me explain with a story:

Recently, my Mac IIcx had a main logic board failure because one of those little jumpers on the board came loose (how that happened remains an endless source of speculation -- the computer is never moved about). I was shocked to discover that a new main logic board (we used to call them “mother boards”) would cost me $600 and that’s with a trade-in allowance!

While the average institution may not balk at a $600 repair cost, I could not help but feel that Apple’s service policy borders on usury. Think about it. My computer fails because its designers did not anticipate a particular failure mode. Although the machine is barely six months old, the factory does not warrant such repairs. My computer is down, I’m out and no where can I find a sympathetic ear, the entire system is simply indifferent to the needs of one despondent user.

What the Macintosh has lost is that “sense of peopleness” that we had with the Apple ][. I know because I worked in a computer store during the heyday of the Apple ][. Once, a despondent customer called to explain that he had installed a card “backwards” in his Apple and it shorted something on the motherboard (we call them main logic boards now) blowing a hole through the board! I quoted him the price of a new motherboard (about $400 plus trade-in) and that that poor man just broke down in tears. I told him to bring the computer in and I’d see what I could do. Being an electrical engineer, and knowing that the Apple ][ was a simple two-layer board, I methodically re-wired the blown out traces with some ordinary hook-up wire. Remarkably, the board worked (no chips were blown). I called the customer and told him the good news - that the damage was just cosmetic and that, while looking like a high school science project, the board worked fine. The cost: $50.00; the customer: ecstatic.

Those days are gone. I called half a dozen computer stores and they all quoted me between $600 and $700. No one suggested my bringing it in, that they would have a look at it. No one suggested that I might get a discount, even after asking for one. The attitude was just so institutional - your computer is broken and now you’re going to have to pay to get it working again. Some maintenance maven at Apple is sure to say that this situation cannot be helped - that the Macintosh has just gotten too complicated for the ordinary service technician and that this is a small price to pay for technological excellence. I say hogwash (but I mean worse) - if the Mac is that complicated, then make it simpler to repair. And if ordinary service technicians can’t fix it, then get extraordinary ones. Heavens knows, we’re paying them enough; computer repair people charge almost twice as much as auto mechanics. Yet the latter have to know more about cars to do a good job, and few car repairs last weeks as is often the case with computers.

I can’t help but feel bitter about this whole experience, mainly because Apple seems so indifferent to its “personal” users. Perhaps this insensitivity comes with being a multi-billion dollar company. Too bad, I somehow wanted to believe that Apple would be different.

My story does have a happy ending in that I did find a sympathetic technician, Mike Cerrone of the Computer Factory, Stamford, CT, who worked with me in isolating the problem to the little jumper. In so doing, he saved me nearly $600 in repairs. People like Mike are what this business is all about and I hope his bedside manner comes back into style.

More on Event Trapping

Two months ago I introduced the concept of intercepting the event trap in Hypercard. This technique could be used to add custom controls, external windows, key click filters and the like to a stack. I seem to have struck a nerve among the Hypercard programming community because several readers have “wired” asking for more information. I will show you how to maintain lists in windows that are external to Hypercard. This work is based on the March ’90 issue and you may want to refer back to it.

This XCMD contains a lot of source code, so I’m just publishing the highlights, the rest of the code is available from MacTutor.

Listing 1 contains the main code for the XCMD, “floating list.c”. We accept commands of the form:

`1

    get FloatingList( COMMAND, <WINDOW, parameters> )

where <window, parameters> are optional and COMMAND is one of the following:

INSTALL: installs the patch to get next event and initialize the external window system. You should invoke this command only once during a stack session.

REMOVE: removes the patch to getnextevent. call only when you are done with the windowing system.

OPEN: open a new window and add the items passed to the list. The window is opened with the name provided in the window parameter. This window will have a list associated with it. Open takes several parameters:

Parameter 3: the rectangle for this window

Parameter 4: the window type.

Parameter 5: 1 if you want the window visible, 0 otherwise.

Parameter 6: data to put into the list. Currently lists are implemented as single column multi-row lists but you can modify the code in do_open() to fit your needs (see listing 2, “Window Commands.c” ).

`2

    put FloatingList( “OPEN”, windowname, “50 10 300 210”, ¬
    “DOCUMENT”, 1, card field “your list name” ) into it

The window types and command names are stored in string# resources with the following format: Each line contains the name followed by a comma followed by the number (token) that codes for that name. For example, the install command is coded as: Install, 0. The source disk contains a sample stack with these resources.

CLOSE: close a window by name. The window must have been previously opened using the window command.

SHOW: show the window whose name is passed.

HIDE: hide the window whose name is passed.

I’ve left room for three more commands: add, delete and clear to allow you to add an item to the list, to delete an item and to clear an item. These will be the subject of a future column. For now, you pass a complete list to window on open.

Each command is handled by a routine in “Window Commands.c” (listing 2).

This system allows you to open windows external to Hypercard and to track events within those windows. This is a powerful but easy-to-use system. How it’s done is really quite simple as I will explain.

The information for the external windows is stored off a structure called “wdGlobals” for window globals. This structure is allocated and intialized as a non-relocatable block in the system heap by do_init() (see listing 2). If the block can be allocated, we then load the event trap patch in as a resource of type ‘EXEC’ and then move it to the system heap. At this point the event trap is ready to start responding to events outside of the Hypercard window.

External windows are created using the Open command and the code is implemented in the routine do_open() (the parser that this routine uses was covered in the March issue). Once allocated, windows are referenced via a structure that I call the ‘post’ named in honor of the post-it note, the greatest invention since cellophane tape.

The structure of the post record is depicted in figure 1. The doubly-linked list allows us to have as many windows open as memory will allow. Note that I allocate the window memory in the post structure in the field “window store”. This is so that the window record gets allocated in the system heap out of Hypercard’s way. The windowPtr is a standard window manager structure which should point to the window store subrecord. List is a standard list handle. Name is the name of the window (called out separately for convenience - this field is redundant with the name field in the window record itself). Is_color determines whether this is a color window and id is currently not used but you can use it to assign unique ids to each window.

Figure 1. Structure of the Post record.

The post structure gets stored in the list field of the wdGlobals structure. Now this structure acts as our global memory since we need to access the structure from both the XCMD and from the event patch itself. In March, I showed you how to save such data in the system resource file. I’ve since had a change of heart and now choose to store the structure in one of the extra fields in the trap header. The routine, SetWindowHeader (Listing 4) places our record at byte offset 10 (decimal) from the start of the patch. If our patch was patched, offset 10 may not point to the correct structure in the future. For this reason, I set the signature of the patch to ‘isme’. Before accessing the window globals, I check to make sure that the patch has the correct signature. If so, then the window globals at offset 10 are valid. This may seem klunky but it’s a heck of a lot better than messing with the system resource file which seems to have a mind of its own.

The patch was described in a previous article so I won’t go into great detail. Note that for most purposes, it performs as a standard event loop. The main difference is that we first look at the event to see if it belongs to one of the “post” notes. If so, we handle it and inform Hypercard that no event occurred on the last invocation of getnextevent. If the event is not for one of our windows, we pass it back to Hypercard and let it decide what to do with it. The code in listing 2 is a “tail patch” in that we first invoke the original trap and then take some sort of action. While many programmers frown on tail-patches, this one does work and we do need to know what the most recent event is in order to process it.

Happy hacking and I hope you find this article interesting. We will explore this area further in future issues.

Listing 1:

/********************************/
/* File: Floating List.c  */
/* */
/* Window with drawing commands  */
/* */
/* Paramters:    */
/* param0 = command*/
/* param1 = name of the window*/
/* param2..n= window parameters  */
/* INSTALL*/
/*  OPEN*/
/* CLOSE*/
/* SHOW */
/* HIDE */
/* ADDITEM*/
/*  DELETEITEM   */
/* CLEAR*/
/*  REMOVE*/
/* */
/* Out: */
/* Responds to the given command*/
/* ----------------------------  */
/* © 1989 Donald Koscheka */
/* All Rights Reserved    */
/********************************/

#include“WindowUtils.h”
#include<HyperXCMD.h>
#include“WindowCommands.h”

pascal void main( paramPtr )
 XCmdBlockPtr  paramPtr;
{
 wdGlobalsPtr  windP;
 short  theToken;
 Str31  command;
 Str31  str;
 char   name[256];
 
 /*** empty is the default answer  ***/
 paramPtr->returnValue = 0L;
 
 /*** (1) Parse for the command type ***/
 theToken = matchToken( paramPtr->params[0], 1020 );

 /*** (2) Now do whatever the command requires***/
 if( theToken >= 0 ){
 name[0] = ‘\0’;
 
 if( paramPtr->params[1] )
 strcat( (char *)&name, *(paramPtr->params[1]) );
 
 windP = (wdGlobalsPtr)GetWindowHeader( EVENT_TRAP );
 
 if( theToken == INSTALL_CMD && !windP )
 do_init();
 else
 if( windP )
 switch( theToken ){
 case OPEN_CMD:
 do_open( name, paramPtr, windP );
 break;
 case CLOSE_CMD:
 do_close( name, paramPtr, windP );
 break;
 case SHOW_CMD :
 do_show( name, paramPtr, windP );
 break;
 case HIDE_CMD :
 do_hide( name, paramPtr, windP );
 break;
 case ADD_CMD :
 do_additem( name, paramPtr, windP );
 break;
 case DELETE_CMD :
 do_deleteitem( name, paramPtr, windP );
 break;
 case CLEAR_CMD : 
 do_clear( name, paramPtr, windP );
 break;
 case REMOVE_CMD :
 do_remove( paramPtr, windP );
 break;
 default : 
 break;
 } /* token switch */
 }
 paramPtr->returnValue = 0L;
}
Listing 2:

/********************************/
/* File: WindowCommands.c */
/* */
/* Window with drawing commands  */
/* */
/* Out: */
/* Responds to the given command*/
/* */
/* Result codes are returned*/
/* thusly:*/
/* */
/* 0 == noErr    */
/* -1== unknown error*/
/* -2..-n == error in parameter  */
/* ----------------------------  */
/* © 1989 Donald Koscheka */
/* All Rights Reserved    */
/********************************/

#include“WindowUtils.h”
#include<ListMgr.h>
#include<HyperXCMD.h>
#include<HyperUtils.h>

#include“WindowCommands.h”
#include“ParseCommands.h”

long  do_init()
/***********************
* Install the special event
* handling code needed to make
* the window system a guest 
* of Hypercard.
*
* Note that the handlers go
* in the application heap
* so that they die a natural
* death at the end of the 
* Hypercard session
*
* returns a handle to the event
* patch (which is locked in the 
* application heap).
*
* 11.26.89 Installs the global
* data in the system resource fork
* also, move the event handler into the
* system heap so that it stays
* alive on us.
***********************/
{
 wdGlobalsPtr  windP;
 Handle event_handler;    
 Handle sysPlace;
 void   *oldTrap;
 long   hSiz;
 
 windP = (wdGlobalsPtr)sys_alloc( (long)sizeof( wdGlobals ));
 if( windP ){
 /*** 11.26.89   ***/
 event_handler = GetResource( ‘EXEC’, EVENT_EXEC );

 if( validHandle( event_handler  ) ){
 
 /*** copy our handler into the system heap***/
 hSiz = GetHandleSize( event_handler );
 
 if( sysPlace = sys_alloc( hSiz ) ){
 BlockMove( *event_handler, sysPlace, hSiz );

 oldTrap = (void *)NGetTrapAddress( EVENT_TRAP, ToolTrap);

 NSetTrapAddress( sysPlace, EVENT_TRAP, ToolTrap );
 
 SetWindowHeader( EVENT_TRAP, (void *)windP );
 
 /*** need to save off oldTrap where ***/
 /*** everyone can find it***/
 windP->evtProc  = sysPlace;
 windP->oldTrap  = oldTrap;
 windP->list   = 0L; /*no windows in our list yet*/
 windP->userData = 0L;  
 /*** initialize this for the user ***/
 }
 }/*** if validHandle( event_handler ) ***/
 }
}

long  do_remove( paramPtr, windP )
 XCmdBlockPtr  paramPtr;
 wdGlobalsPtr  windP;
/***********************
* Remove the event patch
* so that normal event processing can
* occur.
*
* paramPtr->params[1] is a
* handle to the routine to 
* remove.  This routine is
* a standard code type resource
* and we store the old trap
* address at 10(entrypoint)
*
* -- 11.26.89 --
* Our patch is in if windP is
* valid AND its wdProc field is
* valid.  wdProc contains a
* handle to the event patch.
***********************/
{
 Ptr    patch  = (Ptr)windP->evtProc;
 postHand post_it= windP->list;
 postHand next_it;
 
 /*** unload the patch  ***/
 NSetTrapAddress( windP->oldTrap, EVENT_TRAP, ToolTrap);
 
 /*** unload all data in any open windows***/
 while( post_it ){
 next_it = (*post_it)->next;
 remove_post( windP, post_it );
 post_it = next_it;
 }
 
 /*** unload patch & globals***/
 DisposPtr( patch );
 DisposPtr( (Ptr)windP );
}

long  do_open( name,  paramPtr, windP )
 char   *name;
 XCmdBlockPtr  paramPtr;
 wdGlobalsPtr  windP;
/***********************
* open a window with the 
* given name and add it to the
* list.
*
* Open accepts the following 
* parameters:
*
* param1: name (must be unique)
* param2: rect
* param3: procID (windowtype)
* param4: visible
* param5: List data
*
* if the window is already open
* do nothing but bring it to the 
* front.
*
* Windows automatically get a list
* assigned to them.
*
***********************/
{
 Rect   theRect;
 short  visible;
 short  theProc;
 postHand postH;
 WindowPtrtheWind;
 postPtrpp;
 SysEnvRectheWorld;
 Str255 pName;
 
 ListHandle theList;
 Point  cSize;
 Rect   dataBounds;
 FontInfo theFont;
 char   *start;
 char   *end;
 Cell   theCell;
 short  colCnt;
 short  rowCnt;
 short  displayRows;
 short  displayCols;
 short  rowNum = 1;
 
 /*** parse the parameter list***/
 if( postH = Find_Window_By_Name( windP->list, name ) ){
 ShowWindow( (*postH)->wind );
 BringToFront( (*postH)->wind );
 return( 0 );
 }
 
 parseRect( paramPtr->params[2], &theRect );
 
 theProc = (short)matchToken( paramPtr->params[3], WINDOW_TYPES );
 
 visible = (short)parseNum( *(paramPtr->params[4] ) );

 /*** if all the parameters are    ***/
 /*** valid, do the open, otherwise***/
 /*** alert the user.***/
 pName[0] = ‘\0’;
 strcat( (char *)pName, (char *)name );
 CtoPstr( (char *)pName );
 
 postH = (postHand)NewSysHandle( (long)sizeof( post ) );
 if( !validHandle( postH ) )
 return( -1 );
 
 MoveHHi( postH );
 HLock( postH );
 HNoPurge( postH );
 
 add_post( windP, postH );
 
 pp = *postH;

 theWind = NewWindow( &(pp->wStore), &theRect, pName, visible, theProc, 
IN_FRONT, GO_AWAY, 0L );
 SetPort( theWind );
 
 pp->wind = theWind;
 pp->list = 0L;
 pp->id = 0;
 pp->is_color= FALSE;
 
 /*** NOTE: this approach doesn’t work for a color***/
 /*** mac that has it’s monitor set to 2-color!!!***/
 /** NEED TO DETERMINE WHETHER WE HAVE 2 COLORS IN***/
 
 if( SysEnvirons( 1, &theWorld ) == noErr )
 if( theWorld.hasColorQD )
 pp->is_color = TRUE;
 
 (pp->name)[0] = ‘\0’;
 strcat( (char *)(pp->name), name );
 
 /*** ADD A LIST TO THIS WINDOW ***/
 TextFont( 3 );
 TextSize( 12 );
 GetFontInfo( &theFont );
 cSize.v = theFont.ascent + theFont.descent + theFont.leading;

 /*** set the rectangle to local coordinates ***/
 theRect.bottom  = theRect.bottom - theRect.top;
 theRect.right   = theRect.right - theRect.left;
 theRect.top   = 2;
 theRect.left  = 2;
 cSize.h = theRect.right - theRect.left;

 displayRows = (theRect.bottom - theRect.top) / cSize.v;
 displayCols = (theRect.right - theRect.left) / cSize.h;
 
 theRect.bottom = theRect.top + (displayRows * cSize.v);
 theRect.right  = theRect.left + (displayCols * cSize.h );
 
 dataBounds.top  = 0;
 dataBounds.left = 0;
 dataBounds.bottom = 1;
 dataBounds.right  = 1;
 
 /*** adjust rectangle for scroll bars ***/
 theRect.right -= 15 * ((displayRows > 1 )? 1 : 0 );
 theRect.bottom -= 15 * ((displayCols > 1 )? 1 : 0 );
 
 theList = LNew( &theRect, &dataBounds, cSize, 0, theWind, TRUE, TRUE, 
displayCols > 1, displayRows > 1 );

 pp->list = theList;
 rowCnt = 0;
 
 /*** (*) Put the data into the cells***/          
 LDoDraw( FALSE, theList );
 if( paramPtr->params[5] ){
 HLock( paramPtr->params[5] );
 end = start = *(paramPtr->params[5] );
 
 for( colCnt = 0; colCnt < dataBounds.right; colCnt++ ){
 theCell.h = colCnt;
 
 /*** add rows until no more data ***/
 while( *start ){
 theCell.v = rowCnt++;
 while( *end != ‘\0’ && *end != CR )
 end++;
 
 rowNum = LAddRow( 1, rowCnt, theList );
   LSetCell( start, (short)(end-start), theCell, theList);
 start = end++;
 }
 }
 HUnlock( paramPtr->params[5] );
 }
 LDoDraw( TRUE, theList );
 
 /*** select the default cell ***/
 theCell.v = theCell.h = 0;
 LSetSelect( TRUE, theCell, theList );
 
 return( noErr );
}
 
long  do_close( name, paramPtr, windP )
 char   *name;
 XCmdBlockPtr  paramPtr;
 wdGlobalsPtr  windP;
/***********************
* given the name of a window,
* look it up in the window list
* and then try to close it
*
***********************/
{
 postHand postH;
 
 /*** find the window ***/
 if( postH = Find_Window_By_Name( windP->list, name  ) )
 remove_post( windP, postH );
}
 
long  do_show( name, paramPtr, windP )
 char   *name;
 XCmdBlockPtr  paramPtr;
 wdGlobalsPtr  windP;
/***********************
*
*
***********************/
{
 postHand postH;
 
 /*** find the window ***/
 if( postH = Find_Window_By_Name( windP->list, name ) ){
 ShowWindow( (*postH)->wind );
 BringToFront( (*postH)->wind );
 }
}
 
long  do_hide( name, paramPtr, windP )
 char   *name;
 XCmdBlockPtr  paramPtr;
 wdGlobalsPtr  windP;
/***********************
*
*
***********************/
{
 postHand postH;
 
 /*** find the window ***/
 if( postH = Find_Window_By_Name( windP->list, name ) )
 HideWindow( (*postH)->wind );
}
 
long  do_additem( name, paramPtr, windP )
 char   *name;
 XCmdBlockPtr  paramPtr;
 wdGlobalsPtr  windP;
/***********************
* add an item to the end of 
* the windows list list 
*
* param[2] is the item to add.
***********************/
{
 Handle data;
 postHand postH;
 
 /*** find the window ***/
 if( postH = Find_Window_By_Name( windP->list, name ) ){
 }
}

long  do_deleteitem( name, paramPtr, windP )
 char   *name;
 XCmdBlockPtr  paramPtr;
 wdGlobalsPtr  windP;
/***********************
* remove an item from the window
* list 
* 
* param[2] contains the number
* of the item to remove
* 
***********************/
{
 postHand postH;
 
 if( postH = Find_Window_By_Name( windP->list, name ) ){
 }
}

long  do_clear( name, paramPtr, windP )
 char   *name;
 XCmdBlockPtr  paramPtr;
 wdGlobalsPtr  windP;
/***********************
* erase the contents of a window
* and deallocate all it’s drawing
* objects.
***********************/
{
 postHand postH;
 GrafPtroldPort;
 
 /*** find the window ***/
 if( postH = Find_Window_By_Name( windP->list, name ) ){
 
 GetPort( &oldPort );
 SetPort( (*postH)->wind );
 EraseRect( &((*postH)->wind)->portRect );
 InvalRect( &((*postH)->wind)->portRect );
 SetPort( oldPort );
 }
}
Listing 3:

/************************************/
/* File: EventPatch.c*/
/* */
/* Patch to get next event*/
/* to allow us to intercept events */
/* from Hypercard. */
/* */
/* check to see if we get the event*/
/* if not, pass it back on, */
/* otherwise, handle the event and */
/* return a null event to Hypercard*/
/* */
/* The real getnextevent will unbias*/
/* the stack parameters so we need */
/* to save them off before we call */
/* the real trap */
/* */
/* The Boolean result of GetNextEvt*/
/* will be at 0(A7) immediately  */
/* after calling the real trap*/
/* */
/* If we took the event, then all we*/
/* need do is set the result of  */
/* GetNextEvent to false. */
/* */
/* Note that we are post-processing*/
/* the trap here.*/
/* --------------------------------*/
/* ©1989 Donald Koscheka  */
/* All Rights Reserved    */
/************************************/

#include<HyperXCMD.h>
#include“WindowUtils.h”
#include“WindowCommands.h”
#include<SetUpA4.h>

/*** NOTE: you can have no stack frames in the ***/
/*** intercept since this is a stack based trap***/
/*** That is to say, the stack is already biased***/
/*** correctly for the trap -- any local variables ***/
/*** must be declared as statics below, no locals***/
/*** are allowed in main. ***/

static  void*returnAddress;
static  EventRecord*theEvent;
static  short    evtMask;
static  short    haveEvt;
static  WindowPtrwhichWindow;
static  Rectr;
static  short    windoPart;
static  void*oldA4;
static  wdGlobalsPtr windP;
static  ProcPtr  oldTrap;
static  postHand thePost;

/********************************************/
/* Important note: */
/* */
/* Patch behaved badly with modaldialg and   */
/* the scripting window due to the fact that*/
/* both of these facilities require a valid  */
/* A4 register.  The fix was to save off the*/
/* old value of A4 before getting the new    */
/* value of A4.  */
/* */
/* Since oldA4 is a “pseudo global” (ie its  */
/* referenced off our value of A4, we must   */
/* first setup A4 before saving the old      */
/* value off.    */
/********************************************/

void main(){
 asm{
 Move.l A4, D0   ; we might need to save the old A4
 Move.l A0, A4   ; pointer to our globals for later.
 Move.l D0, oldA4; note that oldA4 is referenced off A4
 
 Move.l 10(A0), windP ; handle to the window header data
 
 Move.l (A7)+, returnAddress; save the real return address...
 Move.l (A7), theEvent  ; save the event record pointer
 Move.w 4(A7), evtMask  ; and the event mask
 }
 
 oldTrap = windP->oldTrap;
 
 asm{   ; left the stack intact for the call
 Move.l oldTrap, A0
 Jsr    (A0)
 Move.b (A7), haveEvt; did we get an event?
 }
 
 whichWindow = FrontWindow();
 
 if( windP && haveEvt && (((WindowPeek)whichWindow)->windowKind != dialogKind) 
 ){
 switch( theEvent->what ){
 case mouseDown:
 
 windoPart = FindWindow( theEvent->where, &whichWindow);
 
 if( (windoPart >= inContent ) ){
 /*** need to temporarily unpatch GetNextEvent***/
 /*** for the mouse tracking stuff because some***/
 /*** tracking calls also call getNextEvent!!!***/
 NSetTrapAddress( windP->oldTrap, EVENT_TRAP, ToolTrap);

 switch ( windoPart ){
 case inGoAway:
 if( thePost = find_post( windP, whichWindow))
 if (TrackGoAway( whichWindow, 
 theEvent->where) ){
 remove_post( windP, thePost );
 haveEvt = 0;
 }
 break;
 case inDrag:
 /*** calculate the drag rect ***/
 /*** need a non-A5 way of getting***/
 /*** the screen dimensions.***/
 r.top  = 0;
 r.left = 0;
 r.bottom= 1024;
 r.right = 1024;
 
 DragWindow( whichWindow, theEvent->where, &r );
 haveEvt = 0;
 break;
 case inGrow:
 /*** MyGrowWindow( whichWindow, theEvent->where         );***/
 /*** redraw the contents ***/
 haveEvt = 0;
 break;
 case inContent:
 if (whichWindow != FrontWindow() ){
 SelectWindow( whichWindow );
 haveEvt = 0;
 }
 else 
 if ( thePost = find_post( windP, whichWindow ) ){
 GlobalToLocal( &theEvent->where );
 if( LClick( theEvent->where, 
 theEvent->modifiers,
 (*thePost)->list ) ) ;
 haveEvt = 0;
 }
 break;
 default: 
 break;
 }/* end switch FindWindow */
 
 /*** now re-install the patch***/
 NSetTrapAddress( windP->evtProc, EVENT_TRAP, ToolTrap);

 } /*** if mouse down in our window ***/
 break;
 case mouseUp:
 if( thePost = find_post( windP, FrontWindow() ) ){
 haveEvt = 0; 
 }
 break;
 case keyDown:
 case autoKey:
 break;
 case activateEvt:
 whichWindow = (WindowPtr)theEvent->message;
 if ( find_post( windP, whichWindow ) )
 if ( theEvent->modifiers & activeFlag ){
 r= (*whichWindow).portRect;
 r.top = r.bottom;
 r.left = r.left;
 InvalRect( &r );
 haveEvt = 0;
 }
 break;
 case updateEvt: 
 whichWindow = (WindowPtr)theEvent->message;
 if( thePost = find_post( windP, whichWindow ) ){
 SetPort( whichWindow );
 BeginUpdate( whichWindow );
 UpdateWindow( thePost , whichWindow->visRgn );
 DrawControls( whichWindow );
 EndUpdate( whichWindow );
 haveEvt = 0;
 }
 break;
 default: 
 break;
 } /* switch theEvent->what */
 } /* if haveEvt */
 
 asm{ 
 Move.b haveEvt, (A7)
 Move.l returnAddress, A0
 Move.l oldA4, A4
 Jmp    (A0)
 }
}

UpdateWindow( postH, visRgn )
 postHand postH;
 RgnHandlevisRgn;
/******************
* put your update code here
******************/
{
 ListHandle theList;
 
 theList = (*postH)->list;
 
 if( theList )
 LUpdate( visRgn, theList );
}
Listing 4:

/********************************/
/* File: WindowUtils.c    */
/* */
/* Once the object is parsed, it*/
/* is added to the draw list for*/
/* the window.   */
/* */
/* Eventually need to match */
/* multiple tokens so that we */
/* can have parameters such as:  */
/* BOLD&ITALIC&OUTLINE    */
/* ----------------------------  */
/* © 1989 Donald Koscheka */
/* All Rights Reserved    */
/********************************/

#include“WindowUtils.h”
#include<HyperUtils.h>
#include<HyperXCMD.h>

void  *GetWindowHeader( theTrap )
 short  theTrap;
/************************
* Given the trap id, return
* the item stored at offset 
* $0A off the entry point.  We
* can use this location to store
* a handle to data used by the
* trap handler
*
* The resource type in the header
* (isme) will be at 4(A0) if this
* is our patch.
*
*************************/
{
 void *tProc = (void *)NGetTrapAddress( theTrap, ToolTrap);
 void *header = 0L;
 
 asm{
 Move.l A0, -(sp)
 Move.l tProc, A0
 
 Move.l 4(A0), D0
 Cmp.l  #’isme’,D0
 Bne    @1
 
 Move.l 10(A0), header
@1 Move.l (sp)+, A0
 }
 
 return( header );
}

void    SetWindowHeader( theTrap, theHeader )
 short  theTrap;
 void *theHeader;
/************************
* Given the trap id, place the handle
* in the location at  
* offset $0A off the entry point.  We
* can use this location to store
* a handle to data used by the
* trap handler
*
*************************/
{
 void *tProc= (void *)NGetTrapAddress( theTrap, ToolTrap);
 
 asm{
 Move.l A0, -(sp)
 Move.l tProc, A0
 Move.l #’isme’, 4(A0); save a copy of entry point
 Move.l theHeader, 10(A0)
 Move.l (sp)+, A0
 }
}

postHandFind_Window_By_Name( postList, name )
 postHand postList;
 char   *name;
/***********************
* Given the name of a window
* search the post list until
* that post is found and 
* then return a handle to
* that post or NIL if the window
* is not found.
*
***********************/
{
 postHand myPost = postList;
 
 while( myPost ){
 if( strcmp( (*myPost)->name, name ) == 0 )
 return( myPost ); 
 else
 myPost = (*myPost)->next;
 }
 
 return( 0L );   
}

void  add_post( wdPtr,  postIt )
 wdGlobalsPtr  wdPtr;
 postHand postIt;
/***********************
* Add the current window 
* (postIt) to our list.
*
* basically, the item gets
* added to the end of the list
*
***********************/
{
 postHand myList = wdPtr->list;
 
 (*postIt)->next = (*postIt)->last = 0L;
 
 if( !myList )   /*** no items in the list yet     ***/
 wdPtr->list = postIt;
 else{
 while( (*myList)->next )
 myList = (*myList)->next;
 
 (*postIt)->last = myList;
 (*myList)->next = postIt;
 }
 
}

void  remove_post( wdPtr, postIt )
 wdGlobalsPtr  wdPtr;
 postHand postIt;
/***********************
* remove the requested window
* from the window list and
* deallocate all of its data
*
***********************/
{
 postHand myList = wdPtr->list;
 postHand lastPost;
 postHand nextPost;

 
 if( validHandle( (Handle)postIt ) && validHandle( (Handle)myList ) ){
 
 if( postIt == myList )
 wdPtr->list = (*postIt)->next;
 else{
 while( myList )
 if( myList == postIt ){
 lastPost= (*postIt)->last;
 nextPost= (*postIt)->next;
 
 if( lastPost )
 (*lastPost)->next = nextPost;
 
 if( nextPost )
 (*nextPost)->last = lastPost;
 
 break; 
 }
 else
 myList = (*myList)->next;
 }

/*** if postit is valid, we can deallocate its structures***/
 if( (*postIt)->list )
 LDispose( (*postIt)->list );
 CloseWindow( (*postIt)->wind );
 TrashHandle( (Handle)postIt );
 }
}

postHandfind_post( windP, which )
 wdGlobalsPtr  windP;
 WindowPtrwhich;
/*******************************
* if the window pointer passed in
* corresponds to a window in our list
* of post-it windows, then return the
* post that owns that window.
*
* if no match, return NIL
*******************************/
{
 postHand thePost= windP->list;
 
 while( thePost ){
 if( (*thePost)->wind == which )
 return( thePost );
 else
 thePost = (*thePost)->next;
 }
 
 return( 0L );
}

 

Community Search:
MacTech Search:

Software Updates via MacUpdate

Latest Forum Discussions

See All

The Legend of Heroes: Trails of Cold Ste...
I adore game series that have connecting lore and stories, which of course means the Legend of Heroes is very dear to me, Trails lore has been building for two decades. Excitedly, the next stage is upon us as Userjoy has announced the upcoming... | Read more »
Go from lowly lizard to wicked Wyvern in...
Do you like questing, and do you like dragons? If not then boy is this not the announcement for you, as Loongcheer Game has unveiled Quest Dragon: Idle Mobile Game. Yes, it is amazing Square Enix hasn’t sued them for copyright infringement, but... | Read more »
Aether Gazer unveils Chapter 16 of its m...
After a bit of maintenance, Aether Gazer has released Chapter 16 of its main storyline, titled Night Parade of the Beasts. This big update brings a new character, a special outfit, some special limited-time events, and, of course, an engaging... | Read more »
Challenge those pesky wyverns to a dance...
After recently having you do battle against your foes by wildly flailing Hello Kitty and friends at them, GungHo Online has whipped out another surprising collaboration for Puzzle & Dragons. It is now time to beat your opponents by cha-cha... | Read more »
Pack a magnifying glass and practice you...
Somehow it has already been a year since Torchlight: Infinite launched, and XD Games is celebrating by blending in what sounds like a truly fantastic new update. Fans of Cthulhu rejoice, as Whispering Mist brings some horror elements, and tests... | Read more »
Summon your guild and prepare for war in...
Netmarble is making some pretty big moves with their latest update for Seven Knights Idle Adventure, with a bunch of interesting additions. Two new heroes enter the battle, there are events and bosses abound, and perhaps most interesting, a huge... | Read more »
Make the passage of time your plaything...
While some of us are still waiting for a chance to get our hands on Ash Prime - yes, don’t remind me I could currently buy him this month I’m barely hanging on - Digital Extremes has announced its next anticipated Prime Form for Warframe. Starting... | Read more »
If you can find it and fit through the d...
The holy trinity of amazing company names have come together, to release their equally amazing and adorable mobile game, Hamster Inn. Published by HyperBeard Games, and co-developed by Mum Not Proud and Little Sasquatch Studios, it's time to... | Read more »
Amikin Survival opens for pre-orders on...
Join me on the wonderful trip down the inspiration rabbit hole; much as Palworld seemingly “borrowed” many aspects from the hit Pokemon franchise, it is time for the heavily armed animal survival to also spawn some illegitimate children as Helio... | Read more »
PUBG Mobile teams up with global phenome...
Since launching in 2019, SpyxFamily has exploded to damn near catastrophic popularity, so it was only a matter of time before a mobile game snapped up a collaboration. Enter PUBG Mobile. Until May 12th, players will be able to collect a host of... | Read more »

Price Scanner via MacPrices.net

Limited-time sale: 13-inch M3 MacBook Airs fo...
Amazon has the base 13″ M3 MacBook Air (8GB/256GB) in stock and on sale for a limited time for $989 shipped. That’s $110 off MSRP, and it’s the lowest price we’ve seen so far for an M3-powered... Read more
13-inch M2 MacBook Airs in stock today at App...
Apple has 13″ M2 MacBook Airs available for only $849 today in their Certified Refurbished store. These are the cheapest M2-powered MacBooks for sale at Apple. Apple’s one-year warranty is included,... Read more
New today at Apple: Series 9 Watches availabl...
Apple is now offering Certified Refurbished Apple Watch Series 9 models on their online store for up to $80 off MSRP, starting at $339. Each Watch includes Apple’s standard one-year warranty, a new... Read more
The latest Apple iPhone deals from wireless c...
We’ve updated our iPhone Price Tracker with the latest carrier deals on Apple’s iPhone 15 family of smartphones as well as previous models including the iPhone 14, 13, 12, 11, and SE. Use our price... Read more
Boost Mobile will sell you an iPhone 11 for $...
Boost Mobile, an MVNO using AT&T and T-Mobile’s networks, is offering an iPhone 11 for $149.99 when purchased with their $40 Unlimited service plan (12GB of premium data). No trade-in is required... Read more
Free iPhone 15 plus Unlimited service for $60...
Boost Infinite, part of MVNO Boost Mobile using AT&T and T-Mobile’s networks, is offering a free 128GB iPhone 15 for $60 per month including their Unlimited service plan (30GB of premium data).... Read more
$300 off any new iPhone with service at Red P...
Red Pocket Mobile has new Apple iPhones on sale for $300 off MSRP when you switch and open up a new line of service. Red Pocket Mobile is a nationwide MVNO using all the major wireless carrier... Read more
Clearance 13-inch M1 MacBook Airs available a...
Apple has clearance 13″ M1 MacBook Airs, Certified Refurbished, available for $759 for 8-Core CPU/7-Core GPU/256GB models and $929 for 8-Core CPU/8-Core GPU/512GB models. Apple’s one-year warranty is... Read more
Updated Apple MacBook Price Trackers
Our Apple award-winning MacBook Price Trackers are continually updated with the latest information on prices, bundles, and availability for 16″ and 14″ MacBook Pros along with 13″ and 15″ MacBook... Read more
Every model of Apple’s 13-inch M3 MacBook Air...
Best Buy has Apple 13″ MacBook Airs with M3 CPUs in stock and on sale today for $100 off MSRP. Prices start at $999. Their prices are the lowest currently available for new 13″ M3 MacBook Airs among... Read more

Jobs Board

Solutions Engineer - *Apple* - SHI (United...
**Job Summary** An Apple Solution Engineer's primary role is tosupport SHI customers in their efforts to select, deploy, and manage Apple operating systems and Read more
DMR Technician - *Apple* /iOS Systems - Haml...
…relevant point-of-need technology self-help aids are available as appropriate. ** Apple Systems Administration** **:** Develops solutions for supporting, deploying, Read more
Omnichannel Associate - *Apple* Blossom Mal...
Omnichannel Associate - Apple Blossom Mall Location:Winchester, VA, United States (https://jobs.jcp.com/jobs/location/191170/winchester-va-united-states) - Apple Read more
Operations Associate - *Apple* Blossom Mall...
Operations Associate - Apple Blossom Mall Location:Winchester, VA, United States (https://jobs.jcp.com/jobs/location/191170/winchester-va-united-states) - Apple Read more
Cashier - *Apple* Blossom Mall - JCPenney (...
Cashier - Apple Blossom Mall Location:Winchester, VA, United States (https://jobs.jcp.com/jobs/location/191170/winchester-va-united-states) - Apple Blossom Mall Read more
All contents are Copyright 1984-2011 by Xplain Corporation. All rights reserved. Theme designed by Icreon.