TweetFollow Us on Twitter

Ett Listigt
Volume Number:7
Issue Number:8
Column Tag:Programmer's Forum

Ett Listigt Program

By Martin Minow, Arlington, MA

Introduction

Perhaps I must begin with an apology for the title. It’s Swedish, and translates to “a clever program.” which is a bit of an overstatement for a minor hack but I couldn’t resist the opportunity to make a bilingual pun.

The program, in fact, isn’t clever at all. It merely serves to demonstrate how you can take control of the way the List Manager draws your data.

The List Manager, described in Inside Mac, volume IV, is a very simple table construction and selection function that lets your program manage a small collection of data. I use it in a number of applications: for example, to store the phone numbers for an autodialer and the names of elite runners in a road race display program.

While the List Manager is simple to use, it has a few limitations. For example, it is pretty sloppy about how it handles fonts, so if you put a list in a dialog, you run the risk of displaying parts of the dialog in the list font, rather than the dialog font.

Fortunately, the good folk who designed the List Manager gave programmers a way to add our own control function. They assume we’ll write the function as a code resource that is stored in the application’s resource fork. While this works well for most applications, this article shows two tricks that should make the list definition function easier to write and debug (no separate code resource) and easier to integrate into an application. The debugging advantages are significant: it is impossible to use the Think C symbolic debugger in a separately-compiled code resource.

The List Handler

Ignoring for a moment how we actually get there, the LDEF resource performs four services for the List Manager:

1. Initialize any private information when the list is first created. The demo handler defines some information based on the current font.

2. Draw a list cell. If your application doesn’t specify a drawing routine, the demo handler draws the cell using the font (and size) that were specified when the list was created. The window’s original font and size are restored, so the list can be used in, e.g., a dialog without messing up the font/size for other dialog items.

3. Hilite a list cell. The demo handler uses the new method of color highlighting described in Inside Mac, vol 5, pp 61-62.

4. Close a list -- this call lets a LDEF procedure release any private information. The demo handler ignores this call since it doesn’t store any private information.

The bulk of the work is done by the draw routine: the pen is moved to the proper place within the cell, the list data storage handle is locked in memory, the display area is erased, the font and size are set, and the data drawn (either by the handler or by the application’s callback function). Finally, the list data handle lock state is restored. If the cell was selected, the normal highlight procedure is then executed.

With that as background, the only remaining problem is connecting the list handler to your application. The sample program demonstrates three possible methods: building a separate code resource, building a fake code resource, and directly installing the list handler in the ListRecord without a separate resource.

The Code Resource

The approved way to write a list handler is to build it as a separate code resource that is stored as a LDEF resource in the application’s resource fork. Then, you specify the number of the LDEF resource when you execute LNew. If you’ve never done this before, the process is strange, but it’s fairly simple if you take it step-by-step. Using Think C (version 4), do the following:

1. Create a new project, say LDEF.Π.

2. Add the LDEF Resource.c and MacHeaders files.

3. Set the project type to Code Resource, set the resource name to LDEF and the resource id to, say, 128.

4. Build the code resource, saving it as LDEF.rsrc

Now, build your application. Think C (unfortunately) only supports RMaker, so you have to add the following line to your RMaker source file:

 Include LDEF.rsrc

RMaker will merge the code resource into your application’s resource fork.

Fake LDEF

Instead of building a separate LDEF resource, you can also compile the entire LDEF into your program. However, in order to satisfy the Macintosh Toolbox standards, you still need a resource. You can create one on the fly by building a callback LDEF. You do this by creating a dummy LDEF in your resource file that is defined as follows:

/* 1 */

 Type LDEF = GNRL
 , 128
 .h
 4EF9 0000 0000

Then, when your program starts, it calls the SetupLDEF function that “plugs” the address of the actual function into the LDEF and marks the LDEF as non-purgeable. As with the “normal” procedure, LNew specifies the resource id (128 in the example), and the toolbox ListManager manages the resource normally. Hex 4EF9 is a jump instruction, so the ListManager jumps to the real function. Your application only calls SetupLDEF once, even if the list handler will be used for several lists.

No Resource Needed

You don’t even need the degenerate 3 word version above. Instead, you can stuff the function address into the ListRecord after creating the list. While this technique seems to work without problems, it isn’t blessed by the Mac folk, so don’t complain when it stops working in some future release. The add_LDEF() function creates the fake handle -- call it just after you create the list. Be sure to call remove_LDEF() before deleting the list: otherwise, your program will surely crash!

Which should you use?

Each of the three methods has its own advantages and disadvantages:

• The fully-compiled method is perhaps only useful if you absolutely cannot have a visible LDEF resource: perhaps for security or anti-viral reasons. Before using it, you should remember that it isn’t “blessed” by the Macintosh interface people.

• The “fake-resource” callback method is probably the most useful for development: you can set breakpoints within the list handler. (By the way, the “fake-resource” technique is useful for other user-written handlers that the Toolbox assumes will be stored as resources.) It does require, however, that you make sure that the LDEF be kept memory-resident -- otherwise, you’ll have messy, hard to debug crashes.

• The “true-resource” method is best if you’re worried about memory space, since the Toolbox takes care of loading and unloading the resource. It has the significant disadvantage of being essentially impossible to debug without an assembly-language debugger.

If you’re not sure, I’d recommend using the “fake-resource” technique, making sure that the list handler is stored in a permanently-resident code segment.

The Sample Program

The sample program is not intended to show you how to write a Macintosh application. All it does is let you open a file and display any ICN# resources as a list. It has an interesting bug: to show that the list display function (in IconicLDEF.c) can access the application’s globals, the code references a flag that allows displaying the cell’s contents either as an icon or as the icon’s value. If you switch between the two modes with several partially-obscured windows on the screen, you’ll note that the Macintosh efficiently -- but incorrectly --calculates what needs to be redrawn, and some icons will be displayed partially in one form and partially in the other. This is an easy bug to fix, but I left it in to warn you about the problems of using application-wide globals where a document-specific (or window-specific) variable would be better.

When reading the source of the application and list handler, note that they are conditionally-compiled to operate in all three modes. The projects are, of course, different:

• CompiledDemo.Π

CompiledDemo.c

CompiledLDEF.c

IconicLDEF.c

MacTraps

no resource file.

• FakeResource.Π

FakeDemo.c

FakeLDEF.c

IconicLDEF.c

MacTraps

FakeDemo.rsrc built from FakeDemo.r

• RealResource.Π

RealDemo.c

Iconic LDEF.c

MacTraps

RealResource.rsrc, built from RealLDEF.Π

RealLDEF.Π

RealLDEF.c

MacTraps

If you just type in the listing, you’ll get the “fake-resource” variant.

Acknowledgements

The ListManager is described in Inside Macintosh, Volume IV. Thanks to Amanda Walker, InterCon Corp. for posting the technique of calling an application-specific drawing function from the list handler to Usenet.

Listing:  iconicLDEF.h

/* iconicLDEF.h  */
/*
 * Header file for the LDEF demo: the IconInfo structure
 * defines the contents of a list cell.  Also, define
 * the application global values.
 *
 * Copyright 1990 Martin Minow and MacTutor.
 */
#define NIL ((void *) 0)

typedef struct {
 Handle handle;
 unsigned short  number;
} IconInfo;

/*
 * This is just to demonstrate that we can access an
 * application global value from the LDEF handler.
 */
extern Boolean drawAsText;
!
codeexampleend!
codeexamplestart
Listing:  FakeDemo.c

/* ListDemo.c  */
/*
 *
 * Demo my homemade LDEF.  The program lets you open
 * a file and display all of the icons in it.  You
 * can’t do anything else, though it would be easy
 * to allow you to copy an icon to the clipboard.
 *
 * Copyright 1990 Martin Minow and MacTutor.
 */
#include “iconicLDEF.h”

#define FORMAT FAKE_RESOURCE/* Define  program */
/*
 * These values configure the demo program: that way
 * we only need to write one piece of code.
 */
#define COMPILED 0
#define FAKE_RESOURCE1
#define TRUE_RESOURCE2
#if FORMAT == FAKE_RESOURCE || FORMAT == TRUE_RESOURCE
#define LDEF_ID  128
#else
#define LDEF_ID  0 /* Start with Mac LDEF */
#endif
#define RESOURCE ‘ICN#’
#define iconSize 32/* Pixels per icon */
#define sBarWidth15/* For grow button */
#define iconCols  4
#define iconRows  4
#define hOffsetDelta 16   /* For multiple windows */
#define vOffsetDelta 0
#define hOffsetMax (hOffsetDelta * 10)
#define vOffsetMax (vOffsetDelta * 10)

/*
 * Menu organization
 */
enum Menus {
 MENU_Apple = 1,
 MENU_File= 256,
 MENU_Edit= 257
};

enum Apple_Menu {
 Apple_About = 1
};

enum File_Menu {
 File_Open= 1,
 File_Close,
 File_DrawAsText,
 File_Debug,
 Unused,
  File_Quit
};

#define width(r) ((r).right - (r).left)
#define height(r)((r).bottom - (r).top)

/*
 * All the information about a document
 * is stored here.  Note that, when the
 * toolbox selects a window, we can
 * immediately recover the document.
 */
typedef struct {
 WindowRecord  window;
 ListHandle list;
} DocumentRecord, *DocumentPtr;
/*
 * The current window is always stored in a local
 * WindowPtr variable named “window.”  If it’s ours,
 * we can access the document by casting window to
 * DocumentPtr and de-referencing it.
 */
#define DOC (*((DocumentPtr) window))

/*
 * isOurWindow is TRUE if its argument is our document.
 */
#define isOurWindow(window) (  \
 (window) != NIL  \
 && ((WindowPeek) (window))->windowKind == userKind \
  )

MenuHandleapple_menu;
MenuHandlefile_menu;
Boolean drawAsText;/* For iconicLDEF()  */
inthOffset, vOffset;

void    main(void);
Boolean do_mouse(EventRecord);
Boolean handle_events(void);
void    do_command(long);
void    setup(void);
void    open_document(void);
void    close_document(WindowPtr);
Boolean new_document(void *, int);
Boolean initialize_list(WindowPtr);
void    read_icons(WindowPtr, int);

#if FORMAT == FAKE_RESOURCE
void    setup_LDEF(int);
#endif
#if FORMAT == COMPILED
void    add_LDEF(ListHandle);
void    remove_LDEF(ListHandle);
#endif
void    iconicLDEF();

/*
 * main()
 * Initialize the program and run the event loop.
 */
void
main()
{
 EventRecordevent;
 WindowPtrwindow;
 GrafPtrsave_port;
 Rect   box;

 setup();
 for (;;) {
 SystemTask();
 while (GetNextEvent(everyEvent, &event)
  && event.what != nullEvent) {
 if (event.what == activateEvt
  || event.what == updateEvt)
   window = (WindowPtr) event.message;
 else {
 window = FrontWindow();
 }
 switch (event.what) {
 case mouseDown:
 do_mouse(event);
 break;
 case activateEvt:
 if (isOurWindow(window)) {
 SetPort(window);/* Or InvalRect crashes! */
 LActivate(
   event.modifiers & activeFlag,
   DOC.list
 );
 /*
  * For some reason, LActivate doesn’t redraw
  * the scroll bars.  The correct solution
  * is left as a puzzle for the reader.
  */
 if (event.modifiers & activeFlag) {
 ControlHandle handle;
 Rect   box;
 
 handle = (**DOC.list).vScroll;
 if (handle != NIL) {
 box = (**handle).contrlRect;
 InvalRect(&box);
 }
 }
 
 } 
 break;
 case updateEvt:
 if (isOurWindow(window)) {
 GetPort(&save_port);
 SetPort(window);
 BeginUpdate(window);
 EraseRect(&window->portRect);
 box = (**DOC.list).rView;
 InsetRect(&box, -1, -1);
 FrameRect(&box);
 LDoDraw(TRUE, DOC.list);
 LUpdate(window->visRgn, DOC.list);
 EndUpdate(window);
 SetPort(save_port);
 }
 break;
 default:
 break;
 }
 }
 }
}

/*
 * do_mouse(event)
 * Process a mouse button press, calling handlers as
 * needed.
 */
static Boolean
do_mouse(event)
EventRecord event;
{
 WindowPtrwindow;
 register int  which_part;
 Rect   box;
 int    item;
 Point  mouse;
 int    result;
 long   new, choice;
 Str255 name;
 GrafPtrsave_port;
 
 which_part = FindWindow(event.where, &window);
 switch (which_part) {
 case inDesk:
 SysBeep(2);
 break;
 case inMenuBar:
 choice = MenuSelect(event.where);
 item = LoWord(choice);
 switch (HiWord(choice)) {
 case MENU_Apple:
 GetItem(apple_menu, item, &name);
 if (item == Apple_About)
 SysBeep(10);  /* No List About */
 else {
 GetPort(&save_port);
 OpenDeskAcc(name);
 SetPort(save_port);
 }
 break;
 case MENU_File:
 window = FrontWindow();
 switch (item) {
 case File_Open:
 open_document();
 break;
 case File_Close:
 close_document(window);
 break;
 case File_Debug:
 Debugger();
 break;
 case File_DrawAsText:
 drawAsText = !drawAsText;
 CheckItem(file_menu, File_DrawAsText, drawAsText);
 if (isOurWindow(window))
 InvalRect(&(*window).portRect);
 break;
 case File_Quit:
 ExitToShell();
 }
 default:
 break;
 }
 HiliteMenu(0);
 break;
 case inDrag:
 box = screenBits.bounds;
 box.top += GetMBarHeight();
 InsetRect(&box, 4, 4);
 DragWindow(window, event.where, &box);
 break;
 case inContent:
 if (FrontWindow() != window)
 SelectWindow(window);
 else {
 SetPort(window);
 if (isOurWindow(window)) {
 mouse = event.where;
 GlobalToLocal(&mouse);
 result = LClick(mouse, event.modifiers, DOC.list);
 (void) LLastClick(DOC.list);
 }
 }
 break;
 case inGoAway:
 if (isOurWindow(window)
  && TrackGoAway(window, event.where))
 close_document(window);
 break;
 }
 return (FALSE);
}

/*
 * open_document()
 * Ask for a file (allow all files).  Open its resource
 * fork (if possible).  If we succeed, create a new
 * window and look for all ICN# resources.
 */ 
void
open_document()
{
 SFReplyreply;
 int    file;
 static Point    where = { 85, 85 };

 SFGetFile(
 where, /* Where on the screen*/
 NIL,   /* Unused*/
 NIL,   /* no file filter */
 -1,    /* Allow all file types    */
 NIL,   /*   thus no typeList */
 NIL,   /* no dialog hook */
 &reply /* reply goes here*/
 );
 if (reply.good) {
 SetVol(NIL, reply.vRefNum);
 SetCursor(*GetCursor(watchCursor));
 if ((file = OpenResFile(reply.fName)) == -1)
 SysBeep(10);  /* No resource fork  */
 else {
 if (new_document(reply.fName, file) == FALSE)
 SysBeep(10);  /* No memory  */
 CloseResFile(file);
 }
 SetCursor(&arrow);
 }
}

/*
 * close_document()
 * Close the document in the current (front) window.
 * Dump the handles that were stored in the list’s cells.
 */
void
close_document(window)
WindowPtr window;
{
 IconInfo iconInfo;
 short  size;
 Cell   cell;

 if (isOurWindow(window) == FALSE)
 Debugger();
 SetCursor(*GetCursor(watchCursor));
 /*
  * Iterate over all cells in the list.
  */
 cell.h = cell.v = 0;
 do {
 size = sizeof iconInfo;
 LGetCell(&iconInfo, &size, cell, DOC.list);
 if (size == sizeof iconInfo) /* Non-empty cell? */
 DisposHandle(iconInfo.handle);
 } while (LNextCell(TRUE, TRUE, &cell, DOC.list));
#if FORMAT == COMPILED
 remove_LDEF(DOC.list);
#endif
 LDispose(DOC.list);
 CloseWindow(window);
 DisposPtr(window);
 SetCursor(&arrow);
}

/*
 * setup()
 * One-time initialization.
 */
void
setup()
{
 InitGraf(&thePort);
 InitFonts();
 FlushEvents(everyEvent, 0);
 InitWindows();
 InitMenus();
 TEInit();
 InitDialogs(NIL);
 InitCursor();
 MaxApplZone();
 apple_menu = NewMenu(MENU_Apple, “\p\024”);
 file_menu = NewMenu(MENU_File, “\pFile”);
 AppendMenu(apple_menu, “\p(No List Demo About;(-”);
 AddResMenu(apple_menu, ‘DRVR’);
 AppendMenu(file_menu,
 “\pOpen\311/O;Close;Draw As Text;Debug/.;(-;Quit/Q”);
 InsertMenu(apple_menu, 0);
 InsertMenu(file_menu, 0);
 DrawMenuBar();
#if FORMAT == FAKE_RESOURCE
 setup_LDEF(LDEF_ID);
#endif
 if (new_document(“\pList Demo”, -1) == FALSE) {
 SysBeep(10);
 ExitToShell();
 }
}

/*
 * new_document()
 * Build a document: get memory for the WindowRecord and
 * our attached information.  Offset the window with
 * respect to other windows and make the window.  If
 * this succeeds, initialize the document’s list.  If
 * that succeeds, read the icons.
 */
Boolean
new_document(title, resFile)
void    *title;  /* Pascal string */
intresFile; /* Open resource file, if any */
{
 WindowPtrwindow;
 DocumentPtrdoc;
 Rect   box;
 static longsequence;/* Identify windows */
 
 doc = (DocumentPtr) NewPtr(sizeof (DocumentRecord));
 if (doc == NIL)
 return (FALSE);
 /*
  * Locate the window and get its shape.  This could
  * probably be done better.
  */
 box.left = 0;
 box.top = GetMBarHeight() * 2;
 box.right = box.left
 + (iconSize * iconCols)
 + sBarWidth
 + 2;
 box.bottom = box.top
 + (iconSize * iconRows)
 + 2;
 OffsetRect(&box, hOffset, vOffset); 
 if (box.bottom > screenBits.bounds.bottom
  || box.right > screenBits.bounds.right) {
   OffsetRect(&box, -hOffset, -vOffset);
   hOffset = 0;
   vOffset = 0;
 }
 hOffset += hOffsetDelta;
 if (hOffset >= hOffsetMax)
 hOffset = 0;
 vOffset += vOffsetDelta;
 if (vOffset >= vOffsetMax)
 vOffset = 0;
 window = NewWindow(
 doc,   /* Allocated storage */
 &box,  /* Display Rect    */
 title, /* Title  */
 TRUE,  /* Visible on creation */
 noGrowDocProc,  /* Window type     */
 -1L,   /* Show in front   */
 TRUE,  /* GoAway box */
 ++sequence /* RefCon(debug only) */
 );
 if (window == NIL) {
 DisposPtr(doc);
 return (FALSE);
 }
 if (initialize_list(window) == FALSE) {
 CloseWindow(window);
 DisposPtr(doc);
 return (FALSE);
 }
 SetPort(window);
 TextFont(applFont);
 TextSize(9);
 read_icons(window, resFile);
 return (TRUE);
}

/*
 * initialize_list()
 * Build a list whose cells will hold our icon records.
 * If our LDEF is completely compiled, call add_LDEF
 * after creation.
 */
Boolean
initialize_list(window)
WindowPtr window;
{
 int    type;
 Handle handle;
 int    *jump;
 Rect   box;
 Point  cell;
 Rect   dimension;
 
 box = (*window).portRect;
 InsetRect(&box, 1, 1);  /* Room for frame */
 box.right -= sBarWidth;
 SetPt(&cell, iconSize, iconSize);
 box.bottom =
 box.top + (height(box) / cell.v) * cell.v;
 box.right =
 box.left + (width(box) / cell.h) * cell.h;
 SetRect(&dimension, 0, 0, box.right / cell.h, 0);
 DOC.list = LNew(
 &box,  /* Display rectangle*/
 &dimension,/* Shape (rows, cols)  */
 cell,  /* Cell shape on screen  */
 LDEF_ID, /* List handler, if any  */
 window,/* The window*/
 TRUE,  /* Drawing’s ok   */
 FALSE, /* No grow box    */
 FALSE, /* No horizontal scroll  */
 TRUE   /* Vertical scroll*/
 );
 if (DOC.list == NIL)
 return (FALSE);
 (**DOC.list).selFlags = lOnlyOne;
#if FORMAT == COMPILED
 add_LDEF(DOC.list);
#endif
 (**DOC.list).refCon = (long) iconicLDEF;
 return (TRUE);
}

/*
 * read_icons()
 * Fill the list with the icon handles and resource ids.
 */
void
read_icons(window, resFile)
WindowPtr window;
intresFile;
{
 int    n_icons;
 int    i;
 int    n_rows, n_cols;
 Cell   cell;
 ResTypetype;
 IconInfo iconInfo;
 Str255 name;
 
 LDoDraw(FALSE, DOC.list);
 if (resFile != -1) {
 UseResFile(resFile);
 n_icons = Count1Resources(RESOURCE);
 }
 else {
 n_icons = CountResources(RESOURCE);
 }
 n_cols = (**DOC.list).dataBounds.right;
 n_rows = (n_icons + (n_cols - 1)) / n_cols;
 /*
  * Note that we can have empty cells in the last row.
  * The drawing routine must check for this case.
  */
 if (n_rows > 0)
 LAddRow(n_rows, 0, DOC.list);
 for (i = 0; i < n_icons; i++) {
 if (resFile != -1)
 iconInfo.handle = Get1IndResource(RESOURCE, i + 1);
 else {
 iconInfo.handle = GetIndResource(RESOURCE, i + 1);
 }
 if (iconInfo.handle == NIL)
 iconInfo.number = -1;
 else {
 GetResInfo(
 iconInfo.handle, &iconInfo.number, &type, name);        
 DetachResource(iconInfo.handle);
 }
 cell.v = i / n_cols;
 cell.h = i % n_cols;
 LSetCell(&iconInfo, sizeof iconInfo, cell, DOC.list);
#if 0   /* Debug: watch the icons as they’re read in           */
 {
 Rect   rect;
 SetRect(&rect, 1, 1, 33, 33);
 PlotIcon(&rect, iconInfo.handle);
 }
#endif
 }
 LDoDraw(TRUE, DOC.list);
 InvalRect(&(**DOC.list).rView);
}
Listing:  FakeLDEF.c

/* LDEF Resource.c */
/*
 * Copyright © 1989, 1990 Martin Minow and MacTutor.
 *
 * You may use this software in any application and
 * redistribute this source without restriction as long
 * as this copyright and permission notice remains intact
 * and the source is not redistributed for profit and you
 * assume all responsibility for the proper operation of
 * this software.
 *
 * Written in Think C.  Set Tabs every 2 characters.
 */
#define FORMAT FAKE_RESOURCE
#ifdef DOCUMENTATION

Usage

 void   /* COMPILED only  */
 add_LDEF(list)  /* ...   */
 ListHandle list;/* ...   */
 
 void   /* COMPILED only  */
 remove_LDEF(list) /* ... */
 ListHandle list;/* ...   */
 
 void   /* FAKE_RESOURCE only */
 setup_LDEF(id)  /* ...   */
 int    id; /* ... */
 
User-provided function:

 void
 my_draw_function(
 Rect *lRrect,   /* Drawing rectangle*/
 Cell  lCcell,   /* Cell to redraw */
 short lDataOffset, /* Offset to cell to redraw */
 short lDataLen,  /* Length of cell’s data */
 ListHandle lHandle/* The list handle */
 )

 Note: this is called using C calling conventions --
 mostly because I couldn’t convince Think C to compile
 it as a pascal function.
 
Description

 Initialize your list as follows:
 
 setup_LDEF(resource_id); /* FAKE_RESOURCE only */
 resource_id = 0;/* COMPILED only */
 resource_id = <id>; /* FAKE_ or TRUE_RESOURCE */
 TextFont( ... );/* Font of your choice */
 TextSize( ... );/* Whatever you choose */
 myList = LNew(
 &box,  /* Display dimensions */
 &dimension,/* Shape (rows, cols) */
 cell,  /* Cell shape on screen */
 resource_id,    /* List func, if any */
 dialog,/* The window */
 FALSE, /* Don’t draw yet  */
 FALSE, /* No grow box     */
 FALSE, /* no horizontal scroll */
 TRUE   /* Vertical scroll */
 );
 add_LDEF(myList); /* COMPILED only */
 (**myList).refCon = my_draw_function;

 Now, whenever the list manager needs to draw something,
 the LDEF handler function will be called.  If the list
 refCon is zero, the LDEF handler operates identically
 to the standard, except that it preserves font and font
 size information.  This is all you need if you wish to
 draw a text list in a dialog using other than the system
 font.
 
 If the refCon is set to a user function, the function
 will be called after the following initialization has
 been completed:
 
 • The pen is “normal” and positioned properly to draw
   the text.
 • The cell to be drawn is locked.
 • The drawing rectangle is erased.
 • The font and font size are set to values in the list’s
   port.
  
 The simplest user function need only draw the cell:
 
 DrawText(*((**lHandle).cells), lDataOffset, lDataLen);
 
 A more complicated user function may get the cell’s
 contents (using LGetCell) and perform some function
 based on that information.
 
 If you have compiled the LDEF function, be sure to call
 remove_LDEF() before exiting: otherwise, your program
 will crash:
 
 remove_LDEF(myList);/* COMPILED only */
 LDispose(myList);
 
Author

 Martin Minow
 
 Thanks to Amanda Walker, InterCon Corporation,
 for the call-back idea.

#endif

/*
 * These values configure the demo program: that way
 * we only need to write one piece of code.
 */
#define COMPILED 0
#define FAKE_RESOURCE1
#define TRUE_RESOURCE2
#include <Color.h>
#define NIL 0
typedef short  word;
/*
 * The current ListHandle is always in local variable list.
 */
#define LIST(**list)

/*
 * This structure defines the fake LDEF resource.
 */
typedef struct {
 word instruction;
 void (*function)();
} LDEF_record, *LDEF_pointer, **LDEF_handle;

/*
 * Externally visible functions.
 */
#if FORMAT == FAKE_RESOURCE
void    setup_LDEF(int);
#endif
#if FORMAT == COMPILED
void    add_LDEF(ListHandle);
void    remove_LDEF(ListHandle);
#endif
#if FORMAT == TRUE_RESOURCE
pascal void main(
 int, Boolean, Rect *, Cell, int, int, ListHandle);
#else
static pascal void myListDef(
 int, Boolean, Rect *, Cell, int, int, ListHandle);
#endif

/*
 * Define the type of the callback function. Amanda Walker
 * calls this “the cast from hell”, and with good reason.
 */
typedef void (* FUNC)(Rect *, Cell, word, word, ListHandle);

#if FORMAT == COMPILED
/*
 * Call this after calling LNew for any list that is to
 * use this list definition routine.  It creates a handle
 * and fills it with enough code to jump to myListDef.
 * Note that we do not use a LDEF resource.
 */
void
add_LDEF(list)
ListHandlelist;
{
 LDEF_handleldef;
 
 ldef = (LDEF_handle) NewHandle(sizeof (LDEF_record));
 if (ldef != NIL) {
 (**ldef).instruction = 0x4EF9;  /* JMP instruction */
 (**ldef).function = myListDef;
 LIST.listDefProc = (Handle) ldef;
 }
}

/*
 * Be sure to call this before deleting the list.
 * Otherwise, you’ll have random program crashes.
 */
void
remove_LDEF(list)
ListHandlelist;
{
 LIST.listDefProc = NIL;
}
#endif

#if FORMAT == FAKE_RESOURCE
/*
 * Call this once when your application starts if it uses
 * the list definition function.  You only need to call it
 * once, even if several lists use this function.
 */
void
setup_LDEF(id)
intid;
{
 LDEF_handleldef;
 
 ldef = (LDEF_handle) Get1Resource(‘LDEF’, id);
 if (ldef != NIL
  && GetHandleSize(ldef) == sizeof (LDEF_record)) {
 (**ldef).instruction = 0x4EF9;
 (**ldef).function = myListDef;
 HNoPurge(ldef);
 }
}
#endif

/*
 * myListDef() is called by the Macintosh list manager.  It
 * is identical to the standard function, except that it
 * preserves font and size and may call a user function
 * for strange data formatting.
 */
#if FORMAT == TRUE_RESOURCE
pascal void main
#else
static pascal void myListDef
#endif
 (message, select, rect, cell, offset, length, list)
intmessage; /* List manager action  */
Boolean select;  /* TRUE if cell is to be selected */
Rect    *rect;   /* Rectangle to draw cell in */
Cell    cell;    /* The selected cell */
intoffset;/* Start of data in the list */
intlength;/* Number of bytes to draw */
ListHandlelist;  /* The list itself.  */
{
 int    old_font;
 int    old_size;
 FontInfo info;
 Point  where;
 int    lock_state;
 long   saved_A5;
 GrafPtrold_port;
 
 /*
  * This is needed so the drawing code can find the
  * application globals.
  */
 saved_A5 = SetCurrentA5();
 /*
  * If we’re compiled, we don’t have valid QuickDraw
  * globals (such as thePort), so we use the current
  * port information from the ListRecord.
  */
 GetPort(&old_port);
 SetPort(LIST.port);
 old_font = LIST.port->txFont;/* Save the old */
 old_size = LIST.port->txSize;/* font and size */
 /*
  * Editorial note: ‘--’ is the C decrement operator,
  * not the typeographer’s “long dash”.
  */
 if (message-- == 0) {    /* lInitMsg */
 /*
  * Initialization: save indentation.  Note: do not
  * touch select, rect, cell, offset, or length.
  */
 GetFontInfo(&info);
   LIST.indent.v = info.ascent;
   LIST.indent.h = 4;/* Arbitrary */
 }
 else if (message-- == 0) { /* lDrawMsg */
 where = topLeft(*rect);
 AddPt(LIST.indent, &where);
 MoveTo(where.h, where.v);
 PenNormal();
 lock_state = HGetState(LIST.cells);
 HLock(LIST.cells);
 EraseRect(rect);
 TextFont((LIST.port)->txFont);
 TextSize((LIST.port)->txSize);
 /*
  * We’re ready to roll.  If there’s a user-provided
  * function, call it.  Otherwise, just draw text.
  */
 if (LIST.refCon == NIL)
 DrawText(*(LIST.cells), offset, length);
 else {
 (*(FUNC) (LIST.refCon))
 (rect, cell, offset, length, list);
 } 
 HSetState(LIST.cells, lock_state);
 if (select)/* If selected */
 goto hilite;    /* go hilite it    */
 }
 else if (message-- == 0) { /* lHiliteMsg */
hilite:
 HiliteMode &= ~(1 << hiliteBit);  /* IM V-62 */
   InvertRect(rect);
 }
 TextFont(old_font);
 TextSize(old_size);
 SetA5(saved_A5);
}
Listing:  iconicLDEF.c

/* Iconic LDEF.c */
/*
 * All of the list-drawing nitty-gritty is here.
 * Each list cell contains an icon and the icon id.
 * Either draw the icon in the cell rectangle or convert
 * the icon id to hex and draw that string.
 *
 * Copyright © 1989, 1990 Martin Minow and MacTutor.
 */
#include “IconicLDEF.h”

void iconicLDEF(Rect *, Cell, short, short, ListHandle);
static void DrawHex(unsigned short, short);

/*
 * iconicLDEF() is called by the LDEF handler to draw
 * an icon whose resource ID is stored in the cell.
 */ 
void
iconicLDEF(rect, cell, offset, length, list)
Rect    *rect;   /* Rectangle to draw cell in */
Cell    cell;    /* The selected cell */
short   offset;  /* Start of data in the list */
short   length;  /* Number of bytes to draw */
ListHandlelist;  /* The list itself.  */
{
 IconInfo iconInfo;

 /*
  * Note that we ignore zero-length (empty) cells.
  */
 if (length == sizeof iconInfo) {
 LGetCell(&iconInfo, &length, cell, list);
 /*
  * Show we can access the global parameter.  Of
  * course, a “real” application would use a
  * document-specific data structure, probably
  * accessed via the document structure or window
  * refCon.  Using this global is actually incorrect
  * as partial window updates will leave a list cell
  * half in one format and half in the other.
  */
 if (drawAsText)
 DrawHex(iconInfo.number, sizeof (short) * 2);
 else {
 if (iconInfo.handle != NIL) {
 PlotIcon(rect, iconInfo.handle);
 }
 }
 }
}

/*
 * Recursive routine to draw a value in hex.
 * Each call of DrawHex outputs one nibble.
 */
static void
DrawHex(hex, size)
unsigned short   hex;
short   size;
{
 if (--size > 0)
 DrawHex(hex >> 4, size);
 hex &= 0x0f;
 DrawChar((hex >= 10) ? hex - 10 + ‘a’ : hex + ‘0’);
}
Listing:  FakeDemo.r

FakeResourceDemo.Π.rsrc
APPL????;; APPL, followed by your “signature”
Type LDEF = GNRL
, 128   ;; LDEF_Resource
.h
4EF9 0000 0000

 

Community Search:
MacTech Search:

Software Updates via MacUpdate

MacFamilyTree 8.2.7 - Create and explore...
MacFamilyTree gives genealogy a facelift: modern, interactive, convenient and fast. Explore your family tree and your family history in a way generations of chroniclers before you would have loved.... Read more
WhatsApp 0.2.8000 - Desktop client for W...
WhatsApp is the desktop client for WhatsApp Messenger, a cross-platform mobile messaging app which allows you to exchange messages without having to pay for SMS. WhatsApp Messenger is available for... Read more
TotalFinder 1.10.7 - Adds tabs, hotkeys,...
TotalFinder is a universally acclaimed navigational companion for your Mac. Enhance your Mac's Finder with features so smart and convenient, you won't believe you ever lived without them. Features... Read more
Box Sync 4.0.7886 - Online synchronizati...
Box Sync gives you a hard-drive in the Cloud for online storage. Note: You must first sign up to use Box. What if the files you need are on your laptop -- but you're on the road with your iPhone? No... Read more
Espresso 5.1 - Powerful HTML, XML, CSS,...
Note from the developer: For the new Espresso, we changed our versioning and licensing approach with more consistent pricing and a simpler development timeline: "X+1". Each new update would increase... Read more
VueScan 9.6.04 - 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
Slack 3.0.5 - Collaborative communicatio...
Slack is a collaborative communication app that simplifies real-time messaging, archiving, and search for modern working teams. Version 3.0.5: Bug Fixes: An important security update. Security... Read more
VirtualBox 5.2.6 - x86 virtualization so...
VirtualBox is a family of powerful x86 virtualization products for enterprise as well as home use. Not only is VirtualBox an extremely feature rich, high performance product for enterprise customers... Read more
Vivaldi 1.13.1008.40 - An advanced brows...
Vivaldi is a browser for our friends. In 1994, two programmers started working on a web browser. Our idea was to make a really fast browser, capable of running on limited hardware, keeping in mind... Read more
WhatRoute 2.1.1 - Geographically trace o...
WhatRoute is designed to find the names of all the routers an IP packet passes through on its way from your Mac to a destination host. It also measures the round-trip time from your Mac to the router... Read more

Latest Forum Discussions

See All

Cytus II (Games)
Cytus II 1.0.1 Device: iOS Universal Category: Games Price: $1.99, Version: 1.0.1 (iTunes) Description: "Cytus II" is a music rhythm game created by Rayark Games. It's our fourth rhythm game title, following the footsteps of three... | Read more »
JYDGE (Games)
JYDGE 1.0.0 Device: iOS Universal Category: Games Price: $4.99, Version: 1.0.0 (iTunes) Description: Build your JYDGE. Enter Edenbyrg. Get out alive. JYDGE is a lawful but awful roguehate top-down shooter where you get to build your... | Read more »
Tako Bubble guide - Tips and Tricks to S...
Tako Bubble is a pretty simple and fun puzzler, but the game can get downright devious with its puzzle design. If you insist on not paying for the game and want to manage your lives appropriately, check out these tips so you can avoid getting... | Read more »
Everything about Hero Academy 2 - The co...
It's fair to say we've spent a good deal of time on Hero Academy 2. So much so, that we think we're probably in a really good place to give you some advice about how to get the most out of the game. And in this guide, that's exactly what you're... | Read more »
Everything about Hero Academy 2: Part 3...
In the third part of our Hero Academy 2 guide we're going to take a look at the different modes you can play in the game. We'll explain what you need to do in each of them, and tell you why it's important that you do. [Read more] | Read more »
Everything about Hero Academy 2: Part 2...
In this second part of our guide to Hero Academy 2, we're going to have a look at the different card types that you're going to be using in the game. We'll split them up into different sections too, to make sure you're getting the most information... | Read more »
Everything about Hero Academy 2: Part 1...
So you've started playing Hero Academy 2, and you're feeling a little bit lost. Don't worry, we've got your back. So we've come up with a series of guides that are going to help you get to grips with everything that's going on in the game. [Read... | Read more »
What mobile gaming can learn from the Ni...
While Nintendo might not have had things all its own way since it began developing for mobile, one thing it has got right is the release of the Switch. After the disappointment of the WiiU, which I still can't really explain, the Switch felt a... | Read more »
Programmer of Sonic The Hedgehog launche...
Japanese programmer Yuji Naka is best known for leading the team that created the original Sonic The Hedgehog. He’s moved on from the speedy blue hero since then, launching his own company based in Tokyo – Prope Games. Legend of Coin is the... | Read more »
Why doesn't mobile gaming have its...
The Overwatch League is a pretty big deal. It's an attempt to really push eSports into the mainstream, by turning them into, well, regular sports. But slightly less sweaty. It's a lavish affair with teams from all around the world, and more... | Read more »

Price Scanner via MacPrices.net

9.7-inch 2017 WiFi iPads on sale starting at...
B&H Photo has 9.7″ 2017 WiFi #Apple #iPads on sale for $30 off MSRP for a limited time. Shipping is free, and pay sales tax in NY & NJ only: – 32GB iPad WiFi: $299, $30 off – 128GB iPad WiFi... Read more
Wednesday deal: 13″ MacBook Pros for $100-$15...
B&H Photo has 13″ #Apple #MacBook Pros on sale for up to $100-$150 off MSRP. Shipping is free, and B&H charges sales tax for NY & NJ residents only: – 13-inch 2.3GHz/128GB Space Gray... Read more
Apple now offering Certified Refurbished 2017...
Apple has Certified Refurbished 9.7″ WiFi iPads available for $50-$80 off the cost of new models. An Apple one-year warranty is included with each iPad, and shipping is free: – 9″ 32GB WiFi iPad: $... Read more
10″ iPad Pros on sale for $50-$75 off MSRP, n...
B&H Photo has 10″ and #Apple #iPad Pros on sale for up to $75 off MSRP. Shipping is free, and B&H charges sales tax in NY & NJ only. Note that some sale prices are restricted to certain... Read more
Apple refurbished Mac minis available startin...
Apple has restocked Certified Refurbished Mac minis starting at $419. Apple’s one-year warranty is included with each mini, and shipping is free: – 1.4GHz Mac mini: $419 $80 off MSRP – 2.6GHz Mac... Read more
Amazon offers Silver 13″ Apple MacBook Pros f...
Amazon has new Silver 2017 13″ #Apple #MacBook Pros on sale today for up to $150 off MSRP, each including free shipping: – 13″ 2.3GHz/128GB Silver MacBook Pro (MPXR2LL/A): $1199.99 $100 off MSRP – 13... Read more
Sale: 12″ 1.3GHz MacBooks on sale for $1499,...
B&H Photo has Space Gray and Rose Gold 12″ 1.3GHz #Apple MacBooks on sale for $100 off MSRP. Shipping is free, and B&H charges sales tax for NY & NJ residents only: – 12″ 1.3GHz Space... Read more
Apple offers Certified Refurbished 2017 iMacs...
Apple has a full line of Certified Refurbished iMacs available for up to $350 off original MSRP. Apple’s one-year warranty is standard, and shipping is free. The following models are available: – 27... Read more
13″ MacBook Airs on sale for $120-$100 off MS...
B&H Photo has 2017 13″ 128GB MacBook Airs on sale for $120 off MSRP. Shipping is free, and B&H charges sales tax for NY & NJ residents only: – 13″ 1.8GHz/128GB MacBook Air (MQD32LL/A): $... Read more
15″ Touch Bar MacBook Pros on sale for up to...
Adorama has Space Gray 15″ MacBook Pros on sale for $200 off MSRP. Shipping is free, and Adorama charges sales tax in NJ and NY only: – 15″ 2.8GHz MacBook Pro Space Gray (MPTR2LL/A): $2199, $200 off... Read more

Jobs Board

*Apple* Solutions Consultant - Apple (United...
# Apple Solutions Consultant Job Number: 113384559 Brandon, Florida, United States Posted: 10-Jan-2018 Weekly Hours: 40.00 **Job Summary** Are you passionate about Read more
Art Director, *Apple* Music + Beats1 Market...
# Art Director, Apple Music + Beats1 Marketing Design Job Number: 113258081 Santa Clara Valley, California, United States Posted: 05-Jan-2018 Weekly Hours: 40.00 Read more
*Apple* Pay & Wallet Engineering Manager...
# Apple Pay & Wallet Engineering Manager, Apple Watch Job Number: 83769531 Santa Clara Valley, California, United States Posted: 06-Nov-2017 Weekly Hours: 40.00 Read more
UI Tools and Automation Engineer, *Apple* M...
# UI Tools and Automation Engineer, Apple Media Products Job Number: 113136387 Santa Clara Valley, California, United States Posted: 11-Jan-2018 Weekly Hours: 40.00 Read more
Senior Product Architect, *Apple* Pay - App...
# Senior Product Architect, Apple Pay Job Number: 58046427 Santa Clara Valley, California, United States Posted: 04-Jan-2018 Weekly Hours: **Job Summary** Apple , Read more
All contents are Copyright 1984-2011 by Xplain Corporation. All rights reserved. Theme designed by Icreon.