TweetFollow Us on Twitter

Object Toolbox
Volume Number:7
Issue Number:11
Column Tag:C Workshop

Related Info: List Manager Window Manager Control Manager

Objects and the Toolbox

By Rob Hafernik, Austin, TX

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

Think C Objects and the Toolbox

[Rob Hafernik is lucky enough to be paid to program the Macintosh, by Technology Works, in Austin, Texas.]

All Mac programmers should be using MacApp these days. No question about it, it’s where the action is in Mac programming. Once in a while, however, you come upon a project that MacApp isn’t suited for. It could be that MacApp’s application sizes are too large for some tight project or that the project has to be in C, or whatever. It must be a good reason, or you wouldn’t want to part with all the goodies MacApp gives you for free.

This is the position I found myself in recently, and I started thinking about ways to combine Object Programming with the Mac Toolbox, but without MacApp. This is a bit difficult, since none of the Toolbox routines know an object from a linked list. I did, however, come up with a couple of good ideas, and they’re what this article is about.

This particular article explains a method for combining objects with the Toolbox List Manager, but the method could be used equally well with the Window Manager or Control Manager. Anywhere that the Toolbox allows for definition procedures, you can sneak an object in. This will give you all of the great aspects of Object Programming while still letting the Mac Toolbox do lots of the work.

Let’s back up a little. The List Manager is great if you want to display a list or grid of text, but to display anything but text (such as pictures or icons), you have to write your own definition procedure for a custom list. You also have to write your own defproc if you want items in a list to be highlighted in some way other than the standard (with a check mark, for example). Writing a definition procedure is a nasty chore that most of us shy away from since a defproc is a code resource and code resources are a pain to write and a pain to debug. If you’re in the dark about defprocs for the List Manager, now would be a good time to refer to Inside Macintosh, vol IV, page 276. There is a nice explanation of the definition procedure for lists and how they work.

When you think about the List Manager, it’s only natural to think: “A list of what”? Well, a list of objects would certainly be nice. If each item in a list were an object, then each object in the list could also have methods to do things like draw itself or perform some action when it’s double-clicked. While we’re at it, let’s make the list itself an object with methods to manipulate the list and display it.

That’s just what we have here: A list object that holds information about the list and has methods to add items, display them, select them and so on. The List object also encapsulates the List Manager so that the programmer doesn’t need to deal with it or ever write another List Manager definition procedure. Each item in the list is also an object that is a sub-class of an Item object that we’ll define. The sub-class can do anything it needs to for a particular application.

The example program I’ll use to demonstrate these objects is very simple, almost to the point of being useless. The application, which I call Icon List, can open a file’s resource fork and display any icons it finds there in a list in a window. If you look at the inheritance diagram in figure 1, you’ll see the classes we’ll use in this example. I’ll talk about the CListObj object first.

Figure 1

A CListObj contains a handle to a regular Toolbox List Manager data structure, a rectangle for viewing the list in a window, and a flag to turn drawing on and off (for efficiency when adding a lot of items to a list - it’s a lot faster to turn off drawing, add them all in, then turn drawing back on).

The List Manager in the Mac Toolbox defines a data structure that includes a handle to the code that will actually draw the items in the list and highlight them. If you’re displaying a regular, text-based list (such as the one Standard File displays), then you’ll use the built-in code that is provided by the List Manager (‘LDEF’ resource id 0). If you’re customizing the list to display something else, you provide a block of code in a code resource (of type ‘LDEF’) and tell the List Manager to use that code.

Of course that means that the code you write has to be in a code resource and that’s a lot of trouble. There is a nice little trick that avoids all of this, however. I wish I could take credit for inventing this trick, but I can’t. I don’t even know who did it first, but it was at least three or four years ago. Here’s how the trick works: You have an LDEF resource, true, but it contains only a JMP instruction to some code in your regular application. Before you initialize a list manager list, you open this “fake defproc” resource and copy into it a JMP instruction and the address of the routine in your application code that will function as a list defproc (make sure that your code is in a locked segment!). The list manager will take the handle, dereference it and jump to it. Your JMP instruction will then jump to your code and off you’ll go. When your code returns, the return address of the List Manager is right there on the stack. You now have code in your application that the List Manager thinks is a regular, old defproc - that you can write, compile, link and debug just like any other code.

The Methods

Now for the methods of CListObj. The first is IList. This is always called first to initialize the list. It sets up the List Manager list, initializes it’s fields and sets up the handle for the “Fake” defproc. The Dispose method does just the reverse, throwing everything away. Notice that it does not dispose of the items in the list - the application code should do that, usually before disposing of the CListObj. The next few methods take care of the usual events you have in a Mac program: mouse clicks, update events and activate events. Actually, you can see that most of the work is done by the List Manager, with the CListObj just acting as glue code.

Then there are methods for setting flags (DrawingOn) and scrolling the list (Scroll) and counting the number of items in the list (NumObjs). These are pretty simple, as the code shows (the Toolbox does all the work, and that’s the way we want it). Next comes the Add method. Notice that Add takes a CItemObj as it’s argument. If you look at the definition of CItemObj, you’ll see that it’s nearly empty. It consists of just one method, DrawItem. In CItemObj, even DrawItem doesn’t do anything but go into the debugger with message. This is because each program that uses a CListObj must sub-class CItemObj to do whatever is specific to that program. In the example at hand, the sub-class is called CIconItemObj, and it holds information about an ‘icon’ resource. In addition, the CIconItemObjs’ DrawItem method overrides CItemObj->DrawItem to actually plot the icon and show it’s name and number. The Add method of a CListObj knows nothing of this, of course. It just gets the CIconItemObj and adds it to the list (notice that it just adds a reference to it, it’s handle, to the list). In like fashion, the Remove method removes a CItemObj reference from the list (without disposing the actual object).

Now for the fun part. If you’ve used MacApp, you know about that wonderful object, TList. A TList is like our CListObj here, but it does not display it’s list, it just manages it. And manage it does; there are dozens of useful methods. So useful, in fact, that I’ve stolen several of them for the CListObj object. The first is ForEach. ForEach takes a function pointer and calls the function once for each object in the list, passing each object to the function. It’s the equivalent of a for loop getting each item and calling a function with each one. You can use this all sorts of ways. If the items in the list were e-mail recipients, for example, the e-mail application would do something like:

recipientList->ForEach ( SendMessage );

Now, isn’t that a nice, readable bit of code? The next method, ForEachSelected, is the same except it only calls the function for items in the list that are currently selected.

After ForEachSelected come a couple of methods to manipulate and report on the current selection, SelectNone and AnySelected. Then comes FirstThat, which is similar to ForEach in that it walks down the list calling a function you supply and passing each item in the list. FirstThat, however, stops as soon as the function you’ve supplied returns true for one of the items. In other words, FirstThat does a search for an item in the list, using the criteria in the function you pass and returning the matching item.

The IconList application shows a CListObj in action (well, some of it). When the users selects the “Open ” menu pick, they get a standard file dialog. Once they select a file, the program opens a window and starts up a CListObj. Then it opens the file’s resource fork and looks for ‘icon’ resources. For each ‘icon’ resource it finds, the program allocates a CIconItemObj, fills it’s fields (we could have defined an IIconItem method to do initialization for us) and adds it to a list. It also sets the windowKind field of the window to something special and puts a reference to the CListObj in the window’s refCon field where the application can find it later. Although the Icon List application builds the list once and for all, another application could keep adding and removing items from the list all the time. Once the window is up and the list is built, the event loop takes care of it.

For example, if an update event comes along, the program checks to see if the window is an “Icon List” window (using it’s windowKind field). If it is, the program pulls the CListObj from the window’s refCon field and calls the UpdateList method of the CListObj. What happens next is a little complicated, so keep an eye on Figure 2. The event loop calls the UpdateList method. The UpdateList method calls the Toolbox List Manager with the handle to the list and tells it to update. The Toolbox List Manager calls our “fake” defproc to draw each item in the update region. The “fake” defproc calls a dispatching function, called ListFunction, each time. The ListFunction only knows that the list is made up of a sub-class of the CListObj. It pulls the object in question out of the list and calls it’s DrawItem function with a highlight flag set true or false. After that, it’s up to the DrawItem method to do whatever it wants to in it’s rectangle. Notice that we get all the selection logic and auto-scroll for free from the Mac Toolbox. Activate and MouseDown events are similar.

That’s all there is to it. You include the CListObj code in your application, sub-class CItemObj to do whatever you want and you’ve got a displayed list with a bunch of extras. Object Programming is really nice for programmers. Suppose we wanted to add support for color icons. All we would have to do is sub-class CItemObj again with a CColorIconItemObj and change FillIconList to also scan for ‘cicn’ resources and we would be done (note that CListObj and the event loop would not change at all). CListObj could also be sub-classed. If an application allowed users to double-click on a list item to do something to that item, CListObj could be sub-classed and support for double-clicks (timing and all) added with a “DoubleClick” method. That method could detect a double-click, get the CItemObj from the list and call a “DoubleClicked” method that’s been added to it. You wouldn’t rewrite CListObj, just add to it.

Figure 2.

A similar technique can be used for other definition functions. A Control Manager defproc, for example, would be an object with fields to hold it’s data and methods to respond to the Control Managers calls for drawing, highlighting, setting the value and so on. It would also use a “fake” defproc handle and a dispatching function to trick the Toolbox into using objects.

I always like to hear reactions to my work. I can be reached via e-mail at America On Line as ‘rob042’ or you can catch me as ‘shokwave@well.sf.ca.us’ on the internet.

/*+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-
 CItemObj.h - Defines an abstract List Item
class that must be overriden.
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+- */

#ifndef CLASS_COBJECT
#include"CObject.h"
#endif

#define CLASS_CITEMOBJ

/* :::::::::: item object :::::::::: */
struct CItemObj:CObject {
 /*** no fields... */

 /*** methods */
 void DrawItem ( Boolean selected, 
 /* true if this item is to be
 drawn selected */
 Rect *dispR );  
 /* display rectangle */
 };

/*+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-
 CItemObj.c - Implements the abstract List Item
Class (must be overridden).
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+- */

#include "CItemObj.h"

/* |||||||||||||||||||| */
 void CItemObj::DrawItem ( Boolean selected, 
 Rect *dispR )
 {
 DebugStr( "You MUST override CItemObj::DrawItem" );
 }

/*+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-
 CIconItemObj.h - Defines an Icon List Item 
subclass of CItemObj.
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+- */

#ifndef CLASS_COBJECT
#include"CObject.h"
#endif

#ifndef CLASS_CITEMOBJ
#include"CItemObj.h"
#endif

#define CLASS_CICONITEMOBJ

/* :::::::::: list item object :::::::::: */
struct CIconItemObj:CItemObj {
 /*** fields... */
 int    id; /* icon res id number */
 Str255 name;    /* icon res name */
 Handle bits;    /* handle to icon image */
 
 /*** methods  */
 void DrawItem ( Boolean selected, 
 Rect *dispR );
 /* draw the icon item */
 };

/*+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-
 CIconItemObj.c - Implements an object that holds an icon from a resource 
file and draws it.
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+- */

#include "CIconItemObj.h"
#include <color.h>

/* |||||||||||||||||||| */
 void CIconItemObj::DrawItem ( Boolean selected,
 Rect *dispR )
 {
 Rect   arect;
 Str255 numStr;
 
 /*** draw an icon item */
 EraseRect ( dispR );
 
 /*** draw the icon (inset by 4 pixels) */
 arect = *dispR;
 arect.top += 4;
 arect.bottom = arect.top +32;
 arect.left += 4;
 arect.right = arect.left + 32;
 PlotIcon ( &arect, bits );
 
 /*** display the name and id next to it */
 TextFont ( systemFont );
 TextSize ( 12 );
 TextFace ( 0 );
 arect.left = arect.right + 8;
 arect.right = arect.left + 40;
 arect.top += 9;
 arect.bottom -= 9;
 NumToString ( (long) id, numStr );
 TextBox ( &numStr[1], (int) numStr[0], 
 &arect, teJustLeft );
 
 TextFont ( geneva );
 TextSize ( 9 );
 arect.left = arect.right + 8;
 arect.right = dispR->right;
 arect.bottom += 16;
 TextBox ( &name[1], (int) name[0], 
 &arect, teJustLeft );
 
 /*** if selected, invert the rect (could use 
 hilite mode) */
 if ( selected ) {
 InvertRect ( dispR );
 }
 }

/*+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-
 CObjList.h - Defines an object that holds a
list of other objects and uses the List Manager
to display them.  Note that:
 * List is one-dimensional
 * Objects in list must be a sub-class of
 ItemObj object
 * ListObj is 1 based (not 0 based).
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+- */

#define CLASS_OBJLIST

#ifndef CLASS_CITEMOBJ
#include"CItemObj.h"
#endif

#ifndef CLASS_COBJECT
#include"CObject.h"
#endif

#ifndef NULL
#define NULL0L
#endif

#define MYPROCID 42

/* :::: LDEF dispatch routine definition :::: */
pascal void ListFunction (int lMessage,
 Boolean lSelect, Rect *lRect, Cell lCell,
 int lDataOffSet, int lDataLen, 
 ListHandle lHandle );

/* :::::::::: list object :::::::::: */
struct CListObj:CObject {
 /*** fields */
 ListHandle theList; /* ROM List Manager list */
 Rect viewRect; /* location of the view in the
 window (incl scroll bar) */
 Boolean drawIt; /* drawing on or not */
 
 /*** methods */
 void IList ( Rect *viewR,
 /* view rectangle in win coords */
 int cellHeight, /* height of a row in pixels */
 Boolean hasScrollBar, /* true if vertical scroll bar */
 int selectMethod, /* List Manager selection flags */
 WindowPtr theWin ); /* window this list displays in */

 void Dispose ( void );  /* throw away everything (but 
 not objects in list) */
 
 Boolean MouseInList ( Point thePoint, 
 /* location of mouse click */
 int modifiers ); /* modifier keys */

 void UpdateList (RgnHandle updateRgn );
 /* update list(pass visRgn handle)*/

 void ActivateList ( Boolean activate );
 /* activate event for list,
 true or false */

 void DrawingOn ( Boolean drawing );
 /* turns drawing on and off */

 void Scroll ( int amount );
 /* scroll by amoumt (+ is up) */
 
 int NumObjs ( void );  
 /* return number of objs in list */

 void Add ( CItemObj *theObject );
 /* add a new object to the list */

 void Remove ( CItemObj *theObject );
 /* remove the obj from the list */

 void ForEach ( VoidFunc doThis ); 
 /* execute the function, passing
 each object in the list */

 void ForEachSelected ( VoidFunc doThis );
 /* same as ForEach, but selected */

 void SelectNone ( void );
 /* de-select all */

 Boolean AnySelected ( void );
 /* true if any objs are selected */

 CItemObj *FirstThat ( BooleanFunc test );
 /* return first object in list that
 satisfies test */

 CItemObj *GetIndObject ( int n ); 
 /* return nth object, if it any */
 };

/*+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-
 CListObj.c - Implements an object that holds
a list of other objects and uses the List Manager
to display them.
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+- */

#ifndef CLASS_LISTOBJ
#include"CListObj.h"
#endif

/* |||||||||||||||||||| */
 void CListObj::IList ( Rect *viewR,
 int cellHeight, Boolean hasScrollBar,
 int selectMethod, WindowPtr theWin )
 {
 Rect   dataBounds, viewBounds;
 Point  cellSize;
 Handle fakeDefProc;
 int    jmpInstruction;
 long   jmpAddress;
 
 /*** create the "fake" defproc handle and put
 a jump to our code in it */
 jmpInstruction = 0x4EF9;
 jmpAddress = (long) ListFunction;
 fakeDefProc = GetResource( 'LDEF', MYPROCID );
 HLock ( fakeDefProc );
 BlockMove ( &jmpInstruction, *fakeDefProc,
 sizeof ( int ) );
 BlockMove ( &jmpAddress, *fakeDefProc + 2,
 sizeof ( long ) );
 HUnlock ( fakeDefProc );

 /*** initialize the list object, set up the
 corresponding list manager object */
 theList = NULL;
 viewRect = viewBounds = *viewR;
 drawIt = true;
 SetRect ( &dataBounds, 0, 0, 1, 0 );
 cellSize.h = viewR->right - viewR->left;
 cellSize.v = cellHeight;
 viewBounds.right -= 15;
 theList = LNew ( &viewBounds, &dataBounds,
 cellSize, MYPROCID, theWin, true,
 false, false, hasScrollBar );
 (*theList)->selFlags = (SignedByte) selectMethod;
 (*theList)->refCon = (long) this;
 }

/* |||||||||||||||||||| */
 void CListObj::Dispose ( void )
 {
 /*** dispose of the list, but not contents */
 LDispose ( theList );
 inherited::Dispose ( );
 }

/* |||||||||||||||||||| */
 BooleanCListObj::MouseInList ( Point thePoint,
 int modifiers )
 {
 /*** process a mouse click in the list by
 calling the toolbox List Manager */
 if ( PtInRect(thePoint,&viewRect)&&drawIt ) {
 LClick ( thePoint, modifiers, theList );
 return true; /* return true if mouse was in list */
 }
 return false; /* return false if it was a miss */
 }

/* |||||||||||||||||||| */
 void CListObj::UpdateList( RgnHandle updateRgn )
 {
 /*** process update event (only if drawing is on) */
 if ( drawIt ) LUpdate ( updateRgn, theList );
 }

/* |||||||||||||||||||| */
 void CListObj::ActivateList ( Boolean activate )
 {
 /*** process an activate event */
 LActivate ( activate, theList );  
 }

/* |||||||||||||||||||| */
 void CListObj::DrawingOn ( Boolean drawing )
 {
 /*** turn drawing on or off */
 drawIt = drawing;
 LDoDraw ( drawing, theList );
 }

/* |||||||||||||||||||| */
 void CListObj::Scroll ( int amount )
 {
 /*** scroll the list */
 LScroll ( 0, amount, theList );
 }

/* |||||||||||||||||||| */
 int CListObj::NumObjs ( void )
 {
 /*** return the # of objects in the list */
 return ( (*theList)->dataBounds.bottom );
 }

/* |||||||||||||||||||| */
void CListObj::Add ( CItemObj *theObject )
 {
 int  rowNum;
 long value;
 Cell theCell;
 
 /*** add an item to the bottom of the list */
 rowNum = LAddRow ( 1, 32760, theList );
 value = (long) theObject;
 theCell.h = 0;
 theCell.v = rowNum;
 LSetCell(&value, sizeof (long), theCell, theList);
 }

/* |||||||||||||||||||| */
void CListObj::Remove ( CItemObj *theObject )
 {
 int  nRows, len;
 long value;
 Cell theCell;
 
 /*** delete the object from the list, if it's
 in it (doesn't delete the object) */
 len = sizeof ( long );
 nRows = NumObjs ( );
 for ( theCell.h = 0, theCell.v = 0; 
 theCell.v < nRows; ++theCell.v ) {
 LGetCell( &value, &len, theCell, theList );
 if ( value == (long) theObject ) {
 LDelRow ( 1, theCell.v, theList );
 return;
 }
 }
 }

/* |||||||||||||||||||| */
void CListObj::ForEach ( VoidFunc doThis )
 {
 int    nRows, len;
 long   value;
 Cell   theCell;
 CItemObj *theObject;
 
 /*** call the doThis function, passing each
 object in the list */
 len = sizeof ( long );
 nRows = NumObjs ( );
 for ( theCell.h = 0, theCell.v = 0; 
 theCell.v < nRows; ++theCell.v ) {
 LGetCell ( &value, &len,theCell, theList );
 theObject = (CItemObj *) value;
 doThis ( theObject );
 }
 }

/* |||||||||||||||||||| */
void CListObj::ForEachSelected ( VoidFunc doThis )
 {
 int    nRows, len;
 long   value;
 Cell   theCell;
 CItemObj *theObject;
 
 /*** call the VoidFunc, passing each selected
 object in the list */
 len = sizeof ( long );
 nRows = NumObjs ( );
 theCell.h = theCell.v = 0;
 while (LGetSelect(true, &theCell, theList)) {
 LGetCell (&value, &len, theCell, theList);
 theObject = (CItemObj *) value;
 doThis ( theObject );
 ++theCell.v;
 }
 }

/* |||||||||||||||||||| */
void CListObj::SelectNone ( void )
 {
 int    nRows;
 long   value;
 Cell   theCell;
 CItemObj *theObject;
 
 /*** de-select all items */
 nRows = NumObjs ( );
 theCell.h = theCell.v = 0;
 while (LGetSelect(true,&theCell,theList)) {
 LSetSelect ( false, theCell, theList );
 ++theCell.v;
 }
 }

/* |||||||||||||||||||| */
Boolean CListObj::AnySelected ( void )
 {
 Cell   theCell;
 
 /*** return true if any item is selected */
 theCell.h = theCell.v = 0;
 return (LGetSelect(true,&theCell,theList));
 }

/* |||||||||||||||||||| */
CItemObj *CListObj::FirstThat ( BooleanFunc test )
 {
 int    nRows, len;
 long   value;
 Cell   theCell;
 CItemObj *theObject;
 
 /*** return the first item that passes the
 test in function test */
 len = sizeof ( long );
 nRows = NumObjs ( );
 for ( theCell.h = 0, theCell.v = 0; 
 theCell.v < nRows; ++theCell.v ) {
 LGetCell(&value, &len, theCell, theList);
 theObject = (CItemObj *) value;
 if ( test ( theObject ) ) 
 return ( theObject );
 }
 return ( NULL );
 }

/* |||||||||||||||||||| */
CItemObj * CListObj::GetIndObject ( int n )
 {
 int    len;
 long   value;
 Cell   theCell;
 CItemObj *theObject;

 /*** return the nth object in the list (list
 manager is 0 based, ListObj is 1 based) */
 --n;
 if ( (n < NumObjs ( )) && (n >= 0) ) {
 theCell.h = 0;
 theCell.v = n;
 len = sizeof ( long );
 LGetCell(&value, &len, theCell, theList);
 theObject = (CItemObj *) value;
 return ( theObject );
 }
 return ( NULL );
 }

/* |||||||||||||||||||| */
/* |||||||||||||||||||| */
pascal void ListFunction ( int lMessage, 
 Boolean lSelect, Rect *lRect, 
 Cell lCell, int lDataOffset, 
 int lDataLen, ListHandle lHandle )
 {
 CItemObj *item;
 long   *theData;
 
 /*** dispatch a message sent to a list by the
 list manager to the object it goes with */
 switch ( lMessage ) {
 case lInitMsg:
 break;
 case lDrawMsg:
 if ( lDataLen > 0 ) {
 theData = (long *) **(*lHandle)->cells;
 theData += lDataOffset/4;
 item = (CItemObj *) *theData;
 item->DrawItem ( lSelect, lRect );
 }
 break;
 case lHiliteMsg:
 if ( lDataLen > 0 ) {
 theData = (long *) **(*lHandle)->cells;
 theData += lDataOffset/4;
 item = (CItemObj *) *theData;
 item->DrawItem ( lSelect, lRect );
 }
 break;
 case lCloseMsg:
 break;
 }
 }

/*+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-
 main.c - main program for Icon List.
It’s function is to show off the List Object.
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+- */

/* |||||||||| defines |||||||||| */
#define ICONWINKIND4242

#include<oops.h>

#ifndef CLASS_LISTOBJ
#include"CListObj.h"
#endif

#ifndef CLASS_ICONITEMOBJ
#include"CIconItemObj.h"
#endif

/* |||||||||| Globals |||||||||| */
intDoneFlag;
EventRecord TheEvent;
Point   TheMousePt;
Rect    DragRect;
MenuHandleAppleMenu;
MenuHandleFileMenu;
MenuHandleEditMenu;

/* |||||||||||||||||||| */
main ( )
 {
 WindowPtrwhichWindow;
 char   key;
 CListObj *anIconList;
 GrafPtroldPort;

 Initialize ( );
 while ( !DoneFlag ) {
 /*** the usual event loop... */
 GetNextEvent ( everyEvent, &TheEvent );
 switch ( TheEvent.what ) {
 case activateEvt:
 whichWindow = (WindowPtr) TheEvent.message;
 if ( ((WindowPeek)
 whichWindow)->windowKind ==
 ICONWINKIND ) {
 anIconList = 
  (CListObj*)GetWRefCon(whichWindow);
 anIconList->ActivateList(
 TheEvent.modifiers & activeFlag);
 }
 break;
 case updateEvt:
 whichWindow = (WindowPtr) TheEvent.message;
 if ( ((WindowPeek) 
 whichWindow)->windowKind ==
 ICONWINKIND ) {
 GetPort ( &oldPort );
 SetPort ( whichWindow );
 anIconList = 
  (CListObj*)GetWRefCon(whichWindow);
 BeginUpdate ( whichWindow );
 anIconList->UpdateList(
 whichWindow->visRgn);
 EndUpdate ( whichWindow );
 SetPort ( oldPort );
 }
 break;
 case mouseDown:
 switch ( FindWindow ( TheEvent.where,
 &whichWindow ) ) {
 case inMenuBar:
 DoMenu ( 
 MenuSelect ( TheEvent.where )
 );
 break;
 case inSysWindow:
 SystemClick ( &TheEvent,
 whichWindow );
 break;
 case inDrag:
 if ( ((WindowPeek)
 whichWindow)->windowKind ==
 ICONWINKIND ) {
 DragWindow ( whichWindow,
 TheEvent.where, &DragRect );
 }
 break;
 case inGoAway:
 if ( ((WindowPeek) 
 whichWindow)->windowKind == 
 ICONWINKIND ) {
 if ( TrackGoAway ( whichWindow, 
 TheEvent.where ) ) {
   CloseIconWindow(whichWindow);
   }
 }
 break;
 case inContent:
 if ( ((WindowPeek) 
 whichWindow)->windowKind == 
 ICONWINKIND ) {
 if (whichWindow!=FrontWindow())
 SelectWindow (whichWindow);
 else {
 SetPort ( whichWindow );
 TheMousePt = TheEvent.where;
 GlobalToLocal(&TheMousePt);
 anIconList = (CListObj *)
   GetWRefCon(whichWindow);
 anIconList->MouseInList (  TheMousePt, TheEvent.modifiers);
 }
 }
 break;
 }
 break;
 case autoKey:
 case keyDown:
 key = (char) 
 (TheEvent.message & charCodeMask);
 if ( TheEvent.modifiers & cmdKey )
 DoMenu ( MenuKey ( key ) );
 break;
 case nullEvent:
 SystemTask ( );
 break;
 }
 }
 }

/* |||||||||||||||||||| */
Initialize ( )
 {
 Handle h;
 Str255 volName;
 
 /*** init the mac rom stuff */
 InitGraf ( &thePort );
 InitFonts ();
 InitWindows ();
 InitMenus ();
 TEInit ();
 InitDialogs ( 0L );
 InitCursor ();
 FlushEvents ( everyEvent, 0 );
 
 MaxApplZone (); MoreMasters (); MoreMasters ();
 GetNextEvent ( everyEvent, &TheEvent );
 GetNextEvent ( everyEvent, &TheEvent );
 GetNextEvent ( everyEvent, &TheEvent );
 GetNextEvent ( everyEvent, &TheEvent );

 /*** set up a few globals ... */
 DoneFlag = false;
 DragRect = screenBits.bounds;

 /*** read in the menus */
 AppleMenu = GetMenu ( 1000 );
 InsertMenu ( AppleMenu, 0 );
 AddResMenu ( AppleMenu, 'DRVR' );
 FileMenu = GetMenu ( 1001 );
 InsertMenu ( FileMenu, 0 );
 EditMenu = GetMenu ( 1002 );
 InsertMenu ( EditMenu, 0 );
 DrawMenuBar ();
 }

/* |||||||||||||||||||| */
DoMenu ( long mResult )
 {
 int    theMenu, theItem, i;
 Str255 aname, freestr, maxstr;
 GrafPtrcurport;
 TEHandle te;
 MenuHandle new;
 long   sz, type;
 int    mark, *menuNum;
 WindowPtraWin;
 
 theItem = LoWord ( mResult );
 theMenu = HiWord ( mResult );
 switch ( theMenu ) {
 case 1000:
 if ( theItem == 1 ) DoAbout ( );
 else {
 GetItem ( AppleMenu, theItem, aname );
 GetPort ( &curport );
 OpenDeskAcc ( aname );
 SetPort ( curport );
 }
 break;
 case 1001:
 switch ( theItem ) {
 case 1:
 OpenIconWindow ( );
 break;
 case 2:
 aWin = FrontWindow ( );
 if ( aWin != 0L && 
 ((WindowPeek) aWin)->windowKind == 
 ICONWINKIND )
 CloseIconWindow ( aWin );
 break;
 case 4:
 DoneFlag = true;
 break;
 }
 break;
 case 1002:
 break;
 }

 HiliteMenu(0);
 }

/* |||||||||||||||||||| */
OpenIconWindow ( )
 {
 Point  where;
 SFReplytheFile;
 SFTypeList typs;
 WindowPtrtheWin;
 CListObj *theList;
 Rect   bounds;
 int    fileRef;
 
 /*** get the file */
 where.h = where.v = 42;
 SFGetFile (where,0L 0L,-1,typs,0L,&theFile);
 if ( theFile.good ) {
 
 /*** open the file */
 SetVol ( NULL, theFile.vRefNum );
 fileRef = OpenResFile (theFile.fName );
 if ( fileRef < 0 ) {
 StopAlert ( 1000, 0L );
 return;
 }

 /*** set up the window, start it's list
 object, fill it with icons */
 theWin = GetNewWindow ( 1000, 0L, -1L);
 ((WindowPeek) theWin)->windowKind = ICONWINKIND;
 SetWTitle ( theWin, theFile.fName );
 bounds = theWin->portRect;
 theList = new ( CListObj );
 theList->IList ( &bounds , 40, true, lOnlyOne, theWin );
 theList->DrawingOn ( false );

 /*** fill the list with ICONs */
 FillIconList ( fileRef, theList );
 theList->DrawingOn ( true );
 CloseResFile ( fileRef );
 SetWRefCon ( theWin, (long) theList );
 }
 }

/* |||||||||||||||||||| */
FillIconList ( int aFile, CListObj *aList )
 {
 int    i, iconID, numIcons;
 ResTypeiconType;
 Str255 iconName;
 CIconItemObj    *anIcon;
 Handle iconHan;
 
 /*** get each icon, create a new list item
 object, add it to the list */
 numIcons = Count1Resources ( 'ICON' );
 for ( i = 1; i <= numIcons; ++i ) {
 if ((iconHan = Get1IndResource ('ICON',i)) != 0L) {
 GetResInfo(iconHan, &iconID, &iconType, iconName);
 
 anIcon = new ( CIconItemObj );
 anIcon->id = iconID;
 BlockMove (iconName,anIcon->name,255L);
 DetachResource ( iconHan );
 anIcon->bits = iconHan;
 
 aList->Add ( anIcon );
 }
 }
 }

/* |||||||||||||||||||| */
CloseIconWindow ( WindowPtr theWin )
 {
 CListObj *theIconList;
 CIconItemObj    *anIcon;
 int    i, num;
 
 /*** throw away contents and close window */
 theIconList = (CListObj *)GetWRefCon(theWin);

 /*** throw away the CIconList objects, most efficient to go from the 
end of the list to the start */
 theIconList->DrawingOn ( false );
 num = theIconList->NumObjs();
 for ( i = num; i > 0; --i ) {
 anIcon = (CIconItemObj *) 
 theIconList->GetIndObject ( i );
 theIconList->Remove ( anIcon );
 DisposHandle ( anIcon->bits );
 delete ( anIcon );
 }
 theIconList->Dispose ( );
 DisposeWindow ( theWin );
 }

/* |||||||||||||||||||| */
DoAbout ( )
 {
 Alert ( 4242, 0L );
 }

/*+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-
 Icon List.Π.rsrc - all the resources for the
Icon List project.
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+- */

resource 'ALRT' (4242, "About") {
 {52, 38, 168, 304},
 4242,
 { /* array: 4 elements */
 /* [1] */
 OK, visible, sound1,
 /* [2] */
 OK, visible, sound1,
 /* [3] */
 OK, visible, sound1,
 /* [4] */
 OK, visible, sound1
 }
};

resource 'ALRT' (1000, "No Res Fork") {
 {52, 34, 140, 314},
 128,
 { /* array: 4 elements */
 /* [1] */
 OK, visible, sound1,
 /* [2] */
 OK, visible, sound1,
 /* [3] */
 OK, visible, sound1,
 /* [4] */
 OK, visible, sound1
 }
};

resource 'DITL' (4242) {
 { /* array DITLarray: 3 elements */
 /* [1] */
 {77, 174, 97, 234},
 Button {
 enabled,
 "OK"
 },
 /* [2] */
 {17, 19, 49, 51},
 Icon {
 disabled,
 1001
 },
 /* [3] */
 {25, 68, 41, 242},
 StaticText {
 disabled,
 "Icon List by Rob Hafernik"
 }
 }
};

resource 'DITL' (128) {
 { /* array DITLarray: 2 elements */
 /* [1] */
 {55, 206, 75, 266},
 Button {
 enabled,
 "OK"
 },
 /* [2] */
 {16, 64, 49, 236},
 StaticText {
 disabled,
 "Can't open resource fork of file!"
 }
 }
};

resource 'ICON' (1001, "Application", purgeable) {
 $"0001 0000 0002 8000 0004 4000 0008 2000"
 $"0011 9000 0023 0800 0047 8400 0084 C200"
 $"0130 6100 0220 0080 043E 0040 0882 0020"
 $"10CE 0010 2060 0008 4030 3F04 8810 4082"
 $"4C40 8041 26C1 3022 1381 C814 090E 7F8F"
 $"0402 3007 0201 0007 0100 8007 0080 6007"
 $"0040 1FE7 0020 021F 0010 0407 0008 0800"
 $"0004 1000 0002 2000 0001 4000 0000 80"
};

resource 'ICN#' (128) {
 { /* array: 2 elements */
 /* [1] */
 $"0001 0000 0002 8000 0004 4000 0008 2000"
 $"0011 9000 0023 0800 0047 8400 0084 C200"
 $"0130 6100 0220 0080 043E 0040 0882 0020"
 $"10CE 0010 2060 0008 4030 3F04 8810 4082"
 $"4C40 8041 26C1 3022 1381 C814 090E 7F8F"
 $"0402 3007 0201 0007 0100 8007 0080 6007"
 $"0040 1FE7 0020 021F 0010 0407 0008 0800"
 $"0004 1000 0002 2000 0001 4000 0000 80",
 /* [2] */
 $"0001 0000 0003 8000 0007 C000 000F E000"
 $"001F F000 003F F800 007F FC00 00FF FE00"
 $"01FF FF00 03FF FF80 07FF FFC0 0FFF FFE0"
 $"1FFF FFF0 3FFF FFF8 7FFF FFFC FFFF FFFE"
 $"7FFF FFFF 3FFF FFFE 1FFF FFFC 0FFF FFFF"
 $"07FF FFFF 03FF FFFF 01FF FFFF 00FF FFFF"
 $"007F FFFF 003F FE1F 001F FC07 000F F800"
 $"0007 F000 0003 E000 0001 C000 0000 80"
 }
};

resource 'BNDL' (128) {
 'IcLi',
 0,
 { /* array TypeArray: 2 elements */
 /* [1] */
 'FREF',
 { /* array IDArray: 1 elements */
 /* [1] */
 0, 128
 },
 /* [2] */
 'ICN#',
 { /* array IDArray: 1 elements */
 /* [1] */
 0, 128
 }
 }
};

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

resource 'MENU' (1000, "Apple") {
 1000,
 textMenuProc,
 0x7FFFFFF9,
 enabled,
 apple,
 { /* array: 2 elements */
 /* [1] */
 "About Icon List ", noIcon, noKey, noMark, plain,
 /* [2] */
 "-", noIcon, noKey, noMark, plain
 }
};

resource 'MENU' (1001, "File") {
 1001,
 textMenuProc,
 0x7FFFFFFB,
 enabled,
 "File",
 { /* array: 4 elements */
 /* [1] */
 "Open ", noIcon, "O", noMark, plain,
 /* [2] */
 "Close ", noIcon, "W", noMark, plain,
 /* [3] */
 "-", noIcon, noKey, noMark, plain,
 /* [4] */
 "Quit", noIcon, "Q", noMark, plain
 }
};

resource 'MENU' (1002, "Edit") {
 1002,
 textMenuProc,
 0x7FFFFFE0,
 enabled,
 "Edit",
 { /* array: 5 elements */
 /* [1] */
 "Undo", noIcon, "Z", noMark, plain,
 /* [2] */
 "-", noIcon, noKey, noMark, plain,
 /* [3] */
 "Cut", noIcon, "X", noMark, plain,
 /* [4] */
 "Copy", noIcon, "C", noMark, plain,
 /* [5] */
 "Paste", noIcon, "V", noMark, plain
 }
};

resource 'WIND' (1000) {
 {60, 30, 260, 330},
 noGrowDocProc,
 visible,
 goAway,
 0x0,
 "New Window"
};

resource 'vers' (1) {
 0x1,
 0x0,
 0x0,
 0x1,
 verUs,
 "Icon List 1.0",
 "Version 1.0 - July 1990"
};

resource 'vers' (2) {
 0x1,
 0x0,
 0x0,
 0x1,
 verUs,
 "Icon List 1.0",
 "by Rob Hafernik"
};

data 'IcLi' (0, "Owner resource") {
 $"00"
};

data 'LDEF' (42) {
 $"0000 0000 0000"
};

 

Community Search:
MacTech Search:

Software Updates via MacUpdate

Final Cut Pro 10.6.4 - Professional vide...
Redesigned from the ground up, Final Cut Pro combines revolutionary video editing with a powerful media organization and incredible performance to let you create at the speed of thought.... Read more
iMovie 10.3.4 - Edit personal videos and...
With a streamlined design and intuitive editing features, iMovie lets you create Hollywood-style trailers and beautiful movies like never before. Browse your video library, share favorite moments,... Read more
Motion 5.6.2 - Create and customize Fina...
Motion is designed for video editors, Motion 5 lets you customize Final Cut Pro titles, transitions, and effects. Or create your own dazzling animations in 2D or 3D space, with real-time feedback as... Read more
iMazing 2.15.8 - Complete iOS device man...
iMazing is the world’s favourite iOS device manager for Mac and PC. Millions of users every year leverage its powerful capabilities to make the most of their personal or business iPhone and iPad.... Read more
VueScan 9.7.90 - Scanner software with a...
VueScan is a scanning program that works with most high-quality flatbed and film scanners to produce scans that have excellent color fidelity and color balance. VueScan is easy to use, and has... Read more
Compressor 4.6.2 - Adds power and flexib...
Compressor adds power and flexibility to Final Cut Pro X export. Customize output settings, work faster with distributed encoding, and tap into a comprehensive set of delivery features. Features:... Read more
Capture One 15.3.2.11 - RAW workflow sof...
Capture One is a professional RAW converter offering you ultimate image quality with accurate colors and incredible detail from more than 400 high-end cameras - straight out of the box. It offers... Read more
Vivaldi 5.4.2753.28 - An advanced browse...
Vivaldi is a browser for our friends. We live in our browsers. Choose one that has the features you need, a style that fits and values you can stand by. From the look and feel, to how you interact... Read more
Parallels Desktop 18.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
TechTool Pro 16.0.1 - Hard drive and sys...
TechTool Pro has long been one of the foremost utilities for keeping your Mac running smoothly and efficiently. With the release of this version, it has become more proficient than ever. Main... Read more

Latest Forum Discussions

See All

Turn-Based RPG ‘Avatar: Generations’ Sof...
Square Enix London Mobile, Navigator Games, and Paramount Consumer Products just announced that the turn-based RPG Avatar: Generations based on Nickelodeon’s Avatar: The Last Airbender is soft launching this month for mobile. Avatar: Generations is... | Read more »
Tower of Fantasy launches today and brin...
Level Infinite and Hotta Studio have announced the release of their very ambitious looking shared open world MMORPG Tower of Fantasy. With its cross-platform functionality between PC and mobile, it looks to be one to roll the dice on and enjoy at... | Read more »
‘Genshin Impact’ Version 3.0 Gets a New...
After HoYoverse released Genshin Impact (Free) version 2.8 on all platforms, the company has slowly been teasing the major upcoming 3.0 update. This update features the Sumeru region with many characters. While details on the update including a... | Read more »
Out Now: ‘Tower of Fantasy’, ‘Tightrope...
Each and every day new mobile games are hitting the App Store, and so each week we put together a big old list of all the best new releases of the past seven days. Back in the day the App Store would showcase the same games for a week, and then... | Read more »
SwitchArcade Round-Up: ‘Book Quest’, ‘Cl...
Hello gentle readers, and welcome to the SwitchArcade Round-Up for August 10th, 2022. In today’s article, we’ve got a little news about an update to a game I really like, a few new releases to summarize, and some sales to look at. A bit of a quiet... | Read more »
‘Pine Tar Poker’ is an Otherworldly Poke...
Developer BJ Malicoat, who put out the well-received and former Apple Game of the Day pick Downwordly in June of last year, is back working on another mobile game project called Pine Tar Poker, and it has caught my attention. Why? Because it’s a... | Read more »
Darkness Rises celebrates four years of...
Four years of uptime for a mobile game is akin to eternity, and this is exactly the milestone that Darkness Rises has reached. It is important for developers to keep updating to keep the game fresh, and NEXON has announced a massive anniversary... | Read more »
Keep Your Smatphone’s Case On When Using...
The original Gamevice was born as a sort of offshoot of the weird Wikipad gaming tablet/controller/hybrid thing way back in 2014. Interestingly, the first Gamevice controller for iOS only supported the iPad mini and launched in 2015, with versions... | Read more »
SwitchArcade Round-Up: A ‘Splatoon 3’ Ni...
Hello gentle readers, and welcome to the SwitchArcade Round-Up for August 9th, 2022. In today’s article, we’ve got some news about a Splatoon 3 Nintendo Direct, a review of QUByte’s Thunderbolt Collection, a single new release summary, and the usual... | Read more »
Orangepixel’s Pacifist Survival Game ‘Re...
Back in June we learned that long-time mobile developer Orangepixel, who also makes games for PC and consoles (including the Atari VCS!), would be bringing the unique survival game Residual to mobile devices sometime this year. Originally launched... | Read more »

Price Scanner via MacPrices.net

Apple has 24-inch M1 iMacs available starting...
Apple has 24-inch M1 iMacs with M1 CPUs (8-core CPU/7-core GPU) available today in their Certified Refurbished store for $1099 shipped. Their price is $200 off standard MSRP. Each iMac is in like-new... Read more
13″ M1 MacBook Airs in stock today for $799,...
QuickShip Electronics has open-box return 13″ M1 MacBook Airs in stock and on sale for $200 off MSRP on their eBay store right now, each with free express delivery. According to QuickShip, “The item... Read more
In stock today: Mac Studio models for up to $...
Apple retailer Expercom has Mac Studio models in stock today and on sale for up to $400 off Apple’s MSRP, depending on configuration. Their prices are the lowest price available for a Mac Studio from... Read more
Mac mini with M1 CPU and 512GB of storage on...
Amazon has the M1 Mac mini with a 512GB SSD in stock today on sale for $749.99 including free shipping. Their price is $150 off Apple’s MSRP, and it’s the lowest price available for this... Read more
Need a Mac or iPad for school? Get a free App...
Apple’s Back to School promotion for 2022 continues to run through September 26, 2022. As part of this promotion, Apple will include a free $150 Apple Gift Card with the purchase of any MacBook Air,... Read more
Apple Watch SE on sale for $50 off MSRP
Amazon has Apple Watch SE GPS models on sale for $50 off MSRP for a limited time, each including free shipping. Their prices are the lowest currently available for SE Watches: – 40mm Apple Watch SE... Read more
Save $310 on a 14″ 24-core GPU M1 Max MacBook...
Save $310 on 14″ MacBook Pros with 24-core M1 Max processors at Apple (32GB RAM/1TB SSD) with these Certified Refurbished models in stock today for $2789 in Space Gray or Silver colors. Regular price... Read more
14″ M1 Pro MacBook Pros available today at Ap...
Apple has Certified Refurbished standard-configuration 14″ MacBook Pros with M1 Pro CPUs available today for up to $250 off original MSRP, starting at $1799. Each model features a new outer case,... Read more
13″ MacBook Air with M2 CPU, in Starlight, on...
Apple retailer Expercom has the new Starlight 13″ MacBook Air with an M2 CPU (8GB RAM/256GB SSD) on sale for $1135.05, shipped, through August 12, 2022. Their price is $64 off Apple’s MSRP, and it’s... Read more
14″ M1 Pro MacBook Pro with 1TB SSD on sale f...
Expercom is offering a $200 instant discount on the 14″ M1 Pro MacBook Pro with a 1TB SSD through August 12, 2022. Their discount reduces the price of this configuration to $1999 shipped — the lowest... Read more

Jobs Board

Solutions Engineering Manager - *Apple* - S...
…in our Hardware and Advanced Solutions group leading and developing our Apple technical practice to increase revenue and profitability. The ideal candidate would 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
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
Sephora Beauty Advisor - *Apple* Blossom Ma...
Sephora Beauty Advisor - Apple Blossom Mall Location:Winchester, VA, United States (https://jobs.jcp.com/jobs/location/191170/winchester-va-united-states) - Apple Read more
All contents are Copyright 1984-2011 by Xplain Corporation. All rights reserved. Theme designed by Icreon.