[BACK]Return to gui.c CVS log [TXT][DIR] Up to [Development] / performer / src / lib / libpfutil

File: [Development] / performer / src / lib / libpfutil / gui.c (download)

Revision 1.1, Tue Nov 21 21:39:36 2000 UTC (16 years, 10 months ago) by flynnt
Branch: MAIN
CVS Tags: HEAD

Initial check-in based on OpenGL Performer 2.4 tree.
-flynnt

/*
 * Copyright 1993, 1994, 1995, Silicon Graphics, Inc.
 * ALL RIGHTS RESERVED
 *
 * This source code ("Source Code") was originally derived from a
 * code base owned by Silicon Graphics, Inc. ("SGI")
 * 
 * LICENSE: SGI grants the user ("Licensee") permission to reproduce,
 * distribute, and create derivative works from this Source Code,
 * provided that: (1) the user reproduces this entire notice within
 * both source and binary format redistributions and any accompanying
 * materials such as documentation in printed or electronic format;
 * (2) the Source Code is not to be used, or ported or modified for
 * use, except in conjunction with OpenGL Performer; and (3) the
 * names of Silicon Graphics, Inc.  and SGI may not be used in any
 * advertising or publicity relating to the Source Code without the
 * prior written permission of SGI.  No further license or permission
 * may be inferred or deemed or construed to exist with regard to the
 * Source Code or the code base of which it forms a part. All rights
 * not expressly granted are reserved.
 * 
 * This Source Code is provided to Licensee AS IS, without any
 * warranty of any kind, either express, implied, or statutory,
 * including, but not limited to, any warranty that the Source Code
 * will conform to specifications, any implied warranties of
 * merchantability, fitness for a particular purpose, and freedom
 * from infringement, and any warranty that the documentation will
 * conform to the program, or any warranty that the Source Code will
 * be error free.
 * 
 * IN NO EVENT WILL SGI BE LIABLE FOR ANY DAMAGES, INCLUDING, BUT NOT
 * LIMITED TO DIRECT, INDIRECT, SPECIAL OR CONSEQUENTIAL DAMAGES,
 * ARISING OUT OF, RESULTING FROM, OR IN ANY WAY CONNECTED WITH THE
 * SOURCE CODE, WHETHER OR NOT BASED UPON WARRANTY, CONTRACT, TORT OR
 * OTHERWISE, WHETHER OR NOT INJURY WAS SUSTAINED BY PERSONS OR
 * PROPERTY OR OTHERWISE, AND WHETHER OR NOT LOSS WAS SUSTAINED FROM,
 * OR AROSE OUT OF USE OR RESULTS FROM USE OF, OR LACK OF ABILITY TO
 * USE, THE SOURCE CODE.
 * 
 * Contact information:  Silicon Graphics, Inc., 
 * 1600 Amphitheatre Pkwy, Mountain View, CA  94043, 
 * or:  http://www.sgi.com
 */


/*
 * file: gui.c
 * -----------
 *
 * Simple gui for Performer sample app.
 * Uses pfDataPools to store GUI data so that it is MP safe.
 *
 * $Revision: 1.1 $
 * $Date: 2000/11/21 21:39:36 $
 *
 */

#include <stdlib.h>
#include <unistd.h>
#include <string.h>
#ifndef __linux__
#include <bstring.h>
#endif  /* __linux__ */
#include <math.h>
#include <X11/cursorfont.h>

#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>

#include <X11/X.h>

#include <Performer/pfutil.h>
#include "gui.h"
#include "pup.h"

/******************************************************************************
 *			    Defines and Constants
 ******************************************************************************
 */

#define MAP_XMOUSE(mx)	((((mx - GUI->pixOrgX) - GUI->transX) / GUI->scaleX) )
#define MAP_YMOUSE(my)	((((my - GUI->pixOrgY) - GUI->transY) / GUI->scaleY) )

#define PFUGUI_REFRESH	2	/* Draw into both front and back buffers */

/* Cursor constants */
#define PFU_CURSOR_IN_MAIN 	0
#define PFU_CURSOR_IN_GUI 	1

/* mode bitmasks */
#define STICKY_DFT 0x1

/******************************************************************************
 *			    Static Variables
 ******************************************************************************
 */

static pfuGUI 		*GUI = NULL;
static pfDataPool 	*GUIDP = NULL;

/* static data - for Draw Process */
static pfVec3   OnColor;
static pfVec3   OffColor;
static pfVec3   SliderColor;
static pfVec3   InactiveSliderColor;
static pfVec3   ValColor;
static pfVec3   BackColor;
static pfVec3   TextColor;
static pfVec3   MessageColor;
static pfVec3   InactiveTextColor;
static pfVec4   ClearColor;

/*
 * Font stuff
 *  XXX Currently the font stuff is kept in static arrays.
 *	This means that, when scaling fonts,  only the draw process knows about
 *	The current font sizes.
 *	The font arrays need to be put in the GUI data pool !!!
 *
 */


#define FONT_SIZE_TREE 	(PFU_FONT_BIG)
#define FONT_SIZE_TITLE	(PFU_FONT_BIG)
#define FONT_SIZE_VAL 	(PFU_FONT_MED)
#define FONT_SIZE_TEXT 	(PFU_FONT_SMALL)
#define NUM_FONT_SIZES (PFU_NUM_FONTS+1)

#define NUMBER_FONTS	0
#define TEXT_FONTS	0
#define TREE_FONTS	1
#define NUM_GUI_FONTS   2


/* Message fonts are for pfuDrawMessage and are scaled separately from GUI 
 * and Message fonts are ONLY maintained in the draw process !!!
 */
#define MESSAGE_FONTS	2
#define NUM_TOTAL_FONTS	3



/* Font Management Flags */
#define FONT_SCALE_DIRTY    0x1
#define FONT_DRAW_DIRTY	    0x2
#define GUI_FONT_DIRTY	    0x3
#define MESSAGE_FONT_SCALE_DIRTY 0x4
#define MESSAGE_FONT_DIRTY	    0x3
#define FONT_DIRTY	    0x7

#define NUM_FONT_NAME		"utopia-medium-r"
#define TEXT_FONT_NAME		"utopia-bold-r"
#define FANCY_FONT_NAME		"utopia-bold-i"

static char FontNames[NUM_TOTAL_FONTS][PF_MAXSTRING] = {
    /* TextFonts */ TEXT_FONT_NAME,
    /* TreeFonts */ NUM_FONT_NAME,
    /* MessageFonts */ FANCY_FONT_NAME,
};
static int FontSizes[NUM_TOTAL_FONTS][NUM_FONT_SIZES] = {
    /* TextFonts */ {1.0f, 8.0f, 9.0f, 10.0f},
    /* TreeFonts */ {1.0f, 8.0f, 9.0f, 10.0f},
    /* MessageFonts */ {1.0f, 14.0f, 24.0f, 48.0f},
};
/* Per-process variables necessary since we fork and each
 * process needs to maintain local FM or XFont structures
 */
static pfuGUIFontArray	Fonts[NUM_TOTAL_FONTS];
static int FontFlags = GUI_FONT_DIRTY;
static float GUIFontScale = 0;
static int MessageFontsInited = 0;


static GUIFont *CurFont = 0;

#if 0
static void fontstub(void) {}
#endif
#define	dspstring(_string)	    pfuDrawString(_string)
#define	stringwidth(_f, _string)    pfuGetXFontWidth(&(_f), _string)
#define fontheight(_f)		    pfuGetXFontHeight(&(_f))
#define mysetfont(_f)		    pfuSetXFont(&(_f))



	/*---------------------------------------------------------*/

void pfuCullGUI(pfChannel * ch, void *data);
void pfuDrawGUI(pfChannel * ch, void *data);

	/*---------------------------------------------------------*/

static pfuGUI*	getGUIDataPool(int okToCallBeforeInit) ;
static void	updateGUIViewport(int force);
static void	drawPanel(pfuPanel* p, int redraw);
static pfuWidget*	scanPanel(pfuPanel* p, int x, int y, int ymin);
static void	initNewWidget(pfuWidget* w, int type, int id);
static void	resetWidget(pfuWidget* w);
static void	positionWidgetAttrs(pfuWidget * w);
#if 0
static int	getWidgetSize(int type);
#endif
static void	widgetDim(pfuWidget* w, int xo, int yo, int xs, int ys);
static void	fitWidgets(void) ;
static void	setWidgetValue(pfuWidget* w, int dft, float newValue);
static void	setWidgetSelection(pfuWidget* w, int dft, int index);
static int	scanSelections(RadioButton* r, int x, int y, int ymin);
static void	drawWidget(pfuWidget* w, pfuPanel *p);
static void	drawString(float x, float y, pfVec3 clr, const char* str);
static void	drawBox(pfVec2 coord[4], pfVec3 clr, int elev);
static void	drawStateIndicator(pfVec2 icoord[4], int on);
static void	drawSwitchButton(pfVec2 coord[4], pfVec2 icoord[4], int on);
static void	drawBevel(pfVec2 coord[4], pfVec3 clr, float bevel);
static void	drawTriBevel(pfVec2 coord[4], pfVec3 clr, float bevel);

static void	scaleFonts(float _scale, int drawFlag);
static void	initGUIFonts(int drawFlag);
static void	initMessageFonts(void);
static void	dirtyWidget(pfuWidget * w);

static void drawMessage(pfChannel *chan, const char *msg,
		int rel, int just, float x, float y, int size, int cimode,
		unsigned long textClr, unsigned long shadowClr);

        /*-------------------------- GUI -----------------------------*/

void
pfuInitGUI(pfPipeWindow *pw)
{
    pfPipe *p = pfGetPWinPipe(pw);
    pfFrameStats    *fs;

    getGUIDataPool(1);

    if (pfGetHyperpipe(p))
	GUI->refresh = pfGetHyperpipe(p) * 4;
    else
    {
	if (pfGetPWinType(pw) & PFPWIN_TYPE_X)
	    GUI->refresh = 4;
	else
	    GUI->refresh = 2;
    }
    GUI->pipe = p;
    GUI->pwin = pw;
    GUI->channel = pfNewChan(p);
    pfAddChan(pw, GUI->channel);
    pfChanTravFunc(GUI->channel, PFTRAV_CULL, pfuCullGUI);
    pfChanTravFunc(GUI->channel, PFTRAV_DRAW, pfuDrawGUI);

    /* have only the fast stats on for the GUI */
    fs = pfGetChanFStats(GUI->channel);
    pfFStatsClass (fs, PFSTATS_ALL, PFSTATS_OFF);
    /* set the fast process timing stats just in case someone turns
     * on stats for the gui for load managment
     */
    pfFStatsClassMode(fs, PFFSTATS_PFTIMES, PFFSTATS_PFTIMES_BASIC, PFSTATS_SET);

    pfuGUIViewport(0.0f, 1.0f, 0.0f, .2f);		/* Default viewport */
#ifdef	USE_FONT_MANAGER

    GUI->useFM = 1;
#endif

    updateGUIViewport(1);

    GUI->numPanels = 0;
    GUI->selectedWidget = 0;
    GUI->selectedPanel = 0;

    GUI->transX = GUI->transY = 0.0f;
    GUI->scaleX = GUI->scaleY = 1.0f;

    GUI->nGUIDraws = 0;
    GUI->guiDrawTime = 0.0f;

    pfuInitGUICursors();
    pfuUpdateGUICursor();
    
    GUI->inited = 1;

    GUI->pick->path = pfNewPath();
}

/*
 * All processes need to connect to this data pool.
*/
static pfuGUI*
getGUIDataPool(int okToCallBeforeInit)
{
    if (!GUI)
    {
	if (!GUIDP)
	    GUIDP = pfuGetUtilDPool();

	if (!GUIDP)
	{
	    pfNotify(PFNFY_FATAL, PFNFY_RESOURCE,
		     "getGUIDataPool: Must call pfuInitUtil() first.");
	}

	if (!(GUI = (pfuGUI *) pfDPoolFind(GUIDP, PFUGUI_DATA_DPID)))
	{
	    pfNotify(PFNFY_FATAL, PFNFY_RESOURCE,
		     "getGUIDataPool: could not find GUI Data Pool Data!");
	}
	else
	{
	    if (!(GUI->flags = (pfuGUIFlags*) pfDPoolFind(GUIDP, PFUGUI_FLAGS_DPID)))
	    {
		pfNotify(PFNFY_FATAL, PFNFY_RESOURCE,
		 "getGUIDataPool: could not find GUI Flags Data Pool Data!");
	    }
	    if (!(GUI->pick = (pfuGUIPicker*) pfDPoolFind(GUIDP, PFUGUI_PICK_DPID)))
	    {
		pfNotify(PFNFY_FATAL, PFNFY_RESOURCE,
		 "getGUIDataPool: could not find GUI Path Data Pool Data!");
	    }
	    if (!GUI->inited && !okToCallBeforeInit)
	    {
		pfNotify(PFNFY_WARN, PFNFY_PRINT,
		    "getGUIDataPool - pfuInitGUI() has not yet been called.");
	    }
	}
	initGUIFonts(0);
	GUI->fontScale = -1;
	GUI->hilightedNode = NULL;
	GUI->hlight = pfNewHlight(pfGetSharedArena());
	GUI->cursor = PFU_CURSOR_MAIN;
	if (!GUI->mainCursor)
	    GUI->mainCursor = PFU_CURSOR_MAIN_DEFAULT;
	if (!GUI->guiCursor)
	    GUI->guiCursor = PFU_CURSOR_GUI_DEFAULT;
	pfHlightColor(GUI->hlight, PFHL_FGCOLOR, 1.0f, 0.1f, 0.1f);
	pfHlightColor(GUI->hlight, PFHL_BGCOLOR, 0.1f, 1.0f, 0.1f);
	pfHlightPntSize(GUI->hlight, 3.0f);
	pfHlightLineWidth(GUI->hlight, 1.0f);
	pfHlightMode(GUI->hlight, PFHL_LINES);
    }
    return GUI;
}

static void
updateGUIViewport(int dirty)
{
    int         wxs, wys;
    int		sizeDirty;
    
    pfGetPWinSize(GUI->pwin, &wxs, &wys);

    sizeDirty = ((GUI->wxs != wxs) || (GUI->wys != wys));
    if (sizeDirty)
    {
	GUI->wxs = wxs;
	GUI->wys = wys;
	dirty = 1;
    }
    /* Compute pixel window coordinates of GUI */
    if (dirty)
    {
	GUI->orthoOrgY = 0.0f;
	GUI->pixOrgX = GUI->vpLeft * wxs;
	GUI->pixOrgY = GUI->vpBottom * wys;
	GUI->pixSizeX = (GUI->vpRight - GUI->vpLeft) * wxs;
	GUI->pixSizeY = (GUI->vpTop - GUI->vpBottom) * wys;
	GUI->orthoMaxY = GUI->pixSizeY;
	GUI->flags->dirty = GUI->flags->redrawAll = GUI->refresh;
	if (sizeDirty)
	{
	    GUI->flags->fitDirty = sizeDirty;
	    FontFlags |= FONT_SCALE_DIRTY;
	}
    }
}

void
pfuGUIViewport(float l, float r, float b, float t)
{
    if (!GUI)
	getGUIDataPool(0);

    pfDPoolLock(GUI->flags);

    GUI->vpLeft = l;
    GUI->vpRight = r;
    GUI->vpBottom = b;
    GUI->vpTop = t;

    pfChanViewport(GUI->channel, GUI->vpLeft, GUI->vpRight,
		   GUI->vpBottom, GUI->vpTop);

    updateGUIViewport(1);

    pfDPoolUnlock(GUI->flags);
}

pfChannel *
pfuGetGUIChan(void)
{
    if (!GUI)
	getGUIDataPool(0);
    return GUI->channel;
}

void
pfuGetGUIViewport(float *l, float *r, float *b, float *t)
{
    if (!GUI)
	getGUIDataPool(0);

    *l = GUI->vpLeft;
    *r = GUI->vpRight;
    *b = GUI->vpBottom;
    *t = GUI->vpTop;
}

void
pfuEnableGUI(int s)
{
    if (!GUI)
	getGUIDataPool(0);

    if (s)
    {
	pfChanTravMode(GUI->channel, PFTRAV_DRAW, PFDRAW_ON);

	pfDPoolLock(GUI->flags);
	GUI->flags->dirty = GUI->flags->redrawAll = GUI->refresh;
	pfDPoolUnlock(GUI->flags);
    }
    else
    {
	pfChanTravMode(GUI->channel, PFTRAV_DRAW, PFDRAW_OFF);
	GUI->cursor = PFU_CURSOR_MAIN;
    }
    GUI->active = s;
}

void
pfuExitGUI(void)
{
    /* write out performance stats */
    if (GUI->nGUIDraws)
	pfNotify(PFNFY_INFO, PFNFY_PRINT,
	    "pfuExitGUI: %d frames at %.2f ms per draw",
	    GUI->nGUIDraws,
	    1000.0f * GUI->guiDrawTime / GUI->nGUIDraws);
}

/*
 * Automatically scale and translate widgets to fit into panel.
*/
/*ARGSUSED*/
void
pfuFitWidgets(int val)
{
    if (!GUI)
	getGUIDataPool(0);

    pfDPoolLock(GUI->flags);

    GUI->flags->fitDirty = 1;

    pfDPoolUnlock(GUI->flags);
}


int
pfuInGUI(int x, int y)
{
    int clickX, clickY, clickIn=1;
    if (!GUI)
	getGUIDataPool(1);

    if (!GUI->active)
	return 0;

    if (GUI->mouse.flags  & PFUDEV_MOUSE_DOWN_MASK)
    {
	clickX = GUI->mouse.clickPosLast[0];
	clickY = GUI->mouse.clickPosLast[1];
	clickIn = (clickX >= GUI->pixOrgX && 
			clickX <=(GUI->pixOrgX + GUI->pixSizeX) &&
	    	   clickY >= GUI->pixOrgY && 
			clickY <=(GUI->pixOrgY + GUI->pixSizeY));
    }
    if (GUI->selectedWidget || (clickIn &&
	(x >= GUI->pixOrgX && x <=(GUI->pixOrgX + GUI->pixSizeX) &&
	    y >= GUI->pixOrgY && y <=(GUI->pixOrgY + GUI->pixSizeY))))
    {
	return(1);
    }
    return (0);
}

/*
 * pfuResetGUI will reset all widgets in all active panels to their
 *  default values.  The Widget funcs will be called in all cases,
 *  regardless of whether its value was already the default, execpt for
 *  action buttons (type == BUTTON).
 *  This is especially useful for MESSAGE widgets that have no concept
 *  of a default value.
 *  There is a test so that this function won't recursively call itself
 *  just in case it has been used as a Widget func.
 */

void
pfuResetGUI(void)
{
    static int	 inReset	= 0;
    int		 i;
    int		 j;
    pfuPanel	*p;
    pfuWidget	*w;

    if (inReset)
	return;

    inReset = 1;

    if (!GUI)
	getGUIDataPool(0);

    for (i = 0; i < GUI->numPanels; i++)
    {
	p = GUI->panels[i];
	if (!p || !(p->active))
	{
	    continue;
	}
	p->dirty = GUI->refresh;
	for (j = 0; j < p->numWidgets; j++)
	{
	    w = p->widgets[j];
	    if (w)
	    {
		resetWidget(w);
	    }
	}
    }

    pfDPoolLock(GUI->flags);
    GUI->flags->redrawAll = GUI->refresh;
    GUI->flags->dirty     = GUI->refresh;
    pfDPoolUnlock(GUI->flags);

    inReset = 0;
}

void
pfuGetGUIScale(float *x, float *y)
{
    if (!GUI)
	getGUIDataPool(0);
    *x = GUI->scaleX;
    *y = GUI->scaleY;
}

void
pfuGetGUITranslation(float *x, float *y)
{
    if (!GUI)
	getGUIDataPool(0);
    *x = GUI->transX;
    *y = GUI->transY;
}

void
pfuRedrawGUI(void)
{
    if (!GUI)
	getGUIDataPool(0);
    pfDPoolLock(GUI->flags);
    GUI->flags->dirty = GUI->flags->redrawAll = GUI->refresh;
    pfDPoolUnlock(GUI->flags);
}

void
pfuGUIHlight(pfHighlight *hlight)
{
    if (!GUI)
	getGUIDataPool(0);

    pfDPoolLock(GUI->flags);
    GUI->hlight = hlight;
    pfDPoolUnlock(GUI->flags);
}

pfHighlight *
pfuGetGUIHlight(void)
{
    if (!GUI)
	getGUIDataPool(0);

    return GUI->hlight;
}

/*ARGSUSED*/
int
_pfuGUIPreHlight(pfTraverser *trav, void *data)
{
    pfPushState();

    pfOverride(
        PFSTATE_ENLIGHTING |
        PFSTATE_ENWIREFRAME |
        PFSTATE_ENHIGHLIGHTING |
        PFSTATE_LIGHTMODEL |
        PFSTATE_FRONTMTL |
        PFSTATE_BACKMTL, 0);

    pfEnable(PFEN_HIGHLIGHTING);
    pfApplyHlight(GUI->hlight);

    return 0;
}

/*ARGSUSED*/
int
_pfuGUIPostHlight(pfTraverser *trav, void *data)
{
    pfPopState();
    return 0;
}


static void
drawBevelBox(float xorg, float yorg, float xsize, float ysize, 
	     uint clr, const char *str)
{
    pfVec2	coords[4];
    pfVec3	clr3;
    static pfVec3 strClr = {.1f, .1f, .1f};

    pfSetVec3(clr3, (clr&0xff)/255.0f, 
	      ((clr&0xff00) >> 8)/255.0f, 
	      ((clr&0xff0000) >> 16)/255.0f); 

    pfSetVec2(coords[0], xorg, yorg);
    pfSetVec2(coords[1], xorg+xsize, yorg);
    pfSetVec2(coords[2], xorg+xsize, yorg+ysize);
    pfSetVec2(coords[3], xorg, yorg+ysize);

    drawBevel(coords, clr3, 2);
    drawBox(coords, clr3, -1);
    drawBevel(coords, clr3, -1);

    if (str)
    {
	mysetfont(Fonts[TREE_FONTS][FONT_SIZE_TITLE]);
	drawString(xorg+4, yorg+4, strClr, str);
	mysetfont((*CurFont));
    }
}

typedef struct 
{
    pfVec2	bbox[2];
    int		x, y, depth, skip;
    int		parent, instling;
    pfNode	*node;

} pfuGUINode;

#define CALCY(d) 	( (ySize - (((d) + 1) * 2.0f) * YS ) )
#define CALCW(w) 	( (w) * 1.5f*XS - .5f*XS)

#define XS		60
#define YS		20
#define MAX_NODES	(1<<14)

static int		xSize, ySize, xOrigin, yOrigin, xMouse, yMouse;
static int		xChanSize, yChanSize, xChanOrigin, yChanOrigin;
static int		vpLeft, vpRight, vpBottom, vpTop, vpWidth;
static int		indentLevel;
static pfuGUINode	*nodeList = NULL;
static pfPath		*pickPath = NULL;

static void
drawSwitch(pfSwitch *sw, int x, int y)
{
    static pfVec3 	strClr = {.1f, .1f, .1f};
    int val;
    int			ht = fontheight(Fonts[TREE_FONTS][FONT_SIZE_TEXT]);
    char		row[64];

    val = pfGetSwitchVal(sw);

    mysetfont(Fonts[TREE_FONTS][FONT_SIZE_TEXT]);
    sprintf(row, "switch value %d", val);
    drawBevelBox(x-10, y-10, stringwidth(Fonts[TREE_FONTS][FONT_SIZE_TEXT], row) + 15,
	ht+15, 0xff80a0c0, NULL);
    drawString(x, y, strClr, row);
    mysetfont((*CurFont));
}

static void
drawSCS(pfSCS *scs, int x, int y)
{
    static pfVec3 	strClr = {.1f, .1f, .1f};
    char		row[64];
    pfMatrix 		mat;
    int			ht = fontheight(Fonts[TREE_FONTS][FONT_SIZE_TEXT]);

    int			r;
    int			c;
    int			widest = 0;

    pfGetSCSMat(scs, mat);

    /* determine widest matrix element */
    for (r = 0; r < 4; r++)
    {
	for (c = 0; c < 4; c++)
	{
	    char buffer[64];
	    int  width;
	    sprintf(buffer, "%+.2f", mat[r][c]);
	    width = (int)strlen(buffer);
	    if (widest < width)
		widest = width;
	}
    }
    widest+=2;

    mysetfont(Fonts[TREE_FONTS][FONT_SIZE_TEXT]);
    sprintf(row, "%+*.2f %+*.2f %+*.2f %+*.2f", 
	widest, mat[0][0], 
	widest, mat[0][1], 
	widest, mat[0][2], 
	widest, mat[0][3]); 

    drawBevelBox(x-10, y-10, stringwidth(Fonts[TREE_FONTS][FONT_SIZE_TEXT], row) + 15,
	4 * ht+15, 0xff80a0c0, NULL);

    drawString(x, y+3*ht, strClr, row);

    sprintf(row, "%+*.2f %+*.2f %+*.2f %+*.2f", 
	widest, mat[1][0], 
	widest, mat[1][1], 
	widest, mat[1][2], 
	widest, mat[1][3]); 
    drawString(x, y+2*ht, strClr, row);

    sprintf(row, "%+*.2f %+*.2f %+*.2f %+*.2f", 
	widest, mat[2][0], 
	widest, mat[2][1], 
	widest, mat[2][2], 
	widest, mat[2][3]); 
    drawString(x, y+ht, strClr, row);

    sprintf(row, "%+*.2f %+*.2f %+*.2f %+*.2f", 
	widest, mat[3][0], 
	widest, mat[3][1], 
	widest, mat[3][2], 
	widest, mat[3][3]);
    drawString(x, y, strClr, row);

    mysetfont((*CurFont));
}

typedef struct { int hcol; int col; char name[256]; } colTable;
#define COL_ENTRIES 13
static colTable boxColors[COL_ENTRIES] =
{
    0xff89a0ff, 0xff8989ff, "SCS",	  /*salmon*/
    0xff3c9700, 0xff3c8200, "DCS",    /*green*/
    0xffffa000, 0xffff7e00, "Group",  /*blue*/
    0xffa0ffff, 0xff68ffff, "Switch", /*powder blue*/
    0xff60ffff, 0xff60e0ff, "LOD",    /*yellow*/
    0xff0050a7, 0xff003ba7, "Billboard", /*brown*/
    0xff00c0ff, 0xff00b0ff, "LightSource", /*gold*/
    0xffc7b7ff, 0xffc7a7ff, "LightPoint", /*pink*/
    0xffff20ff, 0xffff10ff, "Morph", /*purple*/
    0xff6510ff, 0xff6500ff, "ASD", /*ruby*/
    0xff72ff3b, 0xff72f13b, "Sequence", /*light green*/
    0xffff8bbb, 0xffff7bbb, "Layer", /*violet*/
    0xff1b53ff, 0xff1b43ff, "Text", /*orange*/
};

static void
drawNode(pfNode *node, float x, float y, int ishlight)
{
    const char *typeName;
    char        str[512];
    int found = 0, i;

    /* Highlight node if it is on the pick path */
    if (pfSearch(pickPath, node) >= 0)
        ishlight = 1;

    if(ishlight == 0)
    {
        typeName = pfGetTypeName((pfObject*) node);
        if (strncmp(typeName, "pf", 2) == 0)
            typeName +=2;

        for (i = 0; i < COL_ENTRIES; i++)
        {
            if (!strcmp(typeName,boxColors[i].name))
                {
                    found = 1;
                    drawBevelBox(x, y, XS-1, YS-1, boxColors[i].col,
typeName);
                }
        }
        if (!found)
            drawBevelBox(x, y, XS-1, YS-1, 0xff808080, typeName);
    }
    else
    {
        typeName = pfGetTypeName((pfObject*) node);
        if (strncmp(typeName, "pf", 2) == 0)
            typeName +=2;

        strcpy(str, typeName);

        if (pfGetNodeName(node) != NULL)
        {
            strcat(str, "\"");
            strcat(str, pfGetNodeName(node));
            strcat(str, "\"");
        }

        /* indicate number of geosets referenced by geode */
        if (pfIsOfType(node, pfGetGeodeClassType()))
        {
            char children[32];
            sprintf(children, "(%d)", pfGetNumGSets((pfGeode *)node));
            strcat(str, children);
        }

        for (i = 0; i < COL_ENTRIES; i++)
        {
            if (!strcmp(typeName,boxColors[i].name))
            {
                found = 1;
        	drawBevelBox(x, y, XS-1, YS-1, boxColors[i].hcol, str);
    	    }
    	}
    	if (!found)
            drawBevelBox(x, y, XS-1, YS-1, 0xff80a0c0, str);
    }
}


#if 0
static void
drawNode(pfNode *node, float x, float y, int ishlight)
{
    /* Highlight node if it is on the pick path */
    if (pfSearch(pickPath, node) >= 0)
	ishlight = 1;

    if (ishlight == 0)
    {
	const char *typeName;
	typeName = pfGetTypeName((pfObject*) node);
	if (strncmp(typeName, "pf", 2) == 0)
	    typeName +=2;
	drawBevelBox(x, y, XS-1, YS-1, 0xff808080, typeName);
    }
    else
    {
	const char *typeName;
	char	str[512];

	typeName = pfGetTypeName((pfObject*) node);
	if (strncmp(typeName, "pf", 2) == 0)
	    typeName +=2;
	
	strcpy(str, typeName);

	if (pfGetNodeName(node) != NULL)
	{
	    strcat(str, "\"");
	    strcat(str, pfGetNodeName(node));
	    strcat(str, "\"");
	}

	/* indicate number of geosets referenced by geode */
	if (pfIsOfType(node, pfGetGeodeClassType()))
	{
	    char children[32];
	    sprintf(children, "(%d)", pfGetNumGSets((pfGeode *)node));
	    strcat(str, children);
	}

	drawBevelBox(x, y, XS-1, YS-1, 0xff80a0c0, str);
    }
}
#endif

#define pfcpack(c)	glColor3ub((c)&0xff, ((c)&0xff00) >> 8, \
				   ((c)&0xff0000) >> 16);
#define pfbgnline()	glBegin(GL_LINE_STRIP)
#define pfendline()	glEnd()
#define pfortho2(l, r, b, t)	glOrtho(l, r, b, t, -1.0f, 1000.0f)
#define pfv2f(v)	glVertex2fv(v)
#define pflinewidth(l)	glLineWidth(l)



static void
drawLink(int x0, int y0, int x1, int y1, int type) 
{
    pfVec2	from, across, down;

    pfSetVec2(from, x0, y0);

    if (type == 0)	/* Draw XY-aligned link */
    {
	/* Don't draw horizontal portion if link is strictly vertical */
	pfcpack(0xff0000);
	if (x0 == x1)
	{
	    pfSetVec2(down, x1, y1);
	    pfbgnline();
	    pfv2f(from);
	    pfv2f(down);
	    pfendline();
	}
	else if (x1 - x0 > vpWidth)
	{
	    /* Draw across, then down */
	    pfSetVec2(across, x1, y0);
	    pfSetVec2(down, x1, y1);
	    pfbgnline();
	    pfv2f(from);
	    pfv2f(across);
	    pfv2f(down);
	    pfendline();
	}
	else
	{
	    /* Draw slanted link */
	    pfSetVec2(across, x1, y1);
	    pfbgnline();
	    pfv2f(from);
	    pfv2f(across);
	    pfendline();
	}
    }
    else	/* Draw slanted link for instance links */
    {
	pfcpack(0xffff00);
	/* If instance link is horizontal, make it not so for clarity */
	if (y0 == y1)
	{
	    pfSetVec2(across, x1+XS, y1-YS);
	    pfSetVec2(down, x0-XS, y0-YS);
	    pfbgnline();
	    pfv2f(from);
	    pfv2f(down);
	    pfv2f(across);
	    pfSetVec2(across, x1, y1);
	    pfv2f(across);
	    pfendline();
	}	
	else
	{
	    pfSetVec2(across, x1, y1);
	    pfbgnline();
	    pfv2f(from);
	    pfv2f(across);
	    pfendline();
	}
    }
}

static int maxDepth, nodeCount;

static void
drawTree(void)
{
    int		i, hilight, hilightDepth;
    pfuGUINode	*node;

    if (pickPath == NULL)
	pickPath = pfNewPath();

    pfDPoolLock(GUI->pick);
    pfCopy(pickPath, GUI->pick->path);
    pfDPoolUnlock(GUI->pick);

    pfPushMatrix();

    /* Translate line endpoint to center of box */
    pfTranslate(XS/2.0f, YS/2.0f, 0.0f);

    for (i=0; i<nodeCount; i++)
    {
	int	p;
	node = nodeList + i;

	p = node->parent;
	if (p >= 0)
	{
	    if (node->instling < 0)
		drawLink(nodeList[p].x, nodeList[p].y, 
			       node->x, node->y, 0); 
	    else
	    {
		drawLink(nodeList[p].x, nodeList[p].y, 
			       nodeList[node->instling].x, 
			       nodeList[node->instling].y, 1); 
		continue;	/* Do not cull phantom instling */
	    }

	    /* Cull box to viewport. This may incorrectly cull instance link */
	    if ((node->bbox[1][0] < vpLeft  && node->bbox[0][0] < vpLeft) ||
		(node->bbox[0][0] > vpRight && node->bbox[1][0] > vpRight) ||
		(node->bbox[1][1] < vpBottom && node->bbox[0][1] < vpBottom) ||
		(node->bbox[0][1] > vpTop && node->bbox[1][1] > vpTop))
	    {
		i = node->skip - 1;
		continue;
	    }
	}
    }

    pfPopMatrix();

    hilight = 0;
    for (i=0; i<nodeCount; i++)
    {
	node = nodeList + i;

	/* Do not draw instanced node unless it's highlighted */
	if (node->instling >= 0)
	{
	    if (hilight == 1)
		drawNode(nodeList[node->instling].node, 
			 nodeList[node->instling].x,
			 nodeList[node->instling].y, 1);
	    continue;
	}

	/* Cull box to viewport */
	if ((node->bbox[1][0] < vpLeft  && node->bbox[0][0] < vpLeft) ||
	    (node->bbox[0][0] > vpRight && node->bbox[1][0] > vpRight) ||
	    (node->bbox[1][1] < vpBottom && node->bbox[0][1] < vpBottom) ||
	    (node->bbox[0][1] > vpTop && node->bbox[1][1] > vpTop))
	{
	    i = node->skip - 1;
	    continue;
	}

	/* Highlight subtree corresponding to box which has mouse focus */
	if (hilight == 0)
	{
	    if (xMouse >= node->x && xMouse <= node->x + XS && 
		yMouse >= node->y && yMouse <= node->y + YS)
	    {
		hilight = 1;
		hilightDepth = node->depth;
		GUI->hilightedNode = node->node;
	    }
	}
	else if (hilight == 1)
	{
	    /* Stop highlighting once we finish subtree */
	    if (node->depth <= hilightDepth)
		hilight = 2;
	}

	switch (hilight) {
	case 0:
	case 2:
	    drawNode(node->node, node->x, node->y, 0);
	    break;
	case 1:
	    drawNode(node->node, node->x, node->y, 1);
	    break;
	}
    }
    if (hilight == 0)
    {
	if (GUI->pick->node == NULL)
	    GUI->hilightedNode = NULL;
    }
    else if (GUI->mouse.flags & PFUDEV_MOUSE_LEFT_DOWN)
    {
	if (pfIsOfType(GUI->hilightedNode, pfGetSCSClassType()))
	    drawSCS((pfSCS*)GUI->hilightedNode, xMouse, yMouse);
	if (pfIsOfType(GUI->hilightedNode, pfGetSwitchClassType()))
	    drawSwitch((pfSwitch*)GUI->hilightedNode, xMouse, yMouse);
    }
}

static pfuGUINode	*pickGUINode = NULL;

/* 
 * Traverse tree and calculate dimensions of graph.
*/
static int
travTree(int parent, pfNode *node, pfuTraverser *trav)
{
    int		i, nodeId;
    pfuGUINode	*gnode;

    if (trav->depth > maxDepth)
	maxDepth = trav->depth;

    if (nodeCount >= MAX_NODES)
    {
	pfNotify(PFNFY_WARN, PFNFY_RESOURCE, 
		 "pfuDrawTree() Too many nodes, max of %d\n", MAX_NODES);
	return 0;
    }

    nodeId = nodeCount++;
    gnode = nodeList + nodeId;
    gnode->parent = parent;
    gnode->depth = trav->depth;
    gnode->node = node;
    gnode->instling = -1;

    /* If node is instanced, find the first instance of it on nodeList */
    if (pfGetNumParents(node) > 1 && 
	pfGetParent(node, 0) != (pfGroup*)nodeList[parent].node)
    {
	/* Linear search through previous nodes to find instance sibling */
	for (i=0; i<nodeCount-1; i++)
	     if (nodeList[i].node == node)
		 break;

	/* Found instance sibling so patch up reference and prune traversal */
	if (i != nodeCount-1)
	{
	    gnode->instling = i;

	    indentLevel += 1.5f * XS;
	    gnode->skip = nodeCount;

	    /* Prune traversal here */
	    return 1;
	}
	/* If instance sibling not found, continue normally since 
	 first parent is not in tree. */
    }

    if (pfIsOfType(node, pfGetGroupClassType()))
    {
	int	width = 0, n, maxDepthSave, pwidth;
	n = pfGetNumChildren(node);

	/* Find maxDepth under this group */
	maxDepthSave = maxDepth;
	maxDepth = -1;

	for (i=0; i<n; i++)
	{
	    trav->depth++;
	    width += travTree(nodeId, pfGetChild(node, i), trav);
	    trav->depth--;
	}

	gnode->skip = nodeCount;

	/* Place parent at center of its children */
	gnode->x = indentLevel - XS - CALCW(width)/2.0f;
	gnode->y = CALCY(trav->depth);

	pwidth = (width * 1.5f * XS - .5f*XS) * .5f;
	
	/* Set group's bounding box */
	if (n == 0)
	{
	    PFSET_VEC2(gnode->bbox[0], gnode->x, gnode->y);
	    PFSET_VEC2(gnode->bbox[1], gnode->x + XS, gnode->y + YS);
	    indentLevel += 1.5f * XS;
	}
	else
	{
	    PFSET_VEC2(gnode->bbox[0], gnode->x - pwidth, CALCY(maxDepth));
	    PFSET_VEC2(gnode->bbox[1], gnode->x + pwidth, gnode->y + YS);
	}

	if (node == GUI->pick->node)
	    pickGUINode = gnode;

	/* Restore maxDepth */
	maxDepth = PF_MAX2(maxDepth, maxDepthSave); 

	return width;
    }
    else
    {
	gnode->x = indentLevel;
	gnode->y = CALCY(trav->depth);

	/* Set node's bounding box */
        PFSET_VEC2(gnode->bbox[0], gnode->x, gnode->y);
        PFSET_VEC2(gnode->bbox[1], gnode->x + XS, gnode->y + YS);

	if (node == GUI->pick->node)
	    pickGUINode = gnode;

	indentLevel += 1.5f * XS;
	gnode->skip = nodeCount;
	return 1;
    }
}

static void
pickTree(void)
{
    pfHit	    	**hits[32];
    int			nhits;

    if (!GUI->pick->requestFlag)
	return;

    if (GUI->pick->x < 0.0f || GUI->pick->x > 1.0f || 
	GUI->pick->y < 0.0f || GUI->pick->y > 1.0f)
	return;

    nhits = pfChanPick(GUI->pick->chan,
		       PFPK_M_NEAREST|PFTRAV_IS_PRIM|PFTRAV_IS_PATH|
		       PFTRAV_LOD_CUR|PFTRAV_SW_CUR|PFTRAV_SEQ_CUR,
		       GUI->pick->x, GUI->pick->y, 
		       0.0f, hits);

    pfDPoolLock(GUI->pick);
    if (nhits)
    {
	pfPath		*path;
	int		n;

	pfQueryHit(hits[0][0], PFQHIT_PATH, &path);
	pfCopy(GUI->pick->path, path);
	n = pfGetNum(GUI->pick->path) - 1;
	GUI->pick->node = NULL;
	while (n >= 0 && 
	       !pfIsOfType(pfGet(GUI->pick->path, n), 
			   pfGetNodeClassType()))
	    n--;
	if (n >= 0)
	    GUI->pick->node = GUI->hilightedNode = 
		pfGet(GUI->pick->path, n);
    }
    else
    {
	pfResetList(GUI->pick->path);
	GUI->pick->node = NULL;
    }
    GUI->pick->requestFlag = 0;
    pfDPoolUnlock(GUI->pick);
}


void
pfuDrawTree(pfChannel *chan, pfNode *tree, pfVec3 panScale)
{
    pfuTraverser	trav;
    float		xScale, xOffset, yScale, yOffset;

    if (nodeList == NULL)
	nodeList = pfMalloc(sizeof(pfuGUINode) * MAX_NODES, NULL);

    if (!GUI)
    {
	getGUIDataPool(0);
    }
    if (FontFlags & GUI_FONT_DIRTY)
	scaleFonts(1.0f, 1);
	
    
    pfFBStateApplyBasicState();

    pfGetChanSize(chan, &xSize, &ySize);
    pfGetChanOrigin(chan, &xOrigin, &yOrigin);
    pfGetChanOutputSize(chan, &xChanSize, &yChanSize);
    pfGetChanOutputOrigin(chan, &xChanOrigin, &yChanOrigin);


    pfPushIdentMatrix();
    pfPushState();
    pfBasicState();
    pflinewidth(2);

    glViewport(xChanOrigin, yChanOrigin, xChanSize, yChanSize);
    glDepthFunc(GL_ALWAYS);
    glMatrixMode(GL_PROJECTION);
    glLoadIdentity();
    glOrtho(0.0f, xSize, 0.0f, ySize, -1.0f, 1000.0f);
    glMatrixMode(GL_MODELVIEW);
    

    pfuInitTraverser(&trav);
    indentLevel = .5f * XS;
    maxDepth = nodeCount = 0;
    pickGUINode = NULL;
    travTree(-1, tree, &trav);


    /* Pan and scale scene graph */
    if (panScale)
    {
	int 		width, height, xc, yc, dx, dy, middle;
#ifdef USE_FONT_MANAGER
	float		fontScale;
	static float 	prevScale = -1.0f;
#endif

	middle = nodeList[0].x;
	width = indentLevel;
	height = (ySize - CALCY(maxDepth));

	if (pickGUINode)
	    dx = middle - 
		.5f * (pickGUINode->bbox[1][0] + pickGUINode->bbox[0][0]);
	else
	    dx = 0.0f;

	/* Map (-1, 1) to pan completely to left and right */
	if (panScale[0] <= 0)
	    dx += -middle * panScale[0];
	else
	    dx += (middle - width) * panScale[0];

	dy = PF_ABS(height - ySize) * panScale[1];

	/* Figure out center of scene graph in order to scale it properly */
	xc = (PF_MIN2(xChanSize, width)) / 2.0f;
	if (ySize < height)
	    yc = ySize / 2.0f;
	else
	    yc = ySize - height / 2.0f;

#ifdef USE_FONT_MANAGER
	fontScale = PF_MAX2(.5f, panScale[2]);
	if (fontScale != prevScale)
	{
	    int	i;
	    prevScale = fontScale;
	    for (i=1; i<PFU_NUM_FONTS; i++)
	    {
		if (Fonts[TREE_FONTS][0].handle && (Fonts[TREE_FONTS][i].handle =
				    fmscalefont(Fonts[TREE_FONTS][0].handle, 
						prevScale*Fonts[TREE_FONTS][i].size)))
		{
		    fmgetfontinfo(Fonts[TREE_FONTS][i].handle, 
				  &(Fonts[TREE_FONTS][i].info));
		}
	    }
	}
#endif

	/* Translate back to graph center */
	pfTranslate(xc, yc, 0.0f);

	/* Scale graph */

	if (panScale[2] < 1.0f && height < ySize)
	{
	    pfScale(panScale[2], 1.0f, 1.0f);
	    yScale = 1.0f;

	}
	else
	{
	    pfScale(panScale[2], panScale[2], 1.0f);
	    yScale = panScale[2];
	}
	/* Translate graph center to origin for nice scale*/
	pfTranslate(-xc, -yc, 0.0f);
	pfTranslate(dx-middle+xSize/2.0f, dy, 0.0f);

	/* Compute scale and offsets to map from scene graph into viewport */
	xScale = panScale[2];
	xOffset = (xScale * (dx-middle+xSize/2.0f - xc))  + xc;
	yOffset = (yScale * (dy - yc))  + yc;
    }
    else
    {
	xScale = yScale = 1;
	xOffset = yOffset = 0;
    }

    xMouse = GUI->mouse.xpos - (xOrigin);
    xMouse = (xMouse - xOffset) / xScale;
    yMouse = GUI->mouse.ypos - (yOrigin);
    yMouse = (yMouse - yOffset) / yScale;

    if (GUI->mouse.modifiers & PFUDEV_MOD_ALT)
    {
	pfDPoolLock(GUI->pick);
	GUI->pick->requestFlag = 1;
	GUI->pick->x = (float)(GUI->mouse.xpos - xOrigin) / (float)xSize;
	GUI->pick->y = (float)(GUI->mouse.ypos - yOrigin) / (float)ySize; 
	GUI->pick->chan = chan;
	pfDPoolUnlock(GUI->pick);
    }
    else
    {
	/* highlight checking is after this in drawTree() so this will
	 * not clobber its result.
	 */
	GUI->hilightedNode = NULL;
    }
    
    /* Compute viewport coordinates in tree-space by inverse panning/scaling */
    vpLeft = -xOffset / xScale;
    vpBottom = -yOffset / yScale;
    vpRight = vpLeft + xSize / xScale;
    vpTop = vpBottom + ySize / yScale;
    vpWidth = vpRight - vpLeft;
	
    drawTree();

    pfPopMatrix();
    pfPopState();
    glDepthFunc(GL_LEQUAL);
}

int
preWrapper(pfTraverser *trav, void *data)
{
    pfList		*list = (pfList*)data;
    pfNodeTravFuncType	pre;
    
    pre = (pfNodeTravFuncType)pfGet(list, 0);

    (*_pfuGUIPreHlight)(trav, NULL);

    if (pre)
	(*pre)(trav, pfGet(list, 2));

    return PFTRAV_CONT;
}

int
postWrapper(pfTraverser *trav, void *data)
{
    pfList		*list = (pfList*)data;
    pfNodeTravFuncType	post;
    
    post = (pfNodeTravFuncType)pfGet(list, 1);

    (*_pfuGUIPostHlight)(trav, NULL);

    if (post)
	(*post)(trav, pfGet(list, 2));

    return PFTRAV_CONT;
}


void
pfuUpdateGUI(pfuMouse *mouse)
{
    int            j;
    int            in;
    int            x, y;
    pfuWidget       *w = 0;
    int             haveWidget = 0, havePanel = 0;
    static pfNode   *oldHilightedNode = NULL;

    if (GUI == NULL)
	return;

    if (!GUI)
	getGUIDataPool(0);

    pickTree();

    /* De-hilight old node and hilight current one, if any */
    if (GUI->hilightedNode != oldHilightedNode)
    {
	pfNodeTravFuncType	pre, post;
	static pfList	        *list = NULL;
	    
	/* De-highlight old node */
	if (oldHilightedNode != NULL)
	{
	    /* Restore original callbacks if necessary */
	    pfGetNodeTravFuncs(oldHilightedNode, PFTRAV_DRAW, &pre, &post);
	    if (pre == preWrapper)
	    {
		list = (pfList*) 
		    pfGetNodeTravData(oldHilightedNode, PFTRAV_DRAW);

		pfNodeTravFuncs(oldHilightedNode, PFTRAV_DRAW, 
				(pfNodeTravFuncType) pfGet(list, 0),
				(pfNodeTravFuncType) pfGet(list, 1));
		pfNodeTravData(oldHilightedNode, PFTRAV_DRAW, pfGet(list, 2));
	    }
	    else
		pfNodeTravFuncs(oldHilightedNode, PFTRAV_DRAW, NULL, NULL);
	}

	/* Highlight current node, if any */
	if (GUI->hilightedNode != NULL)
	{
	    pfGetNodeTravFuncs(GUI->hilightedNode, PFTRAV_DRAW, &pre, &post);

	    /* Don't clobber pre-existing DRAW callbacks */
	    if (pre || post)
	    {
		pfNodeTravFuncs(GUI->hilightedNode, PFTRAV_DRAW, 
				preWrapper, postWrapper);
		if (!list)
		    list = pfNewList(sizeof(void*), 3, pfGetSharedArena());

		pfResetList(list);
		pfAdd(list, (void *)pre);
		pfAdd(list, (void *)post);
		pfAdd(list, 
		      pfGetNodeTravData(GUI->hilightedNode, PFTRAV_DRAW)); 

		pfNodeTravData(GUI->hilightedNode, PFTRAV_DRAW, list); 
	    }
	    else
	    {
		pfNodeTravFuncs(GUI->hilightedNode, PFTRAV_DRAW, 
				_pfuGUIPreHlight, _pfuGUIPostHlight);
	    }
	}

	oldHilightedNode = GUI->hilightedNode;
    }

    /* Highlighting code needs mouse */
    GUI->mouse = *mouse;

    if (!GUI->active && ! GUI->selectedWidget)
	return;

    /* make sure we have up-to-date info in win size and pos */
    updateGUIViewport(0);

    in = (mouse->inWin && 
	((mouse->inWin == 1) || (mouse->inWin == pfGetPWinWSWindow(GUI->pwin))) &&
	pfuInGUI(mouse->xpos, mouse->ypos));
    
    if (in || GUI->selectedWidget)
	GUI->cursor = PFU_CURSOR_GUI;
    else
	GUI->cursor = PFU_CURSOR_MAIN;
    pfuUpdateGUICursor();

    /* If there is no selected widget and no mouse down, leave */
    if (!GUI->selectedWidget &&
        (!(in) || !(mouse->flags & PFUDEV_MOUSE_DOWN_MASK) ||
	 !pfuInGUI(mouse->clickPosLast[0], mouse->clickPosLast[1])))
    {
	return;
    }
    else
    {
	/* Map mouse from window coords into panel coords */
	x = MAP_XMOUSE(mouse->xpos);
	y = MAP_YMOUSE(mouse->ypos);

	/*
	 * Only sliders keep active focus outside the widget, but non sliders
	 * still need to be cleared if they still have focus but aren't
	 * active.
	*/
	if (GUI->selectedPanel)
	{
	    j = GUI->selectedPanel->id;
	}
	else
	    j = 0;

	while (j < GUI->numPanels)
	{
	    if (!GUI->panels[j]->active)
	    {
		GUI->selectedWidget = 0;
		GUI->selectedPanel = 0;
		j++;
		continue;
	    }

	    /* See if mouse focus is still in selected widget */
	    if (!GUI->selectedWidget)
	    {
		pfuWidget         *wclick;
		wclick = scanPanel((pfuPanel *) GUI->panels[j],
				   MAP_XMOUSE(mouse->clickPosLast[0]),
				   MAP_YMOUSE(mouse->clickPosLast[1]),
				   GUI->orthoOrgY);
		w = scanPanel((pfuPanel *) GUI->panels[j], x, y, GUI->orthoOrgY);
		if (w != wclick)
		{
		    w = 0;
		}
		else if (w && w->selectFunc)
		{
		    w = (w->selectFunc)(w,  GUI->panels[j]);
		}
	    }
	    else
	    {
		w = GUI->selectedWidget;
	    }
	    if (w == NULL)
	    {
		j++;
		continue;
	    }

	    haveWidget = 0;

#define CALLFUNC(w)	{					\
	if ((w)->actionFunc) 	{					\
	    /* Unlock widget so callback doesn't double-trip */	\
	    pfDPoolUnlock(w); 					\
	    (*((w)->actionFunc))(w);					\
	    pfDPoolLock(w); 					\
	}}

	    /* Process selected widget. */

	    /*
	     * Lock widget. Must take care not to double-trip on this lock
	     * in the widget callback.
	    */
	    pfDPoolLock(w);
	    switch (w->type & PFUGUI_TYPE_MASK)
	    {
	    case PFUGUI_SLIDER:
		{
		    Slider         *s =(Slider *) w;
		    if (mouse->flags & PFUDEV_MOUSE_LEFT_DOWN)
		    {
			if (s->hvFlag)	    /* horizontal */
			{
			    s->sval = (((float) x - s->start) *
				   s->scale) + s->smin;
			}
			else		    /* vertical */
			{
			    s->sval = (((float) s->start - y) *
				   s->scale) + s->smin;
			}
			if (s->sval < s->smin)
			    s->sval = s->smin;
			if (s->sval > s->smax)
			    s->sval = s->smax;

			/* Make val stick at default */
			if ((w->mode & STICKY_DFT) &&
				PF_ABSLT(s->sval - s->sDefaultVal, 10 * (s->scale)))
			    s->sval = s->sDefaultVal;

			if (s->type == PFUGUI_SLIDER)
			    s->val = s->sval;
			else if (s->type == PFUGUI_SLIDER_LOG)
			    s->val = powf(10.0f, s->sval);

			CALLFUNC(w);

			s->dirty = GUI->refresh;
			GUI->selectedWidget = (pfuWidget *) s;
			GUI->selectedPanel = (pfuPanel *) GUI->panels[j];
			haveWidget = 1;
		    }
		    else 
		    if (!(mouse->flags & PFUDEV_MOUSE_LEFT_DOWN))
		    {
			s->oldVal = s->val;
			s->sOldVal = s->sval;
			s->dirty = GUI->refresh;
			haveWidget = 1;
			if (GUI->selectedWidget)
			{
			    GUI->selectedWidget = 0;
			    GUI->selectedPanel = 0;
			}
		    }
		    break;
		}
	    case PFUGUI_BUTTON:
		if (mouse->flags & PFUDEV_MOUSE_LEFT_DOWN)
		{
		    if (!w->state)
		    {
			w->style = PFUGUI_HILIGHT;
			w->val = 1.0f;
			w->state = 1;
			w->dirty = GUI->refresh;
			GUI->selectedWidget = w;
			GUI->selectedPanel = (pfuPanel *) GUI->panels[j];
		    }
		    haveWidget = 1;
		}
		else 
		if (w->state == 2)
		{
		    w->val = 0.0f;
		    CALLFUNC(w);
		    w->state = 0;
		    w->style = PFUGUI_NORMAL;
		    w->dirty = GUI->refresh;
		    if (GUI->selectedWidget)
		    {
			GUI->selectedWidget = 0;
			GUI->selectedPanel = 0;
		    }
		    haveWidget = 1;
		}
		break;

	    case PFUGUI_SWITCH:
		if (mouse->flags & PFUDEV_MOUSE_LEFT_DOWN)
		{
		    if (!w->state)
		    {
			if (w->val != w->min)
			    w->val = w->min;
			else
			    w->val = w->max;
			CALLFUNC(w);
			w->state = 1;
			w->dirty = GUI->refresh;
			GUI->selectedWidget = w;
			GUI->selectedPanel = (pfuPanel *) GUI->panels[j];
		    }
		    haveWidget = 1;
		}
		else 
		if (w->state)
		{
		    w->state = 0;
		    w->dirty = GUI->refresh;
		    if (GUI->selectedWidget)
		    {
			GUI->selectedWidget = 0;
			GUI->selectedPanel = 0;
		    }
		    haveWidget = 1;
		}
		break;

	    case PFUGUI_MENU_BUTTON:
		{
		    MenuButton     *m =(MenuButton *) w;
		    if (mouse->flags & PFUDEV_MOUSE_LEFT_DOWN)
		    {
			if (!m->state)
			{
			    m->sel = (m->sel + 1) % m->numSelections;
			    m->val = m->selectionList[m->sel].val;

			    if (m->selectionList[m->sel].actionFunc != NULL)
			    {
				/* Unlock widget so callback doesn't double-trip */
			    	pfDPoolUnlock(w);
				(*(m->selectionList[m->sel].actionFunc)) ((pfuWidget *) m);
			    	pfDPoolLock(w);
			    }
			    else 
			    if (m->actionFunc != NULL)
			    {
				/* Unlock widget so callback doesn't double-trip */
			    	pfDPoolUnlock(w);
				(*(m->actionFunc)) ((pfuWidget *) m);
			    	pfDPoolLock(w);
			    }
			    m->state = 1;
			    m->dirty = GUI->refresh;
			    GUI->selectedWidget = (pfuWidget *) m;
			    GUI->selectedPanel = (pfuPanel *) GUI->panels[j];
			}
			haveWidget = 1;
		    }
		    else 
		    if (mouse->flags & PFUDEV_MOUSE_RIGHT_DOWN)
		    {	/* do menu - happens in draw process */
			if (!(GUI->dopup) && !(m->state))
			{
			    GUI->dopup = m;
			    GUI->selectedWidget = (pfuWidget *) m;
			    GUI->selectedPanel = (pfuPanel *) GUI->panels[j];
			}
			haveWidget = 1;
		    }
		    else 
		    if (m->state == 1)
		    { /* menu was selected in draw - call widget function */
			if (m->selectionList[m->sel].actionFunc != NULL)
			    (*(m->selectionList[m->sel].actionFunc)) ((pfuWidget *) m);
			else 
			if (m->actionFunc != NULL)
			    (*(m->actionFunc)) ((pfuWidget *) m);
			m->state = 0;
			m->dirty = GUI->refresh;
			haveWidget = 1;
			GUI->dopup = NULL;
		    }
		    else 
		    if (!(GUI->dopup))
		    { /* was a left-mouse menu select or a finished menu
		       * select - drew it pushed, now pop it back out and
		       * release widget.
		       */
			m->state = 0;
			m->dirty = GUI->refresh;
			if (GUI->selectedWidget)
			{
			    GUI->selectedWidget = 0;
			    GUI->selectedPanel = 0;
			}
			haveWidget = 1;
		    }
		    break;
		}
	    case PFUGUI_RADIO_BUTTON:
		{
		    RadioButton    *r =(RadioButton *) w;
		    if (mouse->flags & PFUDEV_MOUSE_LEFT_DOWN)
		    {
			int             index;

			/* find out where the mouse is */
			index = (int)scanSelections(r,
					  MAP_XMOUSE(mouse->clickPosLast[0]),
					  MAP_YMOUSE(mouse->clickPosLast[1]),
					  GUI->orthoOrgY);
			if (!r->state)
			{
			    if (index == -1)
			    {	/* have title - do switch */
				if (!r->on)
				{
				    r->on = 1;
				    r->val = r->selectionList[r->sel].val;
				}
				else
				    r->on = 0;
				r->state = 1;
			    }
			    else 
			    if (index > -1)
			    {	/* changing selection selection - also turn on */
				r->on = 1;
				r->sel = index;
				r->val = r->selectionList[index].val;
				r->state = 1;
			    }
			    if (r->selectionList[r->sel].actionFunc != NULL)
			    {
				/* Unlock widget so callback doesn't double-trip */
			    	pfDPoolUnlock(w);
				(*(r->selectionList[r->sel].actionFunc)) ((pfuWidget *) r);
			    	pfDPoolLock(w);
			    }
			    else 
			    if (r->actionFunc != NULL)
			    {
				/* Unlock widget so callback doesn't double-trip */
			    	pfDPoolUnlock(w);
				(*(r->actionFunc)) ((pfuWidget *) r);
			    	pfDPoolLock(w);
			    }
			    r->dirty = GUI->refresh;
			    GUI->selectedWidget = (pfuWidget *) r;
			    GUI->selectedPanel = (pfuPanel *) GUI->panels[j];
			}
			haveWidget = 1;
		    }
		    else 
		    if (r->state || (r->sel != r->oldSel))
		    {
			r->state = 0;
			r->oldSel = r->sel;
			r->dirty = GUI->refresh;
			if (GUI->selectedWidget)
			{
			    GUI->selectedWidget = 0;
			    GUI->selectedPanel = 0;
			}
			haveWidget = 1;
		    }
		    break;
		}
	    case PFUGUI_MESSAGE:
		break;
	    default:
		break;
	    }
	    pfDPoolUnlock(w);
	    /* can only have one active widget */
	    if (haveWidget)
	    {
		GUI->panels[j]->dirty = GUI->refresh;
		havePanel = 1;
		break;
	    }

	    j++;	/* go to next panel */
	}
	if (havePanel)
	{
	    pfDPoolLock(GUI->flags);
	    GUI->flags->dirty = GUI->refresh;
	    pfDPoolUnlock(GUI->flags);
	}

	return;
    }
}

/* cull callback stub */
/*ARGSUSED*/
void pfuCullGUI(pfChannel * ch, void *data)
{
}

/*
 * Dirty Bit System for updating the GUI Disply
 * --------------------------------------------
 *  GUI->redrawAll: The big ugly dirty bit - everthing is redrawn from scratch.
 *  GUI->dirty:	    There is at least one dirty widget somewhere....
 *  panel->dirty:   There is at least one dirty widget somewhere....
 *  widget->dirty:  This widget needs to be redrawn
 *
*/
/*ARGSUSED*/
void
pfuDrawGUI(pfChannel * ch, void *data)
{
    static int     drawInited = 0;
    static int     oldxs=-1, oldys=-1;

    pfPipe	    *p;
    int            i, redraw;
    double          startTime, endTime;

    /* Initialize stuff the first time through */
    if (!drawInited)
    {
	int depth;
	pfQuerySys(PFQSYS_MAX_DBL_RGB_BITS, &depth);

	if (depth < 4)
	{
	    PFSET_VEC4(ClearColor, 0.05f, 0.2f, 0.3f, 1.0f);
	    PFSET_VEC3(BackColor, 0.22f, 0.33f, 0.6f);
	    PFSET_VEC3(OnColor, 1.0f, 0.0f, 0.0f);
	    PFSET_VEC3(OffColor, 0.4f, 0.1f, 0.1f);
	    PFSET_VEC3(SliderColor, 0.4f, 0.1f, 0.1f);
	    PFSET_VEC3(ValColor, 0.9f, 0.9f, 0.8f);
	    PFSET_VEC3(TextColor, 0.1f, 0.05f, 0.1f);
	    PFSET_VEC3(MessageColor, 0.6f, 0.2f, 0.2f);
	    PFSET_VEC3(InactiveSliderColor, 0.45f, 0.4f, 0.4f);
	    PFSET_VEC3(InactiveTextColor, 0.5f, 0.5f, 0.4f);
	}
	else
	{
	    PFSET_VEC4(ClearColor, 0.15f, 0.15f, 0.2f, 1.0f);
	    PFSET_VEC3(BackColor, 0.36f, 0.36f, 0.46f);
	    PFSET_VEC3(OnColor, 1.0f, 0.0f, 0.0f);
	    PFSET_VEC3(OffColor, 0.4f, 0.1f, 0.1f);
	    PFSET_VEC3(SliderColor, 0.4f, 0.1f, 0.1f);
	    PFSET_VEC3(ValColor, 0.9f, 0.9f, 0.85f);
	    PFSET_VEC3(TextColor, 0.1f, 0.05f, 0.1f);
	    PFSET_VEC3(MessageColor, 0.6f, 0.2f, 0.2f);
	    PFSET_VEC3(InactiveSliderColor, 0.45f, 0.4f, 0.4f);
	    PFSET_VEC3(InactiveTextColor, 0.45f, 0.5f, 0.5f);
	}
	
	if (!GUI)
	{
	    /* Attatch to the gui data pool created by pfuInitGUI */
	    GUI = getGUIDataPool(0);
	}
	if (GUI == NULL)
	    return;
	

	drawInited = 1;
    }

    p = pfGetChanPipe(ch);

    /* Make sure we draw GUI only on the proper pfPipe. */
    if (p != GUI->pipe && !pfGetHyperpipe(p))
	return;

    /* check states that require redrawing the GUI:
     * window size, position
     */
    if (GUI->wxs != oldxs || GUI->wys != oldys)
    {
	updateGUIViewport(0);

	oldxs = GUI->wxs;
	oldys = GUI->wys;
    }

    /* Compute scale and translation needed to fit widgets into GUI */
    if (GUI->flags->fitDirty)
	fitWidgets();
    if ((FontFlags & GUI_FONT_DIRTY) || 
	    (GUI->fontScale < 0) ||
	    (PF_ABSGE(GUIFontScale - GUI->fontScale, 0.05f)))
	scaleFonts(PF_MIN2(GUI->scaleX, GUI->scaleY), 1);

    /* Return if nothing needs to be redrawn */
    pfDPoolLock(GUI->flags);
    if ((GUI->flags->dirty <= 0) && (GUI->flags->redrawAll <= 0))
    {
	pfDPoolUnlock(GUI->flags);
	return;
    }

    startTime = pfGetTime();


    /* Deal with popup menus in draw process
     * NOTE: this is asynchronous from the handling of the menu
     * values in the case of X input handling!
     */
    if (GUI->dopup && pfGetHyperpipe(GUI->pipe) == 0)
    {
	long		t;
	MenuButton 	*m = (MenuButton *)GUI->dopup;
    	pfDPoolLock(m);
	if (!m->state)
	{ /* menu has not yet been popped/selected */
	    t = dopup(m->menu);
	    if (t > -1)
	    {
		m->sel = (int)t - 1;
		m->val = m->selectionList[m->sel].val;
		m->state = 1;
	    }
	}
	GUI->dopup = NULL;
	pfDPoolUnlock(m);
    }

    redraw = GUI->flags->redrawAll;

    /* Decrement dirty and redraw values */
    if (GUI->flags->redrawAll > 0)
	GUI->flags->redrawAll--;
    if (GUI->flags->dirty > 0)
	GUI->flags->dirty--;

    pfDPoolUnlock(GUI->flags);

    if (redraw > 0)
	/* clear before setting up state, since pfClear
	 * may muck with dither
	 */
	pfClear(PFCL_COLOR, ClearColor);

    /* Draw GUI */
    pfPushState();
    pfBasicState();
    glDepthFunc(GL_ALWAYS);
    glMatrixMode(GL_PROJECTION);
    glLoadIdentity();
    glOrtho(0.0f, GUI->pixSizeX, GUI->orthoOrgY, GUI->orthoMaxY,
		-1.0f, 1.0f);
    glMatrixMode(GL_MODELVIEW);
    glDisable(GL_DITHER);
    pfPushIdentMatrix();

    /* Draw border of GUI only if we're redrawing everything */
    if (redraw > 0)
    {
	static pfVec2   coord[4];
	float	b = PFUGUI_PANEL_BORDER * PF_MIN2(GUI->scaleX, GUI->scaleY);
	PFSET_VEC2(coord[0], b, GUI->orthoOrgY + b - 0.5f);
	PFSET_VEC2(coord[1], GUI->pixSizeX - 5.0f, GUI->orthoOrgY + b - 0.5f);
	PFSET_VEC2(coord[2], GUI->pixSizeX - 5.0f, GUI->orthoMaxY - b - 0.5f);
	PFSET_VEC2(coord[3], b, GUI->orthoMaxY - b - 0.5f);
	drawBevel(coord, ClearColor, b);
	drawBevel(coord, ClearColor, -b * 0.5f);
    }
    /* Scale, then translate widgets so they all fit in panel */
    pfTranslate(GUI->transX, GUI->transY, 0.0f);
    pfScale(GUI->scaleX, GUI->scaleY, 1.0f);


    for (i = 0; i < GUI->numPanels; i++)
	drawPanel(GUI->panels[i], (redraw > 0));
    glDepthFunc(GL_LEQUAL);
    glEnable(GL_DITHER);
    pfPopMatrix();
    pfPopState();

    endTime = pfGetTime();
    GUI->guiDrawTime += (endTime - startTime);
    GUI->nGUIDraws++;

}

    /*----------------------------- pfuPanel ------------------------------*/

pfuPanel*
pfuNewPanel(void)
{
    pfuPanel          *p;

    if (!GUI)
	getGUIDataPool(0);

    if (GUI->numPanels >= PFUGUI_MAXPANELS)
    {
	pfNotify(PFNFY_FATAL, PFNFY_RESOURCE,
		 "NewpfuPanel: too many panels.");
	return (NULL);
    }
    p = (pfuPanel *) pfDPoolAlloc(GUIDP, sizeof(pfuPanel),
			       (PFUGUI_PANELS_DPID + GUI->numPanels));

    memset( p, 0, sizeof( pfuPanel ) );

    if (!p)
    {
	pfNotify(PFNFY_FATAL, PFNFY_RESOURCE,
		 "NewPanel: failed to allocate panel in gui data pool.");
    }
    p->numWidgets = 0;
    p->listSize = 16;
    p->active = 1;
    p->id = GUI->numPanels;
    GUI->panels[GUI->numPanels] = p;
    GUI->numPanels++;
    p->dirty = GUI->refresh;

    return (p);
}

/*ARGSUSED*/
void
pfuGetPanelOriginSize(pfuPanel * p, float *xo, float *yo, float *xs, float *ys)
{
    float           l, r, t, b;
    /* for now, the panel size is fixed, so this api is for later */
    /* XXX - GUI->panWIN X and Y are in screen coords for the in-out tests so
     * they need to be converted to channel coords where. probably should
     * store channel coords
     */
    /*(p, p);*/

    if (!GUI)
	getGUIDataPool(0);

    pfGetChanViewport(GUI->channel, &l, &r, &b, &t);
    *xo = l * GUI->pixSizeX + PFUGUI_PANEL_FRAME;
    *yo = b * GUI->pixSizeY + PFUGUI_PANEL_FRAME;
    *xs = GUI->pixSizeX - 2*PFUGUI_PANEL_FRAME;
    *ys = GUI->pixSizeY - 2*PFUGUI_PANEL_FRAME;
}

void
pfuEnablePanel(pfuPanel * p)
{
    if (!GUI)
	getGUIDataPool(0);
  
    if (p != NULL)
    {
	pfDPoolLock(p);
	p->dirty = GUI->refresh;
	p->active = 1;
	pfDPoolUnlock(p);
	
	pfDPoolLock(GUI->flags);
	GUI->flags->dirty = GUI->flags->redrawAll = GUI->refresh;
	pfDPoolUnlock(GUI->flags);
  
    }
    else
	pfNotify(PFNFY_WARN, PFNFY_USAGE, "pfuEnablePanel() NULL panel.");
}


void
pfuDisablePanel(pfuPanel * p)
{
    if (!GUI)
	getGUIDataPool(0);

    if (p != NULL)
    {
	pfDPoolLock(p);
	p->dirty = GUI->refresh;
	p->active = 0;
	pfDPoolUnlock(p);
	
	pfDPoolLock(GUI->flags);
	GUI->flags->dirty = GUI->flags->redrawAll = GUI->refresh;
	pfDPoolUnlock(GUI->flags);
    }
    else
	pfNotify(PFNFY_WARN, PFNFY_USAGE, "pfuDisablePanel() NULL panel.");
}

void
pfuResetPanel(pfuPanel *p)
{
    static int	 inReset = 0;
    int		 j;
    pfuWidget	*w;

    if (inReset)
	return;

    if (!GUI)
	getGUIDataPool(0);

    inReset = 1;

    p->dirty = GUI->refresh;
    for (j = 0; j < p->numWidgets; j++)
    {
	w = p->widgets[j];
	if (w)
	    resetWidget(w);
    }
    pfDPoolLock(GUI->flags);
    GUI->flags->dirty = GUI->refresh;
    pfDPoolUnlock(GUI->flags);
    
    inReset = 0;
}

static void
drawPanel(pfuPanel *p, int redraw)
{
    pfuWidget      *w;
    int            i;

    if (GUI == NULL)
	return;

    pfDPoolLock(p);

    /* Return if panel is not dirty and GUI does not need redrawing */
    if ((!p->active) || (p->dirty <= 0 && !redraw))
    {
	pfDPoolUnlock(p);
	return;
    }
    p->dirty--;
    pfDPoolUnlock(p);

    /* Draw only dirty widgets in panel or all widgets is GUI needs redraw */
    for (i = 0; i < p->numWidgets; i++)
    {
	w = p->widgets[i];

	pfDPoolLock(w);
	if (redraw || (w->dirty > 0))
	{
	    drawWidget(w, p);
	    w->dirty--;
	}
	pfDPoolUnlock(w);
    }
}

static pfuWidget*
scanPanel(pfuPanel *p, int x, int y, int ymin)
{
    int            i;
    pfuWidget      *w;

    if (p == NULL)
	return (NULL);
    for (i = 0; i < p->numWidgets; i++)
    {
	w = (pfuWidget *) p->widgets[i];
	if (!(w->hidden) && (w->active) && x >= w->xorg &&
	    x <= (w->xorg + w->xsize))
	    if (y >= (w->yorg - ymin)
		&& y <= ((w->yorg - ymin) + w->ysize))
	    {
		return (w);
	    }
    }
    return (NULL);
}

	/*-------------------------- pfuWidget -------------------------*/

pfuWidget*
pfuNewWidget(pfuPanel * p, int type, int id)
{
    pfuWidget         *w;
    int            dpid;

    if (!GUI)
	getGUIDataPool(0);

    if (p)
    {
	pfDPoolLock(p);
	if (p->numWidgets >= PFUGUI_MAXWIDGETS)
	{
	    pfNotify(PFNFY_FATAL, PFNFY_RESOURCE,
		     "NewpfuWidget: too many widgets.");
	    pfDPoolUnlock(p);
	    return (NULL);
	}
	dpid = (PFUGUI_WIDGETS_DPID | (p->numWidgets));
    }
    else
    {
	dpid = id;
    }
    w = (pfuWidget *) pfDPoolAlloc(GUIDP, MAX_WIDGET_SIZE, dpid);

    memset( w, 0, MAX_WIDGET_SIZE );
    
    if (!w)
    {
	pfNotify(PFNFY_FATAL, PFNFY_RESOURCE,
    "NewpfuWidget: failed to allocate widget %d of type 0x%x in gui data pool.",
		 id, type);
	return NULL;
    }

    if (p)
    {
	p->widgets[p->numWidgets] = w;
	p->numWidgets++;
	pfDPoolUnlock(p);

	w->panel = p;
    }
    initNewWidget(w, type, id);

    dirtyWidget(w);

    return (w);
}

pfuWidget*
pfuNewWidgetBlock(pfuPanel* p, int id, int type, int num)
{
    pfuWidget         *w;
    int            dpid, i;

    if (!GUI)
	getGUIDataPool(0);

    if (p)
    {
	pfDPoolLock(p);
	if (p->numWidgets >= PFUGUI_MAXWIDGETS)
	{
	    pfNotify(PFNFY_FATAL, PFNFY_RESOURCE,
		     "pfuNewWidgetBlock() Too many widgets.");
	    pfDPoolUnlock(p);
	    return (NULL);
	}
	dpid = (PFUGUI_WIDGETS_DPID | (p->numWidgets));
    }
    else
    {
	dpid = id;
    }
    w = (pfuWidget *) pfDPoolAlloc(GUIDP, num * MAX_WIDGET_SIZE, dpid);

    if (!w)
    {
	pfNotify(PFNFY_FATAL, PFNFY_RESOURCE,
		 "pfuNewWidgetBlock() failed to allocate %d widgets %d "
		 "of type 0x%x in gui data pool.", num, id, type);

	return NULL;
    }

    memset( w, 0, num * MAX_WIDGET_SIZE );

    if (p)
    {
	for (i = 0; i < num; i++)
	{
	    p->widgets[p->numWidgets] = w;
	    p->numWidgets++;
	    w[i].panel = p;
	}
	pfDPoolUnlock(p);
    }

    for (i = 0; i < num; i++)
    {
	initNewWidget(&w[i], type, i);
    	dirtyWidget(w);
    }

    return (w);
}


static void
initNewWidget(pfuWidget * w, int type, int id)
{
    w->id = id;
    w->mode = 0;
    w->active = 1;
    w->hidden = 0;
    w->style = PFUGUI_NORMAL;
    w->type = type & PFUGUI_TYPE_VALS;
    w->min = 0.0f;
    w->max = 1.0f;
    w->xsize = 0.0f;
    w->ysize = 0.0f;
    w->drawFunc = NULL;
    w->selectFunc = NULL;
    w->actionFunc = NULL;
    
    switch (w->type & PFUGUI_TYPE_MASK)
    {
    case PFUGUI_SLIDER:
	{
	    Slider    *s =(Slider *) w;
	    s->hvFlag = !(type & PFUGUI_VERTICAL);
	    w->mode = STICKY_DFT;
	}
	break;
    case PFUGUI_SWITCH:
	w->min = 0.0f;
	w->val = w->defaultVal = 0.0f;
	w->max = 1.0f;
	break;
    case PFUGUI_MENU_BUTTON:
	{
	    MenuButton *m = (MenuButton *)w;
	    m->defaultSel = 0;
	}
	break;
    case PFUGUI_RADIO_BUTTON:
	{
	    RadioButton    *r =(RadioButton *) w;
	    r->hvFlag = !(type & PFUGUI_VERTICAL);
	    r->toggle = (type & PFUGUI_TYPE_VALS) == PFUGUI_RADIO_BUTTON_TOGGLE;
	    r->defaultSel = 0;
	    if (!(r->toggle))
	    {
		r->on = r->defaultOn = 1;
	    }
	    else
	    {
		r->on = r->defaultOn = 0;
	    }
	    break;
	}
    default:
	break;
    }
}

void
pfuEnableWidget(pfuWidget *w)
{
    pfDPoolLock(w);
    w->active = 1;
    pfDPoolUnlock(w);

    dirtyWidget(w);
}

void
pfuDisableWidget(pfuWidget *w)
{
    pfDPoolLock(w);
    w->active = 0;
    pfDPoolUnlock(w);

    dirtyWidget(w);
}

void
pfuHideWidget(pfuWidget *w)
{
    pfDPoolLock(w);
    w->hidden = 1;
    pfDPoolUnlock(w);
    
    pfDPoolLock(GUI->flags);
    GUI->flags->fitDirty = 1;
    pfDPoolUnlock(GUI->flags);

    dirtyWidget(w);
}

void
pfuUnhideWidget(pfuWidget *w)
{
    pfDPoolLock(w);
    w->hidden = 0;
    pfDPoolUnlock(w);

    pfDPoolLock(GUI->flags);
    GUI->flags->fitDirty = 1;
    pfDPoolUnlock(GUI->flags);

    dirtyWidget(w);
}

void
pfuResetWidget(pfuWidget *w)
{
    resetWidget(w);
}

static void
resetWidget(pfuWidget *w)
{
    switch (w->type & PFUGUI_TYPE_MASK)
    {
    case PFUGUI_SLIDER:
    case PFUGUI_SWITCH:
	pfuWidgetValue(w, w->defaultVal);
	break;
    case PFUGUI_MENU_BUTTON:
	pfuWidgetSelection(w, ((MenuButton *)w)->defaultSel);
	break;
    case PFUGUI_RADIO_BUTTON:
	pfuWidgetSelection(w, ((RadioButton *)w)->defaultSel);
	pfuWidgetOnOff(w, ((RadioButton *)w)->defaultOn);
	break;
    default: break;
    }
    if (((w->type & PFUGUI_TYPE_MASK) != PFUGUI_BUTTON) && w->actionFunc)
    {
	(*(w->actionFunc)) (w);
    }
}

#if 0
static int
getWidgetSize(int type)
{
    switch(type & PFUGUI_TYPE_MASK)
    {
    case PFUGUI_SLIDER:
	return sizeof(Slider);
    case PFUGUI_RADIO_BUTTON:
	return sizeof(RadioButton);
    case PFUGUI_MENU_BUTTON:
	return sizeof(MenuButton);
    default:
	return sizeof(pfuWidget);
    }
}
#endif

void
pfuWidgetDim(pfuWidget * w, int xo, int yo, int xs, int ys)
{
    pfDPoolLock(w);
    widgetDim(w, xo, yo, xs, ys);
    positionWidgetAttrs(w);
    pfDPoolUnlock(w);
    dirtyWidget(w);
}

void
pfuGetWidgetDim(pfuWidget * w, int *xo, int *yo, int *xs, int *ys)
{
    *xo = w->xorg;
    *yo = w->yorg;
    *xs = w->xsize;
    *ys = w->ysize;
}

static void
widgetDim(pfuWidget * w, int xo, int yo, int xs, int ys)
{
    if ((xs != w->xsize) || (ys != w->ysize))
    {
	pfDPoolLock(GUI->flags);
	GUI->flags->fitDirty = 1;
	pfDPoolUnlock(GUI->flags);
    }
    
    w->xorg = xo;
    w->yorg = yo;
    w->xsize = xs;
    w->ysize = ys;

    /* set the widget coords */
    PFSET_VEC2(w->coord[0], xo, yo);
    PFSET_VEC2(w->coord[1], xo + xs, yo);
    PFSET_VEC2(w->coord[2], xo + xs, yo + ys);
    PFSET_VEC2(w->coord[3], xo, yo + ys);

    /* set the state indicator coords */
    if (w->type & PFUGUI_BUTTON)
    {
	PFSET_VEC2(w->icoord[0], xo + xs - 16, yo + 8);
	PFSET_VEC2(w->icoord[1], xo + xs - 8, yo + 8);
	PFSET_VEC2(w->icoord[2], xo + xs - 8, yo + 16);
	PFSET_VEC2(w->icoord[3], xo + xs - 16, yo + 16);
    }
    w->tx = (xo + 8);
    w->ty = ((yo + ys) - 16);

    switch (w->type & PFUGUI_TYPE_VALS)
    {
    case PFUGUI_SLIDER:
    case PFUGUI_SLIDER_LOG:
	{
	    Slider         *s =(Slider *) w;

	    if (s->hvFlag)  /* horizontal */
	    {
		/* slider bar */
		s->lt[0] = s->xorg + 16;
		s->lt[1] = s->ty - 4;
		s->lb[0] = s->xorg + 16;
		s->lb[1] = s->yorg + 4;
		s->rt[0] = (s->xorg + s->xsize) - 16;
		s->rt[1] = s->lt[1];
		s->rb[0] = (s->xorg + s->xsize) - 16;
		s->rb[1] = s->lb[1];

		s->start = s->lt[0];
		if (s->rt[0] - s->lt[0] > 0.0f)
		    s->scale = (s->smax - s->smin) / (s->rt[0] - s->lt[0]);
	    }
	    else    /* vertical */
	    {
		int width = s->xsize / 3;
		if (s->nameWidth)
		    s->tx = s->xorg + (s->xsize - s->nameWidth) * 0.5f;
		if (s->minStrWidth)
		    s->minx = s->xorg + (s->xsize - s->minStrWidth) * 0.5f;
		if (s->maxStrWidth)
		    s->maxx = s->xorg + (s->xsize - s->maxStrWidth) * 0.5f;
		s->ty = ((yo + ys) - 16); /* min string below title */
		/* slider coords */
		s->lt[0] = s->xorg + width;
		s->rt[0] = (s->xorg + s->xsize) - width;
		s->lb[0] = s->lt[0];
		s->rb[0] = s->rt[0];
		s->lt[1] = s->ty - 32;
		s->lb[1] = s->yorg + 20; /* leave room for max string at bottom */

		s->rt[1] = s->lt[1];

		s->rb[1] = s->lb[1];
		s->start = s->lt[1];
		if (s->rt[1] - s->rb[1] > 0.0f)
		    s->scale = (s->smax - s->smin) / (s->rt[1] - s->rb[1]);
	    }
	    break;
	}

    case PFUGUI_RADIO_BUTTON_TOGGLE:
	{
	    RadioButton    *r =(RadioButton *) w;
	    r->toggle = 1;
	    /* setup for indicator button */
	    PFSET_VEC2(r->icoord[0], r->xorg + 8, r->ty);
	    PFSET_VEC2(r->icoord[1], r->xorg + 16, r->ty);
	    PFSET_VEC2(r->icoord[2], r->xorg + 16, r->ty + 8);
	    PFSET_VEC2(r->icoord[3], r->xorg + 8, r->ty + 8);
	    r->tx = r->xorg + 24;
	    break;
	}

    case PFUGUI_RADIO_BUTTON_ATTR:
	{
	    PFSET_VEC2(w->icoord[0], w->xorg + 8, w->ty);
	    PFSET_VEC2(w->icoord[1], w->xorg + 16, w->ty);
	    PFSET_VEC2(w->icoord[2], w->xorg + 16, w->ty + 8);
	    PFSET_VEC2(w->icoord[3], w->xorg + 8, w->ty + 8);
	    w->tx = (w->xorg + 24);

	    break;
	}

    case PFUGUI_MENU_BUTTON:
	{
	    MenuButton     *m =(MenuButton *) w;
	    /* setup for menu arrow */
	    PFSET_VEC2(m->icoord[0], m->xorg + m->xsize - 16, m->yorg + 8);
	    PFSET_VEC2(m->icoord[1], m->xorg + m->xsize - 8, m->yorg + 12);
	    PFSET_VEC2(m->icoord[2], m->xorg + m->xsize - 16, m->yorg + 16);
	    break;
	}

    default:
	break;
    }
}

static void
fitWidgets(void)
{
    int            i, j, xmax, ymax, xmin, ymin, xsize, ysize;
    pfuPanel          *p;
    pfuWidget         *w;
    int	    foundWidget = 0, start;

    if (GUI->numPanels <= 0)
	return;

    pfDPoolLock(GUI->flags);
    GUI->flags->fitDirty = 0;
    pfDPoolUnlock(GUI->flags);
    FontFlags |= FONT_SCALE_DIRTY;

    /* find the max/max widget coords over all the widgets */
    for (j = 0; j < GUI->numPanels; j++)
    {
	p = GUI->panels[j];

	if (p->numWidgets)
	{

	    w = p->widgets[0];

	    if (!foundWidget)
	    {
		xmax = w->xorg + w->xsize - 1;
		xmin = w->xorg;
		ymax = w->yorg + w->ysize - 1;
		ymin = w->yorg;
		foundWidget = 1;
		start = 1;
	    }
	    else
		start = 0;

	    for (i = start; i < p->numWidgets; i++)
	    {
		w = p->widgets[i];
		if (!w->hidden)
		{
		    xmax = PF_MAX2(xmax, w->xorg + w->xsize - 1);
		    ymax = PF_MAX2(ymax, w->yorg + w->ysize - 1);
		    xmin = PF_MIN2(xmin, w->xorg);
		    ymin = PF_MIN2(ymin, w->yorg);
		}
	    }
	}
    }
    /*  XXX - padding seems necesary */
    ymin -= 8;

    if (!foundWidget || (xmax <= xmin) || (ymax <= ymin))
	return;

    xsize = xmax - xmin;
    ysize = ymax - ymin;

    if (xsize > 0.0f)
	    GUI->scaleX = (float) (GUI->pixSizeX - 2*PFUGUI_PANEL_FRAME) / (float) xsize;
    else
	GUI->scaleX = 1.0f;

    if (ysize > 0.0f)
	    GUI->scaleY = (float) (GUI->pixSizeY - PFUGUI_PANEL_FRAME) / (float) ysize;
    else
	GUI->scaleX = 1.0f;

    xmin *= GUI->scaleX;
    ymin *= GUI->scaleY;
    xsize *= GUI->scaleX;
    ysize *= GUI->scaleY;

    /* Left justify x */
    GUI->transX = -xmin + PFUGUI_PANEL_FRAME;

    /* Top justify y */
    GUI->transY = -(ymin + ysize) + (GUI->pixSizeY - PFUGUI_PANEL_FRAME);
}

void
pfuWidgetLabel(pfuWidget * w, const char *name)
{
    pfDPoolLock(w);

    strncpy(w->name, name, (PF_MAXSTRING - 1));
    w->name[(PF_MAXSTRING - 1)] = '\0';
    w->nameWidth = stringwidth(Fonts[TEXT_FONTS][PFU_FONT_MED], w->name);

    if ((w->type & PFUGUI_SLIDER) && (w->type & PFUGUI_VERTICAL) &&  w->xsize)
	w->tx = w->xorg + (w->xsize - w->nameWidth) * 0.5f;

    pfDPoolUnlock(w);

    dirtyWidget(w);
}

const char *
pfuGetWidgetLabel(pfuWidget * w)
{
    return(w->name);
}

int
pfuGetWidgetLabelWidth(pfuWidget * w)
{
    int findex=FONT_SIZE_TEXT;

    switch (w->type & PFUGUI_TYPE_MASK)
    {
	case PFUGUI_MESSAGE:
	    findex = FONT_SIZE_VAL;
	    break;
	default:
	    break;
    }
    return stringwidth(Fonts[TEXT_FONTS][findex], w->name);
}

int
pfuGetWidgetId(pfuWidget * w)
{
    return(w->id);
}

int
pfuGetWidgetType(pfuWidget * w)
{
    return(w->type);
}


void	
pfuWidgetMode(pfuWidget * w, int mode, int val)
{
    switch (mode)
    {
    case PFUGUI_STICKY_DEFAULT:
	PFFLAG_BOOL_SET(w->mode, STICKY_DFT, val);
	break;
    }
}

int	
pfuGetWidgetMode(pfuWidget * w, int mode)
{
    switch (mode)
    {
    case PFUGUI_STICKY_DEFAULT:
	return PFFLAG_BOOL_GET(w->mode, STICKY_DFT);
    }
    return -1;
}

float
pfuGetWidgetValue(pfuWidget * w)
{
    return(w->val);
}

float
pfuGetWidgetDefaultValue(pfuWidget * w)
{
    return(w->defaultVal);
}

int
pfuGetWidgetDefaultSelection(pfuWidget * w)
{
    switch(w->type & PFUGUI_TYPE_MASK)
    {
    case PFUGUI_RADIO_BUTTON:
	return ((RadioButton *) w)->defaultSel;
    case PFUGUI_MENU_BUTTON:
	return ((MenuButton *) w)->defaultSel;
    default:
	return 0;
    }
}

int
pfuGetWidgetSelection(pfuWidget * w)
{
    switch(w->type & PFUGUI_TYPE_MASK)
    {
    case PFUGUI_SLIDER:
	return((Slider *) w)->val;
    case PFUGUI_RADIO_BUTTON:
	return ((RadioButton *) w)->sel;
    case PFUGUI_MENU_BUTTON:
	return ((MenuButton *) w)->sel;
    default:
	return -1;
    }
}

void
pfuWidgetRange(pfuWidget * w, int mode, float min, float max, float initial)
{
    pfDPoolLock(w);
    w->dv = mode;
    w->min = min;
    w->max = max;
    w->val = initial;
    w->defaultVal = w->oldVal = w->val;

    if (w->type & PFUGUI_SLIDER)
    {
	Slider         *s =(Slider *) w;

	/* set slider relative values */
	switch (s->type & PFUGUI_TYPE_VALS)
	{
	case PFUGUI_SLIDER:
	    s->sval = s->sDefaultVal = s->sOldVal = s->val;
	    s->smin = min;
	    s->smax = max;
	    break;
	case PFUGUI_SLIDER_LOG:
	    if (s->val <0.0001f)
		s->val = 0.0001f;
	    s->sval = s->sDefaultVal = s->sOldVal = log10f(s->val);
	    if (min <0.001f)
		s->min = 0.001f;
	    s->smin = log10f(s->min);
	    s->smax = log10f(s->max);
	    break;
	}
	if (s->hvFlag)
	{
	    if (s->rt[0] - s->lt[0] > 0.0f)
		s->scale = (s->smax - s->smin) / (s->rt[0] - s->lt[0]);
	}
	else
	if (s->rt[1] - s->rb[1] > 0.0f)
	    s->scale = (s->smax - s->smin) / (s->rt[1] - s->rb[1]);

	sprintf(s->minstr, "%3.2f", min);
	sprintf(s->maxstr, "%3.2f", max);
	s->minStrWidth = stringwidth(Fonts[TEXT_FONTS][FONT_SIZE_VAL], s->minstr) + 4;
	s->maxStrWidth = stringwidth(Fonts[TEXT_FONTS][FONT_SIZE_VAL], s->maxstr);
	if (s->minStrWidth)
	    s->minx = s->xorg + (s->xsize - s->minStrWidth) * 0.5f;
	if (s->maxStrWidth)
	    s->maxx = s->xorg + (s->xsize - s->maxStrWidth) * 0.5f;
    }

    pfDPoolUnlock(w);

    dirtyWidget(w);
}


void
pfuWidgetValue(pfuWidget * w, float newValue)
{
    setWidgetValue(w, 0, newValue);
}

void
pfuWidgetDefaultValue(pfuWidget * w, float newValue)
{
    setWidgetValue(w, 1, newValue);
}

/*
 * setWidgetValue - tries to do the right thing for each kind
 *	of widget with the new value.
 *	Its straighforward for sliders.
 *	Labels are not affected.
 *	Menu buttons and Radio buttons look through their attrs
 *	    for a matching value and then select that index.
 *	Switches and buttons treat it as a TRUE(!=0) FALSE(=0) toggle.
 */
static void
setWidgetValue(pfuWidget * w, int dft, float newValue)
{
    pfDPoolLock(w);
    switch (w->type & PFUGUI_TYPE_VALS)
    {
    case PFUGUI_SLIDER:
	{
	    Slider         *s =(Slider *) w;
	    if (!dft)
	    {
		s->val = s->oldVal = newValue;
		s->sval = s->sOldVal = newValue;
	    }
	    else
	    {
		s->defaultVal = s->sDefaultVal = newValue;
	    }
	}
	break;
    case PFUGUI_SLIDER_LOG:
	{
	    Slider         *s =(Slider *) w;
	    if (!dft)
	    {
		s->val = s->oldVal = newValue;
		s->sval = s->sOldVal = log10f(newValue);
	    }
	    else
	    {
		s->defaultVal = s->sDefaultVal = log10f(newValue);
	    }
	}
	break;
    case PFUGUI_SWITCH:
	if (!dft)
	{
	    if (PF_ABSLT(w->min - newValue, 0.01f))
	    {
		w->val = w->min;
	    }
	    else
	    {
		w->val = w->max;
	    }
	}
	else
	{
	    w->defaultVal = newValue;
	}
	break;
    case PFUGUI_BUTTON:
	if (!dft)
	{
	    if (newValue != 0.0f)
	    {
		w->style = PFUGUI_HILIGHT;
		w->val = newValue;
		w->state = 1;
	    }
	    else
	    {
		w->val = 0.0f;
		w->state = 0;
		w->style = PFUGUI_NORMAL;
	    }
	}
	break;
    case PFUGUI_MENU_BUTTON:
	{
	    MenuButton *m =(MenuButton *) w;
	    int sel=(-1), i=0, l=(m->numSelections);
	    for (i=0; i < l; i++)
	    {
		if (PF_ABSLT(m->selectionList[i].val - newValue, 0.00001f))
		{
		    sel = i;
		    break;
		}
	    }
	    if (!dft)
	    {
		if (sel > -1)
		{
		    m->sel = m->oldSel = sel;
		    m->val = m->oldVal = m->selectionList[m->sel].val;
		}
		else
		{
		    pfNotify(PFNFY_WARN, PFNFY_PRINT,
			"pfuWidgetValue of PFUGUI_MENU_BUTTON: "
			"could find index for value of %f", newValue);
		}
	    }
	    else
	    if (m->defaultSel != sel) {
		m->defaultSel = sel;
		m->defaultVal = m->selectionList[sel].val;
		m->menu_dirty = 1;
	    }
	}
	break;
    case PFUGUI_RADIO_BUTTON: case PFUGUI_RADIO_BUTTON_TOGGLE:
	{
	    RadioButton *r =(RadioButton *) w;
	    int sel=(-1), i=0, l=(r->numSelections);
	    for (i=0; i < l; i++)
	    {
		if (PF_ABSLT(r->selectionList[i].val - newValue, 0.00001f))
		{
		    sel = i;
		    break;
		}
	    }
	    if (sel == -1)
	    {
		pfNotify(PFNFY_WARN, PFNFY_PRINT,
		    "pfuWidgetValue of PFUGUI_RADIO_BUTTON: "
		    "could find index for value of %f", newValue);
	    }
	    else
	    {	/* changing selection selection  */
		if (!dft)
		{
		    r->sel = r->oldSel = sel;
		    r->val = r->oldVal =  r->selectionList[sel].val;
		}
		else
		{
		    r->defaultSel = sel;
		    r->defaultVal = r->selectionList[sel].val;
		}
	    }
	}
	break;
    }
    pfDPoolUnlock(w);

    dirtyWidget(w);
}

void
pfuWidgetSelection(pfuWidget * w, int index)
{
    setWidgetSelection( w, 0,  index);
}

void
pfuWidgetDefaultSelection(pfuWidget * w, int index)
{
    setWidgetSelection( w, 1,  index);
}

/*
 *	Tries to do the right thing for each kind
 *	of widget with the new index.
 *	Menu buttons and Radio buttons use the index to select an selection.
 *	An index of (-1) can be used to turn off a Radio button.
 *	Sliders and labels are not affected.
 *	Switches and buttons treat it as a TRUE(!=0) FALSE(=0) toggle.
 */
static void
setWidgetSelection(pfuWidget * w, int dft, int index)
{
    pfDPoolLock(w);
    switch (w->type & PFUGUI_TYPE_MASK)
    {
    case PFUGUI_SLIDER:
	break;
    case PFUGUI_SWITCH:
	if (!dft)
	{
	    if (index == 0)
	    {
		w->val = w->min;
	    }
	    else
	    {
		w->val = w->max;
	    }
	}
	else
	{
	    if (index == 0)
	    {
		w->defaultVal = w->min;
	    }
	    else
	    {
		w->defaultVal = w->max;
	    }
	}
	break;
    case PFUGUI_BUTTON:
	if (!dft)
	{
	    if (index == 1)
	    {
		w->style = PFUGUI_HILIGHT;
		w->val = 1.0f;
		w->state = 1;
	    }
	    else
	    {
		w->val = 0.0f;
		w->state = 0;
		w->style = PFUGUI_NORMAL;
	    }
	}
	break;
    case PFUGUI_MENU_BUTTON:
	{
	    MenuButton *m =(MenuButton *) w;
	    if ((index > -1) && index < m->numSelections)
	    {
		if (!dft)
		{
		    m->sel = m->oldSel = m->sel = index;
		    m->val = m->selectionList[index].val;
		}
		else
		{
		    m->defaultSel = index;
		    m->defaultVal = m->selectionList[index].val;
		}
	    }
	    else
	    {
		pfNotify(PFNFY_WARN, PFNFY_PRINT,
		    "pfuWidgetSelection of PFUGUI_MENU_BUTTON: "
		    "bad index of %d", index);
	    }
	}
	break;
    case PFUGUI_RADIO_BUTTON:
	{
	    RadioButton *r =(RadioButton *) w;
	    if (!dft && index == -1 &&
		((w->type & PFUGUI_TYPE_VALS) == PFUGUI_RADIO_BUTTON_TOGGLE))
	    {	/* have title - do switch */
		if (!r->on)
		{
		    r->on = 1;
		    r->val = r->selectionList[r->sel].val;
		}
		else
		    r->on = 0;
		r->state = 0;
	    }
	    else 
	    if (index > -1)
	    {	/* changing selection selection */
		if (!dft)
		{
		    r->sel = index;
		    r->val = r->selectionList[index].val;
		    r->state = 0;
		}
		else
		{
		    r->defaultSel = index;
		    r->defaultVal = r->selectionList[index].val;
		}
	    }

	}
	break;
    }

    pfDPoolUnlock(w);

    dirtyWidget(w);
}

/*
 * pfuWidgetOnOff - tries to do the right thing for each kind
 *	of widget with an on/off select.
 *	It can be used to turn off a Radio button.
 *	Sliders and labels are not affected.
 *	Switches and buttons treat it as a TRUE(!=0) FALSE(=0) toggle.
 */
void
pfuWidgetOnOff(pfuWidget * w, int on)
{
    pfDPoolLock(w);

    switch(w->type & PFUGUI_TYPE_MASK)
    {
	case PFUGUI_SWITCH:
	    if (on == 0)
	    {
		w->val = w->min;
	    }
	    else
	    {
		w->val = w->max;
	    }
	    break;
	case PFUGUI_BUTTON:
	    if (on == 1)
	    {
		w->style = PFUGUI_HILIGHT;
		w->val = 1.0f;
		w->state = 1;
	    }
	    else
	    {
		w->val = 0.0f;
		w->state = 0;
		w->style = PFUGUI_NORMAL;
	    }
	    break;
	case PFUGUI_RADIO_BUTTON:
	    ((RadioButton *) w)->on = (on == 1);
	    break;
	default:
	    break;
    }

    pfDPoolUnlock(w);

    dirtyWidget(w);
}

void
pfuWidgetDefaultOnOff(pfuWidget * w, int on)
{
    switch(w->type & PFUGUI_TYPE_MASK)
    {
	case PFUGUI_SWITCH:
	    if (on == PF_OFF)
	    {
		w->defaultVal = w->min;
	    }
	    else
	    {
		w->defaultVal = w->max;
	    }
	    break;
	case PFUGUI_RADIO_BUTTON:
	    ((RadioButton *) w)->defaultOn = (on == 1);
	    break;
	default:
	    break;
    }
}

int
pfuIsWidgetOn(pfuWidget * w)
{
    switch(w->type & PFUGUI_TYPE_MASK)
    {
	case PFUGUI_RADIO_BUTTON:
	    return((RadioButton *) w)->on;
	case PFUGUI_SWITCH:
	    return (!(w->val == w->min));
	case PFUGUI_BUTTON:
	    return (!(w->val == 0.0f));
    default:
	return -1;
    }
}

void
pfuWidgetDrawFunc(pfuWidget * w, pfuWidgetDrawFuncType func)
{
    pfDPoolLock(w);
    w->drawFunc = func;
    pfDPoolUnlock(w);
    dirtyWidget(w);
}

void
pfuWidgetSelectFunc(pfuWidget * w, pfuWidgetSelectFuncType func)
{
    pfDPoolLock(w);
    w->selectFunc = func;
    pfDPoolUnlock(w);
}

void
pfuWidgetActionFunc(pfuWidget * w, pfuWidgetActionFuncType func)
{
    pfDPoolLock(w);
    w->actionFunc = func;
    pfDPoolUnlock(w);
}

pfuWidgetActionFuncType
pfuGetWidgetActionFunc(pfuWidget * w)
{
    if (w)
	return w->actionFunc;
    else
	return NULL;
}

pfuWidgetSelectFuncType
pfuGetWidgetSelectFunc(pfuWidget * w)
{
    if (w)
	return w->selectFunc;
    else
	return NULL;
}

pfuWidgetDrawFuncType
pfuGetWidgetDrawFunc(pfuWidget * w)
{
    if (w)
	return w->drawFunc;
    else
	return NULL;
}

static void
positionWidgetAttrs(pfuWidget * w)
{
    int             i;
    int            xp, yp;
    RadioButton	    *r = 0;
    MenuButton	    *m = 0;
    
    if (!(w->type & PFUGUI_RADIO_BUTTON))
	return;

    switch (w->type & PFUGUI_TYPE_MASK)
    {
    case PFUGUI_RADIO_BUTTON:

	r = (RadioButton *) w;
	if (r->numSelections == 0)
	    break;
    	    
	/* Setup start of selection positioning */
	if (r->hvFlag)
	{	/* radio selections go horizontally */
	    xp = r->xorg + r->nameWidth + (34);
	    yp = r->yorg;
	}
	else	/* Radio selections go down vertically */
	{
	    xp = r->xorg;
	    yp = r->yorg + r->ysize - (2*PFUGUI_RADIO_BUTTON_YSIZE + 4);
	}
	break;
    default:
	break;
    }
    
    if (r && r->selectionList && (r->numSelections > 0))
    {
	for (i = 0; i < r->numSelections; i++)
	{
	    switch (w->type & PFUGUI_TYPE_MASK)
	    {
	    case PFUGUI_RADIO_BUTTON:
		/* do successive selection positioning for radio buttons */
		if (r->hvFlag)
		{
		    /* is horizontal */
		    widgetDim(&(r->selectionList[i]), xp, yp,
		      r->selectionList[i].nameWidth + 28, PFUGUI_RADIO_BUTTON_YSIZE);
		    xp += r->selectionList[i].xsize;
		}
		else
		{
		    /* is vertical */
		    widgetDim(&(r->selectionList[i]), xp, yp,
		      PFUGUI_RADIO_BUTTON_XSIZE, PFUGUI_RADIO_BUTTON_YSIZE);
		    yp -= r->selectionList[i].ysize;
		}
		break;
	    default:
		break;
	    }
	}
    }
}

void
pfuWidgetSelections(pfuWidget * w, pfuGUIString * selectionList,
		    int *valList, void(**funcList)(pfuWidget *),
		    int numSelections)
{
    int             i;
    int		    type, atype;
    pfuWidget       **selection;
    RadioButton	    *r = 0;
    MenuButton	    *m = 0;

    if (!((w->type & PFUGUI_RADIO_BUTTON) || (w->type & PFUGUI_MENU_BUTTON)))
	pfNotify(PFNFY_WARN, PFNFY_USAGE,
		 "pfuWidgetSelections() Bad widget type 0x%x.", w->type);

    pfDPoolLock(w);
    
    type = w->type;
    switch (type & PFUGUI_TYPE_MASK)
    {
    case PFUGUI_RADIO_BUTTON:

	r = (RadioButton *) w;
	r->numSelections = numSelections;
	selection = (pfuWidget **) &(r->selectionList);
	atype = PFUGUI_RADIO_BUTTON_ATTR;
	break;
    case PFUGUI_MENU_BUTTON:
	m = (MenuButton *) w;
	atype = PFUGUI_MENU_BUTTON_ATTR;
	m->numSelections = numSelections;
	selection = (pfuWidget **) &(m->selectionList);
	m->menu_dirty = 1;
	break;
    }

    if (selectionList && (numSelections > 0))
    {
	if (*selection != NULL)
	{
	    /* free old selection list */
	    pfDPoolFree(GUIDP, *selection);
	}
	*selection = pfuNewWidgetBlock(NULL, PFUGUI_ATTRSBLOCK_DPID + (w->id),
			       atype, numSelections);
	for (i = 0; i < numSelections; i++)
	{
	    strcpy((*selection)[i].name, selectionList[i]);
	    (*selection)[i].nameWidth =
		stringwidth(Fonts[TEXT_FONTS][FONT_SIZE_VAL], (*selection)[i].name);
	    if (valList)
		(*selection)[i].val = valList[i];
	    else
		(*selection)[i].val = i;

	    if (funcList)
		(*selection)[i].actionFunc = funcList[i];
	    else
		(*selection)[i].actionFunc = w->actionFunc;
	}
    }
    positionWidgetAttrs(w);
    pfDPoolUnlock(w);
    dirtyWidget(w);
}

/*
 * Warning: Scanning is fragile - isn't using for speed,
 * but is only looking at things that _never_ change
 */
static int
scanSelections(RadioButton * r, int x, int y, int ymin)
{
    int             i;

    /* first check title */
    if (((r->hvFlag) && x <= (r->selectionList[0].xorg)) ||
	(!(r->hvFlag) &&
    (y >= ((r->selectionList[0].yorg + r->selectionList[0].ysize) - ymin))))
	return (-1);

    for (i = 0; i < r->numSelections; i++)
    {
	if ((y >= (r->selectionList[i].yorg - ymin)) &&
	    (y <= ((r->selectionList[i].yorg - ymin) +
		   r->selectionList[i].ysize)))
	    if ((x >= r->selectionList[i].xorg) &&
	     (x <= (r->selectionList[i].xorg + r->selectionList[i].xsize)))
		return (i);
    }
    return (-2);
}

/*
 * Propogate dirty flag from widget to panel and GUI so the widget gets redrawn.
*/
static void
dirtyWidget(pfuWidget * w)
{
    pfDPoolLock(w);
    w->dirty = GUI->refresh;
    pfDPoolUnlock(w);

    if (w->panel)
    {
    	pfDPoolLock(w->panel);
    	w->panel->dirty = GUI->refresh;
    	pfDPoolUnlock(w->panel);
    }

    pfDPoolLock(GUI->flags);
    GUI->flags->dirty = GUI->refresh;
    pfDPoolUnlock(GUI->flags);
}

/*
 *  drawWidget is fragile - doesn't lock the widget
 *  so it should always be called within a lock
 */
static void
drawWidget(pfuWidget *w, pfuPanel *p)
{
    int            i;
    char            str[80];
    pfVec3	   textColor, valColor;

    if (w == NULL)
	return;

    if (w->hidden)
	return;
	
    if (w->drawFunc)
    { /* use user-specified draw function */
	(w->drawFunc)(w, p);
	return;
    }

    if (w->active)
    {
	PFCOPY_VEC3(textColor, TextColor);
	PFCOPY_VEC3(valColor, ValColor);
    }
    else
    {
	PFCOPY_VEC3(textColor, InactiveTextColor);
	PFCOPY_VEC3(valColor, InactiveTextColor);
    }
    switch (w->type & PFUGUI_TYPE_MASK)
    {
    case PFUGUI_MESSAGE:
	mysetfont(Fonts[NUMBER_FONTS][FONT_SIZE_VAL]);
	drawBevel(w->coord, MessageColor, 3);
	drawBox(w->coord, MessageColor, -1);
	drawBevel(w->coord, MessageColor, -2);
	drawString(w->tx, w->ty, valColor, w->name);
	mysetfont((*CurFont));
	break;

    case PFUGUI_BUTTON:
	mysetfont(Fonts[TEXT_FONTS][FONT_SIZE_TITLE]);
	if (w->style == PFUGUI_HILIGHT)
	{
	    drawBevel(w->coord, BackColor, 3);
	    drawBox(w->coord, BackColor, -1);
	    drawBevel(w->coord, BackColor, -3);
	}
	else
	{
	    drawBevel(w->coord, BackColor, 3);
	    drawBox(w->coord, BackColor, 1);
	    drawBevel(w->coord, BackColor, -1);
	}

	drawString(w->tx, w->ty, textColor, w->name);
	if (w->dv)
	{
	    if (w->dv == PFUGUI_INT)
	    {
		i = (int) w->val;
		sprintf(str, "%ld", i);
	    }
	    else
	    {
		sprintf(str, "  %3.2f", w->val);
	    }
	    drawString(w->tx, w->ty - 16, textColor, str);
	}
	break;

    case PFUGUI_SWITCH:
	mysetfont(Fonts[TEXT_FONTS][FONT_SIZE_TITLE]);
	drawSwitchButton(w->coord, w->icoord, w->val != w->min);
	drawString(w->tx, w->ty, textColor, w->name);
	if (w->dv)
	{
	    if (w->dv == PFUGUI_INT)
	    {
		i = (int) w->val;
		sprintf(str, "%ld", i);
	    }
	    else
	    {
		sprintf(str, "  %3.2f", w->val);
	    }
	    drawString(w->tx, w->ty - 16, textColor, str);
	}
	break;

    case PFUGUI_SLIDER:
	{
	    Slider         *s =(Slider *) w;
	    int            hvflag = s->hvFlag;
	    pfVec2          coord[4];
	    float           gxl;
	    float           stmp = 1.0f /(s->scale);
	    int            minWidth = s->minStrWidth;
	    int            maxWidth = s->maxStrWidth;
	    int            width;
	    int            tx;

	    if ((s->scale) == 0.0f)
		pfNotify(PFNFY_WARN, PFNFY_PRINT,
			 "warning: slider has range scale of 0!");
	    drawBevel(s->coord, BackColor, 3);
	    drawBox(s->coord, BackColor, -1);
	    drawBevel(s->coord, BackColor, -2);

	    /* print current value after name */
	    sprintf(str, "  %5.2f", w->val);

	    /* print current, min and max values */
	    if (hvflag)
	    {			/* horizontal */
		/* name location */
		tx = s->tx + minWidth + ((s->xsize - minWidth - maxWidth) / 2)
		    - ((s->nameWidth + 80) / 2.0f);
		drawString(tx, s->ty, textColor, s->name);
		mysetfont(Fonts[NUMBER_FONTS][FONT_SIZE_VAL]);
		glColor3fv(valColor);
		dspstring(str);
		drawString(s->tx, s->ty, valColor, s->minstr);
		drawString((s->tx + s->xsize) - (maxWidth + 16),
			   s->ty, valColor, s->maxstr);
	    }
	    else
	    {
		/* vertical */
		drawString(s->tx, s->ty, textColor, s->name);
		mysetfont(Fonts[NUMBER_FONTS][FONT_SIZE_VAL]);
		width = stringwidth(Fonts[TEXT_FONTS][FONT_SIZE_VAL], str);
		tx = s->maxx + (maxWidth - width);
		drawString(tx, s->ty - 12, valColor, str);
		drawString(s->minx, s->ty - 30, valColor, s->minstr);
		drawString(s->maxx, s->yorg + 4, valColor, s->maxstr);
	    }

	    /* XXX these coords should be cached */
	    if (hvflag)
	    {			/* horizontal */
		/* draw the range bar as raised bevel */
		PFSET_VEC2(coord[0], s->lb[0] - 1, s->lb[1] + 4);
		PFSET_VEC2(coord[1], s->rt[0] + 1, s->lb[1] + 4);
		PFSET_VEC2(coord[2], s->rt[0] + 1, s->rt[1] - 4);
		PFSET_VEC2(coord[3], s->lb[0] - 1, s->rt[1] - 4);
		drawBox(coord, BackColor, 1);
		drawBevel(coord, BackColor, 2);

		/* draw the slider grip as raised bevel */
		gxl = (stmp * (s->sval - s->smin)) + s->lt[0];
		PFSET_VEC2(coord[0], gxl, s->lb[1] + 2);
		PFSET_VEC2(coord[1], gxl + 1, s->lb[1] + 2);
		PFSET_VEC2(coord[2], gxl + 1, s->rt[1] - 2);
		PFSET_VEC2(coord[3], gxl, s->rt[1] - 2);
	    }
	    else
	    {			/* vertical */
		/* draw the range bar as raised bevel */
		PFSET_VEC2(coord[0], s->lb[0] + 4, s->lb[1] + 1);
		PFSET_VEC2(coord[1], s->rt[0] - 4, s->lb[1] + 1);
		PFSET_VEC2(coord[2], s->rt[0] - 4, s->rt[1] - 1);
		PFSET_VEC2(coord[3], s->lb[0] + 4, s->rt[1] - 1);
		drawBox(coord, BackColor, 1);
		drawBevel(coord, BackColor, 2);

		/* draw the slider grip as raised bevel */
		gxl = s->lt[1] - (stmp * (s->sval - s->smin));
		PFSET_VEC2(coord[0], s->lb[0] + 2, gxl);
		PFSET_VEC2(coord[1], s->rt[0] - 2, gxl);
		PFSET_VEC2(coord[2], s->rt[0] - 2, gxl + 1);
		PFSET_VEC2(coord[3], s->lb[0] + 2, gxl + 1);
	    }
	    if (s->active)
	    {
		drawBevel(coord, SliderColor, 3);
		drawBevel(coord, SliderColor, -2);
	    }
	    else
	    {
		drawBevel(coord, InactiveSliderColor, 3);
		drawBevel(coord, InactiveSliderColor, -2);
	    }

	    /* draw inset bevel at default location */
	    if ((PF_ABSGT(s->sDefaultVal - s->sval, 10 * (s->scale))))
	    {
		if (hvflag)
		{		/* horizontal */
		    gxl = (stmp * (s->sDefaultVal - s->smin)) + s->lt[0];
		    coord[0][0] = gxl - 2;
		    coord[1][0] = gxl + 2;
		    coord[2][0] = gxl + 2;
		    coord[3][0] = gxl - 2;
		}
		else
		{		/* vertical */
		    gxl = s->lt[1] - (stmp * (s->sDefaultVal - s->smin));
		    coord[0][1] = gxl - 2;
		    coord[1][1] = gxl - 2;
		    coord[2][1] = gxl + 2;
		    coord[3][1] = gxl + 2;

		}

		drawBox(coord, BackColor, -1);
		drawBevel(coord, BackColor, -1);
	    }
	    if (s->sOldVal != s->sval)
	    {
		if (hvflag)
		{		/* horizontal */
		    gxl = (stmp * (s->sOldVal - s->smin)) + s->lt[0];
		    /* draw inset bevel where old slider grip was */
		    coord[0][0] = gxl;
		    coord[1][0] = gxl;
		    coord[2][0] = gxl;
		    coord[3][0] = gxl;
		    coord[0][1] += 1;
		    coord[1][1] += 1;
		    coord[2][1] -= 1;
		    coord[3][1] -= 1;
		}
		else
		{		/* vertical */
		    gxl = s->lt[1] - (stmp * (s->sOldVal - s->smin));
		    /* draw inset bevel where old slider grip was */
		    coord[0][1] = gxl;
		    coord[1][1] = gxl;
		    coord[2][1] = gxl;
		    coord[3][1] = gxl;
		    coord[0][0] += 1;
		    coord[1][0] += 1;
		    coord[2][0] -= 1;
		    coord[3][0] -= 1;
		}

		drawBevel(coord, BackColor, -1);
	    }
	    mysetfont((*CurFont));
	}
	break;

    case PFUGUI_RADIO_BUTTON:
	{
	    pfVec2          coord[4];
	    pfuWidget      *selection;
	    RadioButton    *r =(RadioButton *) w;
	    int            t;

	    drawSwitchButton(r->coord, r->icoord, r->on);
	    drawString(r->tx, r->ty, textColor, r->name);

	    if (r->selectionList)
	    {			/* draw selection widgets */
		int             on;

		/* put little inset bevel between name and selections */
		if (r->hvFlag)
		{		/* horizontal */
		    t = r->selectionList[0].xorg - 2;
		    PFSET_VEC2(coord[0], t - 1, r->yorg + 1);
		    PFSET_VEC2(coord[1], t + 2, r->yorg + 1);
		    PFSET_VEC2(coord[2], t + 2, r->yorg + r->ysize);
		    PFSET_VEC2(coord[3], t - 1, r->yorg + r->ysize);
		}
		else
		{		/* vertical */
		    t = r->selectionList[0].yorg + r->selectionList[0].ysize + 2;
		    PFSET_VEC2(coord[0], r->xorg + 1, t - 1);
		    PFSET_VEC2(coord[1], r->xorg + r->xsize, t - 1);
		    PFSET_VEC2(coord[2], r->xorg + r->xsize, t + 2);
		    PFSET_VEC2(coord[3], r->xorg + 1, t + 2);
		}
		drawBevel(coord, BackColor, -1);

		mysetfont(Fonts[NUMBER_FONTS][FONT_SIZE_VAL]);
		/* draw selectionibutes */
		for (i = 0; i < r->numSelections; i++)
		{
		    selection = &(r->selectionList[i]);
		    /* draw state indicator for selection */
		    if (r->sel != i)
			on = 0;
		    else
		    {
			if (r->on)
			    on = 1;
			else
			    on = -1;
		    }
		    if (i == r->defaultSel)
		    {
			PFSET_VEC2(coord[0], selection->icoord[0][0] - 1, selection->icoord[0][1] - 1);
			PFSET_VEC2(coord[1], selection->icoord[1][0] + 1, selection->icoord[1][1] - 1);
			PFSET_VEC2(coord[2], selection->icoord[2][0] + 1, selection->icoord[2][1] + 1);
			PFSET_VEC2(coord[3], selection->icoord[3][0] - 1, selection->icoord[3][1] + 1);
			drawStateIndicator(coord, on);
		    }
		    else
			drawStateIndicator(selection->icoord, on);
		    /* draw selection name after its state indicator */
		    drawString(selection->tx, selection->ty, textColor, selection->name);
		}
		mysetfont((*CurFont));;
	    }
	}
	break;

    case PFUGUI_MENU_BUTTON:
	{
	    MenuButton     *m =(MenuButton *) w;
	    Display *dsp = pfGetCurWSConnection();
	    int screen = pfGetPWinScreen(GUI->pwin);
	    if (m->menu_dirty)
	    {
		/* setup selection menu */
		sprintf(str, "Select %s %%t", w->name);
		if (m->menu)
		    freepup(m->menu);
		m->menu = (int)defpup(dsp, screen, str);
		for (i = 0; i < m->numSelections; i++)
		{
		    if (i == m->defaultSel)
		    {
			sprintf(str, "*%s*", m->selectionList[i].name);
			addtopup(m->menu, str, 0);
		    }
		    else
			addtopup(m->menu, m->selectionList[i].name, 0);
		}
		m->menu_dirty = 0;
	    }
	    drawBevel(m->coord, BackColor, 3);
	    if (m->state)
	    { /* draw it pushed */
		drawBox(m->coord, BackColor, -1);
		drawBevel(m->coord, BackColor, -3);
	    }
	    else
	    {
		drawBox(m->coord, BackColor, 1);
	    }
	    /*
	    drawBevel(m->coord, BackColor, 3);
	    drawBox(m->coord, BackColor, 1);
	    drawBevel(m->coord, BackColor, -1);
	    */
	    sprintf(str, "%s: %s\n", m->name, m->selectionList[m->sel].name);
	    drawString(w->tx, w->ty, textColor, str);
	    drawTriBevel(m->icoord, BackColor, 3);
	    drawTriBevel(m->icoord, BackColor, -1);
	    break;
	}
    default:
	pfNotify(PFNFY_WARN, PFNFY_USAGE, "Unknown widget type %d",
		 w->type & PFUGUI_TYPE_MASK);
	break;
    }
    if (w->type == PFUGUI_BUTTON)
    {				/* release push-button to un-pushed for next
				 * draw */
	if (w->state == 1)
	{
	    w->state = 2;
	}
    }
}

/******************************************************************************
 *			    Low level drawing utilities
 ******************************************************************************
 */

#define SCALE_CLR(s, c, f)	{\
	s[0]=f*c[0]; s[1]=f*c[1]; s[2]=f*c[2]; \
	if (f > 1.0f) { /* clamp colors */\
	    if (s[0] > 1.0f) s[0] = 1.0f; \
	    if (s[1] > 1.0f) s[1] = 1.0f; \
	    if (s[2] > 1.0f) s[2] = 1.0f; \
	}}

static void
drawString(float x, float y, pfVec3 clr, const char *str)
{
    /* pfVec3  sh; SCALE_CLR(sh, clr, 0.2f); c3f(sh); cmov2(x + 1, y - 1);
     * dspstring(str);
     */

    if (str[0])
    {
	glColor3fv(clr);
	pfuDrawStringPos(str, x, y, 0.0f);
    }
}

static void
drawBox(pfVec2 coord[4], pfVec3 clr, int elev)
{
    /* Draw box */
    if (elev)
    {
	pfVec3          c;
	if (elev < 0)
	{
	    SCALE_CLR(c, clr,0.95f);
	}
	else
	{
	    SCALE_CLR(c, clr, 1.05f);
	}
	glColor3fv(c);
    }
    else
    {
	glColor3fv(clr);
    }
    glBegin(GL_QUADS);
    glVertex2fv(coord[0]);
    glVertex2fv(coord[1]);
    glVertex2fv(coord[2]);
    glVertex2fv(coord[3]);
    glEnd();
}

static void
drawBevel(pfVec2 coord[4], pfVec3 clr, float bevel)
{
    pfVec3          sh;
    pfVec2          bcoord[4];
    float           shLeft, shRight, shBot, shTop;

    PFSET_VEC2(bcoord[0], coord[0][0] - bevel, coord[0][1] - bevel);
    PFSET_VEC2(bcoord[1], coord[1][0] + bevel, coord[1][1] - bevel);
    PFSET_VEC2(bcoord[2], coord[2][0] + bevel, coord[2][1] + bevel);
    PFSET_VEC2(bcoord[3], coord[3][0] - bevel, coord[3][1] + bevel);

    if (bevel < 0)	/* 'in' bevel */
    {	/* in bevels rely on backface(FALSE) */
	shLeft = 0.6f;
	shBot = 1.6f;
	shRight = 1.2f;
	shTop = 0.4f;
    }
    else	/* 'out' bevel */
    {
	shLeft = 1.2f;
	shBot = 0.4f;
	shRight = 0.6f;
	shTop = 1.6f;
    }

    /* left */
    SCALE_CLR(sh, clr, shLeft);
    glColor3fv(sh);
    glBegin(GL_QUADS);
    glVertex2fv(coord[3]);
    glVertex2fv(coord[0]);
    glVertex2fv(bcoord[0]);
    glVertex2fv(bcoord[3]);
    /* glEnd(); */

    /* bottom */
    SCALE_CLR(sh, clr, shBot);
    glColor3fv(sh);
    /* glBegin(GL_QUADS); */
    glVertex2fv(coord[0]);
    glVertex2fv(coord[1]);
    glVertex2fv(bcoord[1]);
    glVertex2fv(bcoord[0]);
    /* glEnd(); */

    /* right */
    SCALE_CLR(sh, clr, shRight);
    glColor3fv(sh);
    /* glBegin(GL_QUADS); */
    glVertex2fv(coord[1]);
    glVertex2fv(coord[2]);
    glVertex2fv(bcoord[2]);
    glVertex2fv(bcoord[1]);
    /* glEnd(); */

    /* top */
    SCALE_CLR(sh, clr, shTop);
    glColor3fv(sh);
    /* glBegin(GL_QUADS); */
    glVertex2fv(coord[2]);
    glVertex2fv(coord[3]);
    glVertex2fv(bcoord[3]);
    glVertex2fv(bcoord[2]);
    glEnd();

}

static void
drawTriBevel(pfVec2 coord[4], pfVec3 clr, float bevel)
{
    pfVec3          sh;
    pfVec2          bcoord[3];
    float           shLeft, shBot, shTop;

    PFSET_VEC2(bcoord[0], coord[0][0] - bevel, coord[0][1] - bevel);
    PFSET_VEC2(bcoord[1], coord[1][0] + bevel, coord[1][1] - bevel);
    PFSET_VEC2(bcoord[2], coord[2][0] - bevel, coord[2][1] + bevel);

    if (bevel < 0)	/* 'in' bevel */
    {	/* in bevels rely on backface(FALSE) */
	shLeft = 0.6f;
	shBot = 1.6f;
	shTop = 0.4f;
    }
    else	/* 'out' bevel */
    {
	shLeft = 1.2f;
	shBot = 0.4f;
	shTop = 1.6f;
    }

    /* left */
    SCALE_CLR(sh, clr, shLeft);
    glColor3fv(sh);
    glBegin(GL_QUADS);
    glVertex2fv(coord[2]);
    glVertex2fv(coord[0]);
    glVertex2fv(bcoord[0]);
    glVertex2fv(bcoord[2]);
    /* glEnd(); */

    /* bottom */
    SCALE_CLR(sh, clr, shBot);
    glColor3fv(sh);
    /* glBegin(GL_QUADS); */
    glVertex2fv(coord[0]);
    glVertex2fv(coord[1]);
    glVertex2fv(bcoord[1]);
    glVertex2fv(bcoord[0]);
    /* glEnd(); */

    /* top */
    SCALE_CLR(sh, clr, shTop);
    glColor3fv(sh);
    /* glBegin(GL_QUADS); */
    glVertex2fv(coord[1]);
    glVertex2fv(coord[2]);
    glVertex2fv(bcoord[2]);
    glVertex2fv(bcoord[1]);
    glEnd();

}

static void
drawStateIndicator(pfVec2 icoord[4], int on)
{
    switch(on)
    {
    case 1:
	drawBevel(icoord, BackColor, 2);
	drawBox(icoord, OnColor, 1);
	break;
    case -1:
	drawBox(icoord, OffColor, -1);
	drawBevel(icoord, OnColor, -1);
	break;
    case 0:
	drawBox(icoord, OffColor, -1);
	drawBevel(icoord, BackColor, -1);
	break;
    }
}

static void
drawSwitchButton(pfVec2 coord[4], pfVec2 icoord[4], int on)
{
    drawBevel(coord, BackColor, 3);
    if (on)	/* draw pushded-in bevel */
    {
	drawBox(coord, BackColor, -1);
	drawBevel(coord, BackColor, -3);
    }
    else
    {
	drawBox(coord, BackColor, 1);
    }
    drawStateIndicator(icoord, on);
}

static void
scaleFonts(float _scale, int drawFlag)
{
    int loadCursor = 0;
    int i, j, size, doScale=0;
    double start,  end;
    
    if ((_scale > 0.0f) && 
	(PF_ABSGE(_scale - GUI->fontScale, 0.05f) || 
		(GUI->fontScale < 0.0f)))
    {
	FontFlags |= FONT_DIRTY;
	GUI->fontScale = _scale;
	doScale = 1;
    }
    else
    {
	FontFlags &= ~(FONT_SCALE_DIRTY);
    }
    if ((FontFlags & FONT_DIRTY) &&
	drawFlag && GUI->pwin && pfGetPWinWSWindow(GUI->pwin))
    {
	pfuLoadPWinCursor(GUI->pwin, PFU_CURSOR_watch);
	loadCursor = 1;
    }
    if (doScale)
    {
	char str[PF_MAXSTRING];
	
	GUIFontScale = GUI->fontScale;
	if (GUI && GUI->flags)
	{
	    pfDPoolLock(GUI->flags);
	    GUI->flags->dirty = GUI->flags->redrawAll = GUI->refresh;
	    pfDPoolUnlock(GUI->flags);
	}
start = pfGetTime();
	for (j = 0; j < NUM_GUI_FONTS; j++)
	{
	    for (i = 1; i < PFU_NUM_FONTS; i++)
	    {
		size = (GUIFontScale * FontSizes[j][i] * 1.6f); /* XXX Fudge Factor */
		if (size > 0)
		    Fonts[j][i].size = size;
		sprintf(str, "-*-%s-normal--%i-*-*-*-*-*-iso8859-1", 
		    FontNames[j], Fonts[j][i].size);
		if (Fonts[j][0].info)
		{
		    if (Fonts[j][i].info)
			XFreeFont(pfGetCurWSConnection(), Fonts[j][i].info);
		    pfuLoadXFont(str, &(Fonts[j][i]));
		}
		else
		    pfNotify(PFNFY_WARN, PFNFY_RESOURCE,
			"scaleFonts: font \"%s\" not scalable to size %i",
		       FontNames[j], Fonts[j][i].size);
	    }
	}
end = pfGetTime();
pfNotify(PFNFY_INFO, PFNFY_PRINT, "scaleFonts %.2f - Load: %.2f secs", 
	    _scale,  (float) (end - start));
	FontFlags &= ~(FONT_SCALE_DIRTY);
	FontFlags |= FONT_DRAW_DIRTY;
    }
    if (drawFlag && (FontFlags & FONT_DRAW_DIRTY))
    {
start = pfGetTime();
	for (j = 0; j < NUM_GUI_FONTS; j++)
	{
	    if (Fonts[j][0].info)
		for (i = 1; i < PFU_NUM_FONTS; i++)
		{
			pfuMakeXFontBitmaps(&(Fonts[j][i]));
		}
	}
	FontFlags &= ~(FONT_DRAW_DIRTY);
glFinish();
end = pfGetTime();
pfNotify(PFNFY_INFO, PFNFY_PRINT, "scaleFonts - Make Font Bitmaps: %.2f secs", 
	    (float) (end - start));
	initMessageFonts();
    }
    if (loadCursor) /* replace cursor */
    {
	pfuLoadPWinCursor(GUI->pwin, pfuGetGUICursor(pfuGetGUICursorSel()));
    }
}

static void
initMessageFonts(void)
{
    int i, size;
    char str[PF_MAXSTRING];
    
    MessageFontsInited = 1;
    for (i = 1; i < PFU_NUM_FONTS; i++)
    {
	size = (FontSizes[MESSAGE_FONTS][i] * 1.5f); /* XXX Fudge Factor */
	if (size > 0)
	    Fonts[MESSAGE_FONTS][i].size = size;
	sprintf(str, "-*-%s-normal--%i-*-*-*-*-*-iso8859-1", 
	    FontNames[MESSAGE_FONTS], Fonts[MESSAGE_FONTS][i].size);
	if (Fonts[MESSAGE_FONTS][0].info)
	{
	    if (Fonts[MESSAGE_FONTS][i].info)
		XFreeFont(pfGetCurWSConnection(), Fonts[MESSAGE_FONTS][i].info);
	    pfuLoadXFont(str, &(Fonts[MESSAGE_FONTS][i]));
	}
	else
	    pfNotify(PFNFY_WARN, PFNFY_RESOURCE,
		"initMessageFonts: font \"%s\" not scalable to size %i",
	       FontNames[MESSAGE_FONTS], Fonts[MESSAGE_FONTS][i].size);
    }   
    for (i = 1; i < PFU_NUM_FONTS; i++)
    {
	if (Fonts[MESSAGE_FONTS][0].info)
	    pfuMakeXFontBitmaps(&(Fonts[MESSAGE_FONTS][i]));
    }
}

static void
initGUIFonts(int drawFlag)
{
#ifdef USE_FONT_MANAGER
static int      FMInited = 0;
    fmfonthandle    fontHandle;

    if (!FMInited)
    {
	fminit();
	FMInited = 1;
    }
    bzero(Fonts, sizeof(Fonts));
#endif	/* USE_FONT_MANAGER */
    {
	int i, j;
	char str[256];
	for (i=0; i < NUM_TOTAL_FONTS; i++)
	{
	    int c;
	    char **ts;
	    sprintf(str, "-*-%s-normal--*-*-*-*-*-*-iso8859-1", FontNames[i]);
	    ts = XListFonts(pfGetCurWSConnection(),  str, 1, &c);
	    if (c)
	    {
		Fonts[i][0].info = (void*)1; /* mark font found */
		XFreeFontNames(ts);
		for (j=0; j < NUM_FONT_SIZES; j++)
		{
		    Fonts[i][j].size = FontSizes[i][j];
		}
	    }
	    else
		pfNotify(PFNFY_WARN, PFNFY_RESOURCE,
		    "initGUIFonts: font \"%s\" not found", FontNames[i]);
	}
	FontFlags |= FONT_DIRTY;
	GUIFontScale = 0.0f;
	scaleFonts(1.0f, drawFlag);
	CurFont = &Fonts[TEXT_FONTS][PFU_FONT_BIG];
	if (drawFlag)
	    mysetfont(*(CurFont));
    }
}
/*******************************************************************************
 *				Draw Message Utilities
 *******************************************************************************
 */


/* Draw a message justified wrt to x,y in font of size 'size'.
 * 'just' may be CENTER, LEFT, RIGHT. x, y should range from 0 to 1
 * and 'size' should be SMALL, MED, or BIG.
 * The colors of the message are fixed: purple text with black shadow.
 * This is ageneral and  expensive routine and is meant to be used for
 * fairly static messages that only need updating upon a REDRAW event.
 * For messages that need to be
 * updated frequently, a more streamlined routine should be used.
 */
void
pfuDrawMessage(pfChannel *chan, const char *msg,
		int rel, int just, float x, float y, int size, int cimode)
{

static pfVec4	    textRGB = {1.0f, 0.0f, 1.0f, 1.0f};
static pfVec4	    shadowRGB = {0.125f, 0.06f, 0.125f, 1.0f};

    if (cimode)
    {
    }
    else
    {
	drawMessage(chan, msg, rel, just, x, y, size, cimode,
			(unsigned long) textRGB, (unsigned long) shadowRGB);
    }

}

void
pfuDrawMessageCI(pfChannel *chan,
		    const char *msg, int rel, int just,
		    float x, float y, int size,
		    int textClr, int shadowClr)
{
    drawMessage(chan, msg, rel, just, x, y, size, PFU_CI, textClr, shadowClr);
}

void
pfuDrawMessageRGB(pfChannel *chan,
		    const char *msg, int rel, int just,
		    float x, float y, int size,
		    pfVec4 textClr, pfVec4 shadowClr)
{
    drawMessage(chan, msg, rel, just, x, y, size, PFU_RGB,
		(unsigned long) textClr, (unsigned long) shadowClr);
}

static void
drawMessage(pfChannel *chan, const char *msg,
		int rel, int just, float x, float y, int size, int cimode,
		unsigned long textClr, unsigned long shadowClr)
{
    float	 width;
    float	 xstart;
    float	 height;
    float	 ystart;
    int		 xo, yo, xs, ys;
    int		 vp[4];
    pfMatrix	 proj;


    if (!chan)
	rel = PFU_MSG_PIPE;

    if (!GUI)
    {
	getGUIDataPool(1);
    }
    if (!MessageFontsInited)
	initMessageFonts();;
	
    if (GUI && GUI->flags && GUI->flags->fitDirty)
	fitWidgets();

    if (size < 0 || size > PFU_NUM_FONTS)
    {
	pfNotify(PFNFY_WARN, PFNFY_USAGE,
	    "pfuDrawMessage: unknown font size %d,  using PFU_FONT_MED.",
	    size);
	size = PFU_FONT_MED;
    }
    
    glGetIntegerv(GL_VIEWPORT, vp);

    if (rel == PFU_MSG_CHAN)
    {
	/* convert to channel viewport coords */
	pfGetChanOutputOrigin(chan, &xo, &yo);
	pfGetChanOutputSize(chan, &xs, &ys);
	if (!(xs > 0) || !(ys > 0))
	{
	    pfWindow *w = pfGetCurWin();
	    pfNotify(PFNFY_WARN, PFNFY_PRINT,
		"pfuDrawMessage - bad pfChannel size: %d %d, trying current window.", xs, ys);
	    pfGetWinSize(w, &xs, &ys);
	    xo = yo = 0;
	    if (!(xs > 0) || !(ys > 0))
	    {
		pfNotify(PFNFY_WARN, PFNFY_PRINT,
		    "pfuDrawMessage - bad window size: %d %d", xs, ys);
		return;
	    }
	}
	glViewport(xo, yo, xs, ys);
	glScissor(xo, yo, xs, ys);
    }
    else /* Pipe Window relative message position */
    {
	if (chan)
	{
	    pfPipeWindow *pw = pfGetChanPWin(chan);
	    pfGetPWinSize(pw, &xs, &ys);
	}
	else
	{
	    xs = vp[2];
	    ys = vp[3];
	}

	if (!(xs > 0) || !(ys > 0))
	{
	    pfNotify(PFNFY_WARN, PFNFY_PRINT,
		"pfuDrawMessage bad Pipe Window size: %d %d", xs, ys);
	}

	/* set the viewport to be able to draw to the entire window */
	glViewport(0, 0, xs, ys);
	glScissor(0, 0, xs, ys);
    }
    {
	GLint mm;
	glGetIntegerv(GL_MATRIX_MODE, &mm);
	if (mm != GL_PROJECTION)
	    glMatrixMode(GL_PROJECTION);
	glGetFloatv(GL_PROJECTION_MATRIX, (float*) proj);
	glLoadIdentity(); 
	glOrtho(0.0f, xs, 0.0f, ys, -1.0f, 1000.0f);
	glMatrixMode(GL_MODELVIEW);
    }

    pfPushIdentMatrix();

    /* convert x and y to pixel coords */
    x *= xs;
    y *= ys;

    mysetfont(Fonts[MESSAGE_FONTS][size]);
    width = (stringwidth(Fonts[MESSAGE_FONTS][size], msg)) + 10.0f;
    height = fontheight(Fonts[MESSAGE_FONTS][size]) + 10.0f;

    switch (just) {
    case PFU_LEFT_JUSTIFIED:
	xstart = x;
	break;
    case PFU_RIGHT_JUSTIFIED:
	xstart = x - width - 2.0f;
	break;
    case PFU_CENTER_JUSTIFIED:
    default:
	xstart = x - width*0.5f;
	break;
    }

    /* Don't let text go off the right side of the screen */
    if (just != PFU_LEFT_JUSTIFIED)
    {
	if ((xstart + width) >= (xs - 1.0f))
	    xstart = (xs) - width - 2.0f;
    }

    /* Don't let text go off the top of the screen */
    if ((y + height) >= (ys - 1.0f))
	ystart = PF_MAX2(0.0f, (ys) - height - 2.0f);
    else
	ystart = y;

    pfPushState();
    pfBasicState();
    glDepthFunc(GL_ALWAYS);

    /* Draw shadow */
    if (cimode)
	glIndexi((int)shadowClr);
    else
	glColor4fv((float*)shadowClr);
    pfuDrawStringPos(msg, xstart + 2.0f, ystart - 2.0f, 0.0f);

    /* Draw top */
    if (cimode)
	glIndexi((int)textClr);
    else
	glColor4fv((float*)textClr);
    pfuDrawStringPos(msg, xstart, ystart, 0.0f);
    
    pfPopMatrix();
    glDepthFunc(GL_LEQUAL);
    pfPopState();

    { /* restore viewpoort */
	glViewport(vp[0], vp[1], vp[2], vp[3]);
    }
    glMatrixMode(GL_PROJECTION);
    glLoadMatrixf((float*) proj);
    glMatrixMode(GL_MODELVIEW);
    mysetfont(Fonts[TEXT_FONTS][FONT_SIZE_TITLE]);
}

/******************************************************************************
 *				Cursor Routines
 ******************************************************************************
 */

#if 0
static int	simCursorName=0, guiCursorName=0;
static Cursor	simCursor=0, guiCursor=0;
#endif

void
pfuGUICursor(int target, int c)
{
    if (!GUI)
	getGUIDataPool(1);

    switch(target)
    {
	case PFU_CURSOR_GUI:
	    if (c == PFU_CURSOR_DEFAULT)
		c = PFU_CURSOR_GUI_DEFAULT;
	    GUI->guiCursor = c;
	    break;
	case PFU_CURSOR_MAIN:
	    if (c == PFU_CURSOR_DEFAULT)
		c = PFU_CURSOR_MAIN_DEFAULT;
	    GUI->mainCursor = c;
	    break;
	default:
	    pfNotify(PFNFY_WARN, PFNFY_USAGE,
		"pfuGUICursor() - unknown target %d", target);
	    break;
    }

}

Cursor
pfuGetGUICursor(int target)
{
    if (!GUI)
	getGUIDataPool(1);
    switch(target)
    {
	case PFU_CURSOR_GUI:
	    return (GUI->guiCursor);
	case PFU_CURSOR_MAIN:
	    return (GUI->mainCursor);
	default:
	    pfNotify(PFNFY_WARN, PFNFY_USAGE,
		"pfuGetGUICursor() - unknown target %d", target);
	    return 0;
    }
}

void
pfuGUICursorSel(int c)
{
    if (!GUI)
	getGUIDataPool(1);
    if (c == PFU_CURSOR_DEFAULT)
	c = PFU_CURSOR_GUI_DEFAULT;
    GUI->cursor = c;
}

Cursor
pfuGetGUICursorSel(void)
{
    if (!GUI)
	getGUIDataPool(1);
    return GUI->cursor;
}


void
pfuInitGUICursors(void)
{
    pfuCreateCursor(pfuGetGUICursor(PFU_CURSOR_GUI));
    pfuCreateCursor(pfuGetGUICursor(PFU_CURSOR_MAIN));
}

void
pfuUpdateGUICursor(void)
{
    Cursor		cursorVal;
    static int		oldCursorVal = -1;
    
    cursorVal = pfuGetGUICursor(pfuGetGUICursorSel());
    if ((cursorVal != oldCursorVal) && (GUI->pwin != NULL) &&
		(pfGetPWinWSWindow(GUI->pwin) != NULL))
    {
	pfuLoadPWinCursor(GUI->pwin, cursorVal);
	oldCursorVal = (int)cursorVal;
    }
}

void
pfuDrawChanDVRBox(pfChannel *chan)
{
    pfPipeVideoChannel *pvc;
    int xo, yo, xs, ys;
    float xscale, yscale;
    pfVec2 coords[4];

    pfPushState();
    pfBasicState();
    glDepthFunc(GL_ALWAYS);

    /* Draw output channel box to show DVR */
    pvc = pfGetChanPVChan(chan);
    pfGetChanOutputOrigin(chan, &xo, &yo);
    pfGetChanOutputSize(chan, &xs, &ys);
    pfGetPVChanScale(pvc, &xscale, &yscale);
    glScissor(xo, yo, xs, ys);
    glViewport(xo, yo, xs, ys);
    glMatrixMode(GL_PROJECTION);
    glLoadIdentity();
    glOrtho(-1.0f, 1.0f, -1.0f, 1.0f, -1.0f, 1000.0f);
    glMatrixMode(GL_MODELVIEW);
    glLoadIdentity();
    coords[0][0] = -xscale; coords[0][1] = -yscale;
    coords[1][0] = xscale; coords[1][1] = -yscale;
    coords[2][0] = xscale; coords[2][1] = yscale;
    coords[3][0] = -xscale; coords[3][1] = yscale;
    glColor4f(1.0f, 0.0f, 0.0f, 1.0f);
    glLineWidth(2.0f);
    glBegin(GL_LINE_LOOP);
    glVertex2fv(coords[0]);
    glVertex2fv(coords[1]);
    glVertex2fv(coords[2]);
    glVertex2fv(coords[3]);
    glEnd();

    pfPopState();
    glDepthFunc(GL_LEQUAL);
}