[net.sources.mac] popup menus in Lightspeed C

timd@ur-tut.UUCP (02/23/87)

/* I cleaned this up so that lines only take up one, ie. no wrap around. If
** i missed somthing you have my appologies.
**	--td
*/
#include	<QuickDraw.h>
#include	<MenuMgr.h>
#include	<Memorymgr.h>
#include	<WindowMgr.h>
#include	<TextEdit.h>
#include	<OSUtil.h>
#include	<Eventmgr.h>
#include	<pascal.h>


typedef short int16;
typedef long  int32;

#define mDrawMsg 0
#define mChooseMsg 1
#define mSizeMsg 2

#define POINTERH ((*theMenu)->menuWidth - 4)
#define POINTERV 8
#define MENUV 	20
#define FRAMEH	2
#define	FRAMEV	2


#define DELAY 	2L

/*--------------------------------------------------------------------
**doc:	pin(i,l,u):int
**doc>	return a value in a bounded range
**
**		INPUTS:
**			i	- test value
**			l	- lower bound
**			u	- upper bound
**
**		OUTPUTS:	NONE
**
**		Description
**			returns a value in the range of "l" to "u". If
**			the value is greater the "u" returns "u", if value
**			is less than "l" returns "l", else returns "i".
**
**
*/		
pin(i,l,u)
int16 i;
int16 l;
int16 u;
{
	if (i<l)
		return (l);
	else if (i>u)
		return (u);
	else
		return (i);
}

/*--------------------------------------------------------------------
**doc:	MenuDefProc(message, theMenu,menuRect,hitPt,whichItem):int
**doc>	return a value in a bounded range
**
**		INPUTS:
**			message 	-	type of action requested
**			theMenu		-	Handle to the menu
**			menuRect	-	Not sure
**			hitPt		-	where the mouse was pressed
**			whichItem	-	which item was selected from 
**			the menu
**
**		OUTPUTS:	NOT SURE
**
**		Description
**			uses a standard menu routine for the menu def proc.
**			not really sure what it does.
**
**
**
*/		

MenuDefProc(message, theMenu,menuRect,hitPt,whichItem)
int16 message;
MenuHandle theMenu;
Rect *menuRect;
Point *hitPt;
int16 *whichItem;
{
 ProcPtr	pFuncPnt;
 Handle		tmpHand;
 
 HLock(theMenu);					/* careful... */
 tmpHand = (*theMenu)->menuProc;
 
 HLock(tmpHand);					/* careful... */
 pFuncPnt = (ProcPtr) *tmpHand;
 
 CallPascal(message,theMenu,menuRect,*hitPt,whichItem,pFuncPnt);
  
 HUnlock(tmpHand);
 HUnlock(theMenu);
}

/*--------------------------------------------------------------------
**doc:	PopUpSelcet(theMenu,hitPt):int
**doc>	the item number of the item selcted
**
**		INPUTS:
**			theMenu		-	Handle to the menu
**			hitPt		-	where the mouse was pressed
**
**		OUTPUTS:	NONE
**
**		Description
**			Handles all the overhead of pop up menus. This routine
**			does the work. It sets up the section of the screen to 
**			be used for the pop up menu, and does a bit copy of the 
**			screen under where the menu is placed to restore the
**			screen when it's done. Returns the item number for the
**			item selected or zero if no item was selected.
**
**
*/		
int32 PopUpSelect(theMenu,hitPt)
MenuHandle theMenu;
Point *hitPt;
{
	
	GrafPtr port;
	GrafPtr wMngPort;
	BitMap 	**theMenuBits;
	BitMap 	*menuBits;
	int16 	rowBytes;
	int16	rows;
	Rect	menuRect;
	SysPPtr	tmpsysPtr;

	int16 whichItem;
	int16 blink;
	int32 nilPt;
	int32 dticks;

	dticks = DELAY;
	if (!WaitMouseUp())
		return(0L);
		
	LoadResource((*theMenu)->menuProc);
	HLock((*theMenu)->menuProc);	/* ERROR CHECKING NEEDED HERE */


/* find out what size the menu will be.  */
	MenuDefProc(mSizeMsg,theMenu,&menuRect,&hitPt,&whichItem);

/* create a rectangle of that size, within the boundries of the screen */

	SetRect(&menuRect,0,MENUV,(*theMenu)->menuWidth,
			MENUV + (*theMenu)->menuHeight);

	InsetRect(&menuRect,-FRAMEH,-FRAMEV);

	rowBytes = ((menuRect.right - menuRect.left + 15)>>4)<<1;
	rows = menuRect.bottom - menuRect.top;
	
/* 
** allocate memory to store the portion of the screen we're going to
** over write.
*/
	theMenuBits = (BitMap **)
			NewHandle(rowBytes * rows + (int32)sizeof(BitMap));

	if (!theMenuBits)
		return (0L);

	HLock(theMenuBits);
	menuBits = *theMenuBits;

	menuBits->baseAddr = (char *)(menuBits + 1);
	menuBits->rowBytes = rowBytes;
	menuBits->bounds = menuRect;

	GetPort(&port);
	GetWMgrPort(&wMngPort); /* could be changed to the window pointer */ 
	SetPort(wMngPort);
		
	SetOrigin(
		pin(POINTERH - hitPt->h,
		    (*theMenu)->menuWidth - wMngPort->portRect.right + FRAMEH,
			1-FRAMEH),
		pin(MENUV+POINTERV - hitPt->v,
		  MENUV + (*theMenu)->menuHeight - wMngPort->portRect.bottom + 
			FRAMEV, 1 - FRAMEV)); 
			
	

	ClipRect(&wMngPort->portRect);

/* 
** copy the section of the screen that we're going to cover over
** so that we can restore it when we're done.
**
*/
	CopyBits(&wMngPort->portBits,menuBits,&menuBits->bounds,
			 &menuBits->bounds,0,0L);

	/*
	** next 10 lines of code are here to set up the "menu rectangle"
	*/
	InsetRect(&menuRect,FRAMEH,FRAMEV);
	EraseRect(&menuRect);
	InsetRect(&menuRect,-1,-1);
	FrameRect(&menuRect);
	InsetRect(&menuRect,1,1);

	PenNormal();
	MoveTo(menuRect.left+1,menuRect.bottom+1);
	Line((*theMenu)->menuWidth,0);
	Line(0,-(*theMenu)->menuHeight);

	ClipRect(&menuRect);

/*
** draw the menu using the standard menu def proc. set the hit point
** to be what would normally be in the menu bar so that the menu name
** will be hilighted.
**
*/
	whichItem =0;
	SetPt(hitPt,0,0);
	MenuDefProc(mDrawMsg,theMenu,&menuRect,&hitPt,&whichItem);
	do {
		MenuDefProc(mChooseMsg,theMenu,&menuRect,&hitPt,&whichItem);
		GetMouse(&hitPt);
	} while(WaitMouseUp());
/*
** Blink the selected item several times.
*/
	if (whichItem)
		{
		tmpsysPtr = GetSysPPtr();
		for (blink = ((tmpsysPtr->misc)>>2) & 0x3;blink;blink--) {
		    nilPt = 0L;
		    MenuDefProc(mChooseMsg,theMenu,&menuRect,&nilPt,&whichItem);
		    Delay(DELAY,&nilPt);
		    MenuDefProc(mChooseMsg,theMenu,&menuRect,&hitPt,&whichItem);
		    Delay(DELAY,&nilPt);
		}
	}
	
	
	HUnlock((*theMenu)->menuProc);

	ClipRect(&wMngPort->portRect); 		/* set back to do CopyBits() */

/*
** restore the screen so that what ever was underneath our menu looks
** like it did before the menu wrote over it.
**
*/
	CopyBits(menuBits,&wMngPort->portBits,&menuBits->bounds,
			 &menuBits->bounds,0,0L);
	HUnlock(theMenuBits);
	DisposHandle(theMenuBits);

	SetOrigin(0,0);
	ClipRect(&wMngPort->portRect);
	SetPort(port);

	return (whichItem?((int32)(*theMenu)->menuID<<16)+whichItem:0L);
}


main()
{
	extern	pascal int pdumfuc();
	
	MenuHandle menu;
	EventRecord event;
	GrafPtr port;
	Rect box;
	int32	returncde;

	InitGraf(&thePort);
	InitFonts();
	InitWindows();
	TEInit();
	InitDialogs(0L);
	InitMenus();
	InitCursor();

	GetWMgrPort(&port);
	SetPort(port);
	ClipRect(&port->portRect);
	SetRect(&box,32,32,64,64);
	PlotIcon(&box,GetIcon(0));
	
	menu = NewMenu(1,"");
	AppendMenu(menu,"\PBeep;(-;Quit");

		while(1) {
			GetNextEvent(everyEvent,&event);
			if(event.what == mouseDown)
				if(PtInRect(event.where,&box)) {
				    returncde = PopUpSelect(menu,&event.where);
					switch (LoWord(returncde)) {
						case 1:
							SysBeep(4);
							break;
						case 3:
							ExitToShell();
							break;
				   }
				}
		}
}



/*		I hope this helps those of you who wanted popup menu info
**			-- timd
**			University of Rochester
*/