[comp.sys.mac] List Manager in LightspeedC

rmh@apple.UUCP (06/16/87)

A little while back, someone wanted to see an example of using the
List Manager in LightSpeedC.  I've got one here which uses its own
LDEF (List Manager Definition procedure) to draw custom data in the
cells.

This set of routines invokes a standard file dialog look-alike.
It doesn't use any of the actual standard file routines; all is
done "from scratch".

Caveat emptor: I don't claim this is the best way to do this, only
that it worked for me.  You may also find funny bits in these fragments
that are left over from stuff I cut out to trim the example down
a bit.  (And I hope I didn't break anything when I did the trimming!)
Some attention has been paid to the problem of accessing
global data; this is not normally a problem in LightspeedC, but I
intend to port this code to MPW someday, where (for drivers and DAs) it is.

One more warning: this code doesn't return anything to the caller!
I built it as a demo which didn't need to know what the user finally
selected. After the ModalDialog loop exits, use LLastClick to learn
what was selected.

First fragment is the LDEF code, then the code which goes in your application.

======================================================================

X# include <MacTypes.h>
X# include <TextEdit.h>
X# include <ListMgr.h>
X
X/*
X * This is a list definition procedure, documented in IM IV, pg. 276
X * Compile this as a code resource of type LDEF.
X */
Xpascal void main (lMessage, lSelect, lRect, lCell, lDataOffset,
X					lDataLen, lHandle)
Xshort lMessage;
XBoolean lSelect;
XRect *lRect;
XCell lCell;
Xshort lDataOffset;
Xshort lDataLen;
XListHandle lHandle;
X	{
X	Rect r;
X	/*
X	 * Never dereference a LightspeedC "DataHandle" - it's a
X	 * pointer to a pointer to an array.  Instead convert it to
X	 * a pointer to pointer to char.
X	 */
X	Handle cells = (Handle) (*lHandle)->cells;
X	Handle iconh;
X	
X	switch (lMessage)
X		{
X		case lInitMsg:
X			return;
X		case lDrawMsg:
X			/*
X			 * Draw one cell's data. These cells are a single
X			 * line, with a picture at the left and text to
X			 * the right, like the standard file dialog.
X			 * The picture used depends on whether the
X			 * last char of data for the cell is a plus sign
X			 * or not.
X			 */
X			HLock (cells);
X			if (*((*cells)+lDataOffset+(lDataLen-1)) == '+')
X				iconh = GetResource ('PICT', 0);
X			else
X				iconh = GetResource ('PICT', 1);
X			if (iconh != (Handle)0)
X				{
X				r = *lRect;
X				r.right = r.left + 16;
X				DrawPicture (iconh, &r);
X				}
X			
X			/*
X			 * Picture is drawn; now do the text.
X			 * Don't include the final char of the
X			 * text, as that was a flag to tell us
X			 * what picture to use.
X			 */
X			r = *lRect;
X			r.left += 16;
X			TextBox ((*cells)+lDataOffset, (long)(lDataLen-1),
X				&r, teJustLeft);
X			HUnlock (cells);
X			if (lSelect)
X				InvertRect (lRect);
X			break;
X		case lHiliteMsg:
X			InvertRect (lRect);
X			break;
X		case lCloseMsg:
X			return;
X		default:
X			SysBeep (10);
X			return;
X		}
X	}

=======================================================================

X# include "Defs.h"
X# include "ListMgr.h"
X
X/*
X * Items in the dialog
X */
X# define OK	1	/* Button	*/
X# define CANCEL	2	/* Button	*/
X# define TITLE	3	/* Static Text	*/
X# define LIST	4	/* User Item	*/
X
Xtypedef struct
X	{
X	ListHandle lh;
X	EventRecord er;
X	Boolean update;
X	} globals;
X
X/*
X * This routine invokes a standard-file-like dialog. The dialog window
X * itself is defined as a resource of our own; we aren't using any of
X * the system's standard file stuff. We are also using our own LDEF
X * (List Manager Definition routine) to manage the list of items
X * shown in the scrolling window (including managing the scrolling!).
X *
X * This routine is designed to work either in an application or in a
X * desk accessory.  Global data is difficult to manage
X * correctly in a DA, so here we fake global data by putting all would-be
X * globals into an automatic structure in this routine.  (That is, none
X * of our data needs to be accessed outside the lifetime of this routine,
X * so it doesn't need to outlive this routine.  It just needs to be
X * accessible to the lower-level routines.)  A pointer to the structure
X * is placed in the dialog window's refCon field, where the lower-level
X * routines can access it.
X */
X	
XPickList ()
X	{
X	DialogRecord dr;
X	DialogPtr dp;
X	Handle ih;
X	Rect r;
X	short itemHit;
X	short itemtype;
X	char buf[10];
X	Rect databounds;
X	Cell csize;
X	long *lptr;
X	Point where;
X	globals g;
X	GrafPtr saveport;
X	extern pascal long LLastClick();
X	extern pascal Boolean myFilter ();
X	extern pascal void ShowList ();
X
X	GetPort (&saveport);
X	
X	/* 17187 is the dialog's resource ID */
X	dp = GetNewDialog (17187, &dr, (WindowPtr)-1L);
X	if (dp == NULL)
X		return;
X	SetPort (dp);	/* IMPORTANT - Do this before calling LNew() */
X	((WindowPeek)dp)->refCon = (long)&g;
X	
X	ParamText ("\pThis", "\p is", "\p the", "\p title");
X	
X	/*
X	 * The USERITEM in this dialog is a scrolling list of text,
X	 * handled by the List Manager.  Create the list empty, and store
X	 * its handle in the window record so that low-level routines
X	 * can find it.  Install an LDEF routine to draw the list.
X	 *
X	 * 128 is the resource ID of the LDEF routine, which is stored
X	 * in the resource fork of the application.
X	 */
X	GetDItem (dp, LIST, &itemtype, &ih, &r);
X	SetRect (&databounds, 0, 0, 1, 0);
X	csize.h = csize.v = 0;
X	r.right -= 15;
X	g.lh = LNew (&r, &databounds, csize, (short)128, dp,
X	 				true, false, false, true);
X	(*(g.lh))->selFlags = lOnlyOne;
X	r.right += 15;
X	SetDItem (dp, LIST, itemtype, (Handle)ShowList, &r);
X
X	g.update = FALSE;
X	ShowWindow (dp);
X		
X	do
X		{
X		ModalDialog (myFilter, &itemHit);
X		/* myFilter() has put the event record into g for us */
X		if (itemHit == LIST)
X			{
X			GlobalToLocal (&(g.er.where));
X			if (LClick (g.er.where, g.er.modifiers, g.lh))
X				itemHit = OK; /* Double-click */
X			}
X		} while (itemHit != OK && itemHit != CANCEL);
X
X	LDispose (g.lh);
X	SetPort (saveport);
X	CloseDialog (dp);
X	}
X
Xpascal void ShowList (wp, item)
XWindowPtr wp;
Xshort item;
X	{
X	int i, j = 0;
X	Handle h;
X	short hlen;
X	short total;
X	Point theCell;
X	ListHandle lh;
X	static char buf[256];
X	short itemtype;
X	Rect r;
X	GrafPtr thePort;
X	
X	GetDItem (wp, item, &itemtype, &h, &r);
X	InsetRect (&r, -1, -1);
X	FrameRect (&r);
X	
X	lh = ((globals *)(((WindowPeek)wp)->refCon))->lh;
X
X	if (((globals *)(((WindowPeek)wp)->refCon))->update)
X		{
X		GetPort (&thePort);
X		LUpdate (thePort->visRgn, lh);
X		}
X	else
X		myFillList (lh);
X	}
X
X/*
X * This guy's job is just to allow the higher-level routine which called
X * ModalDialog to actually see any mouseDown events handled by
X * ModalDialog.  Except for this spying, it behaves exactly like
X * the standard filterProc function (see Inside Mac, vol I chap 13)
X */
Xpascal Boolean myFilter (dp, theEvent, itemHit)
XDialogPtr dp;
XEventRecord *theEvent;
Xshort *itemHit;
X	{
X	if (theEvent->what == keyDown
X	 && (((theEvent->message & charCodeMask) == '\003')	/* ENTER */
X	  || ((theEvent->message & charCodeMask) == '\015')))	/* RETURN */
X		{
X		/* User typed ENTER or RETURN */
X		*itemHit = OK;
X		theEvent->what = mouseDown;
X		return (TRUE);
X		}
X	
X	if (theEvent->what == mouseDown)
X		((globals *)(((WindowPeek)dp)->refCon))->er = *theEvent;
X		
X	return (FALSE);
X	}
X
XmyFillList (lh, mh)
XListHandle lh;
X	{
X	/*
X	 * Well, never mind what I put in my list.
X	 * This routine just uses LAddToCell and suchlike
X	 * routines to manipulate the contents of the
X	 * list.  See IM IV, chapter 30.
X	}

==========================================================================
Rick Holzgrafe			 | {sun,voder,nsc,mtxinu,dual}!apple!rmh
Communications Software Engineer | AppleLink HOLZGRAFE1 (I don't look often)
Apple Computer, Inc.		 | "All opinions expressed are mine, and do
20525 Mariani Ave. MS: 27-Y	 | not necessarily represent those of my
Cupertino, CA 95014		 | employer, Apple Computer Inc."