#include <X11/IntrinsicP.h>
#include <X11/StringDefs.h>
#include "LabeledPictureP.h"

/* Private Data */
#define LPOffset(tag)	XtOffset(LabeledPictureWidget, labeled_picture.tag)
#define ACCESS_SUPER(w,tag) (XtSuperclass(w)->core_class.tag)
#define CALL_SUPER(w,cmd,arg) (*ACCESS_SUPER(w,cmd))arg
#define CALL_SELF(w,cmd,arg) (*XtClass(w)->core_class.cmd)arg

static int defLabelMargin = 3;
static int defSideMargin = 6;
static Boolean defTrue = True;

static XtResource resources[] = {
	{AiNlabel, XtCLabel, XtRString, sizeof(String),
	 LPOffset(label), XtRString, NULL},
	{AiNpixmap, XtCPixmap, XtRPixmap, sizeof(Pixmap),
	 LPOffset(pixmap), XtRPixmap, NULL},
	{AiNforeground, XtCForeground, XtRPixel, sizeof(Pixel),
	 LPOffset(foreground), XtRString, "Black"},
	{AiNfont, XtCFont, XtRFontStruct, sizeof(XFontStruct*),
	 LPOffset(font), XtRString,
	 "-adobe-courier-bold-r-normal--12-120-75-75-m-70-iso8859-1"},
	{AiNlabelMargin, AiCLabelMargin, XtRInt, sizeof(int),
	 LPOffset(label_margin), XtRInt, (caddr_t)&defLabelMargin},
	{AiNsideMargin, AiCSideMargin, XtRInt, sizeof(int),
	 LPOffset(side_margin), XtRInt, (caddr_t)&defSideMargin},
	{AiNcallback, XtCCallback, XtRCallback, sizeof(caddr_t),
	 LPOffset(callback), XtRCallback, (caddr_t)NULL},
	{AiNsensitive, XtCSensitive, XtRBoolean, sizeof(Boolean),
	 LPOffset(sensitive), XtRBoolean, (caddr_t)&defTrue},
	{AiNtextPosition, AiCTextPosition, AiRTextPosition, sizeof(TextPosition),
	 LPOffset(position), XtRString, (caddr_t)"lower"},
};

static void ClassInitialize();
static void Initialize(), Redisplay(), Destroy();
static void SelectIt(), UnSelectIt(), ActivateIt();

/**** Actions ****/
static char defaultActions[] = 
	"<EnterWindow>:	SelectIt()\n\
	 <LeaveWindow>: UnSelectIt()\n\
	 <Btn1Down>:	ActivateIt()";

static XtActionsRec actions[] = {
	{"SelectIt",	SelectIt},
	{"UnSelectIt",	UnSelectIt},
	{"ActivateIt",	ActivateIt},
};

LabeledPictureClassRec labeledPictureClassRec = {
{
/* core_class fields */	
    /* superclass	  	*/	(WidgetClass)&widgetClassRec,
    /* class_name	  	*/	"LabeledPicture",
    /* widget_size	  	*/	sizeof(LabeledPictureRec),
    /* class_initialize   	*/	ClassInitialize,
    /* class_part_initialize	*/	NULL,
    /* class_inited       	*/	FALSE,
    /* initialize	  	*/	Initialize,
    /* initialize_hook		*/	NULL,
    /* realize		  	*/	XtInheritRealize,
    /* actions		  	*/	actions,
    /* num_actions	  	*/	XtNumber(actions),
    /* resources	  	*/	resources,
    /* num_resources	  	*/	XtNumber(resources),
    /* xrm_class	  	*/	NULLQUARK,
    /* compress_motion	  	*/	TRUE,
    /* compress_exposure  	*/	TRUE,
    /* compress_enterleave	*/	TRUE,
    /* visible_interest	  	*/	FALSE,
    /* destroy		  	*/	Destroy,
    /* resize		  	*/	XtInheritResize,
    /* expose		  	*/	Redisplay,
    /* set_values	  	*/	NULL,
    /* set_values_hook		*/	NULL,
    /* set_values_almost	*/	XtInheritSetValuesAlmost,
    /* get_values_hook		*/	NULL,
    /* accept_focus	 	*/	NULL,
    /* version			*/	XtVersion,
    /* callback_private   	*/	NULL,
    /* tm_table		   	*/	defaultActions,
    /* query_geometry		*/	NULL,
  },
};

WidgetClass labeledPictureWidgetClass = (WidgetClass)&labeledPictureClassRec;

#define _max_(x,y)	((x)>(y)?(x):(y))

/*
 * Converter from String to TextPosition
 */
static void
convertStr2TextPosition(args,numargs,from,to)
XrmValue *args;
Cardinal *numargs;
XrmValue *from;
XrmValue *to;
{
	TextPosition *pos = (TextPosition*)XtMalloc(sizeof(TextPosition));
	switch (*from->addr) {
	case 'l':
	case 'L':
		switch(from->addr[1]) {
		case 'e':
		case 'E':
			*pos = left;
			break;
		case 'o':
		case 'O':
			*pos = lower;
			break;
		default:
			XtWarning("LabeledPicture: Bad text position string");
			*pos = lower;
		}
		break;
	case 'r':
	case 'R':
		*pos = right;
		break;
	case 'u':
	case 'U':
		*pos = upper;
		break;
	default:
		XtWarning("LabeledPicture: Bad text position string");
		*pos = lower;
	}
	
	to->size = sizeof(TextPosition);
	to->addr = (caddr_t)pos;
}

static void
ClassInitialize()
{
	XtAddConverter(XtRString, AiRTextPosition,
		       convertStr2TextPosition, NULL,0);
}

static void
AdjustSize(w,force)
LabeledPictureWidget w;
Boolean force;
{
	LabeledPicturePart *lp = &w->labeled_picture;
	int depth;
	int x,y,bwidth;
	Window rootwin;
	
	lp->label_width = XTextWidth(lp->font,lp->label,strlen(lp->label));
	if (lp->pixmap != NULL && lp->pixmap != AiNullPixmap) {
		XGetGeometry(XtDisplay(w),lp->pixmap,
			     &rootwin,
			     &x,
			     &y,
			     &lp->pixmap_width,
			     &lp->pixmap_height,
			     &bwidth,
			     &depth);
		if (w->core.width == 0 || (force && !(lp->status & ST_INIT_WIDTH))) {
			if (lp->position == upper || lp->position == lower) {
				w->core.width = _max_(lp->pixmap_width,
						      lp->label_width)+
						lp->side_margin*2;
			}
			else {
				w->core.width = lp->pixmap_width+
						lp->label_width+
						lp->side_margin*3;
			}
		}
		if (w->core.height == 0 || (force && !(lp->status & ST_INIT_HEIGHT))) {
			if (lp->position == upper || lp->position == lower) {
				w->core.height = lp->pixmap_height+
						 lp->font->max_bounds.descent+
						 lp->font->max_bounds.ascent+
						 lp->label_margin;
			}
			else {
				w->core.height = _max_(lp->pixmap_height,
						       lp->font->max_bounds.descent+
						       lp->font->max_bounds.ascent)+
						 lp->label_margin;
			}
		}
		if (depth == 1) { /* bitmap */
			w->labeled_picture.pixmap_is_bitmap = True;
		}
		else if (depth != XtScreen(w)->root_depth) {
			XtError("LabeledPicture: pixmap has different depth from the screen");
		}
		else
			w->labeled_picture.pixmap_is_bitmap = False;
	}
	else {
		lp->pixmap_width = lp->pixmap_height = 0;
		if (w->core.width == 0 || (force && !(lp->status & ST_INIT_WIDTH)))
			w->core.width = lp->label_width+lp->side_margin;
		if (w->core.height == 0 || (force && !(lp->status & ST_INIT_HEIGHT)))
			w->core.height = lp->font->max_bounds.descent+
					 lp->font->max_bounds.ascent+
					 lp->label_margin;
	}
}

static void
Initialize(request, new)
Widget request, new;
{
	XGCValues gv;
	LabeledPictureWidget w = (LabeledPictureWidget) new;
	LabeledPicturePart *lp = &w->labeled_picture;

	gv.font = lp->font->fid;
	gv.foreground = lp->foreground;
	gv.background = w->core.background_pixel;
	lp->label_GC = XtGetGC((Widget)w,
			       GCFont | GCForeground | GCBackground,
			       &gv);
	lp->picture_GC = XtGetGC((Widget)w,
				 GCForeground | GCBackground,
				 &gv);

	if (lp->label == NULL)
		lp->label = w->core.name;
	lp->status = 0;
	if (w->core.width)
		lp->status |= ST_INIT_WIDTH;
	if (w->core.height)
		lp->status |= ST_INIT_HEIGHT;

	AdjustSize(w,False);
}

static void 
Redisplay(new)
Widget new;
{
	LabeledPictureWidget w = (LabeledPictureWidget)new;
	LabeledPicturePart *lp = &(w->labeled_picture);
	int len = strlen(w->labeled_picture.label);
	int picX,picY,lblX,lblY;

	if (lp->position == left) {
		picX = lp->label_width+lp->side_margin;
		picY = 0;
		lblX = 0;
		lblY = w->core.height/2;
	}
	else if (lp->position == right) {
		picX = 0;
		picY = 0;
		lblX = lp->pixmap_width+lp->side_margin;
		lblY = w->core.height/2;
	}
	else if (lp->position == upper) {
		picX = (w->core.width-lp->pixmap_width)/2;
		picY = lp->font->max_bounds.ascent+
			lp->font->max_bounds.descent+
			lp->label_margin;
		lblX = (w->core.width-lp->label_width)/2;
		lblY = lp->font->max_bounds.ascent;
	}
	else { /* lower */
		picX = (w->core.width-lp->pixmap_width)/2;
		picY = 0;
		lblX = (w->core.width-lp->label_width)/2;
		lblY = lp->pixmap_height+lp->label_margin+
			lp->font->max_bounds.ascent;
	}

	XClearWindow(XtDisplay(w),XtWindow(w));
	if (lp->pixmap_is_bitmap) {
		if (lp->pixmap != AiNullPixmap) {
			XCopyPlane(XtDisplay(w),
				   lp->pixmap,
				   XtWindow(w),
				   lp->picture_GC,
				   0,0,
				   lp->pixmap_width,lp->pixmap_height,
				   picX,picY,
				   1);
		}
	}
	else {
		if (lp->pixmap != AiNullPixmap) {
			XCopyArea(XtDisplay(w),
				  lp->pixmap,
				  XtWindow(w),
				  lp->picture_GC,
				  0,0,
				  lp->pixmap_width,lp->pixmap_height,
				  picX,picY);
		}
	}
	XDrawString(XtDisplay(w),XtWindow(w),
		    lp->label_GC,
		    lblX,lblY,
		    lp->label,len);
	if (lp->status & ST_SELECT)
		XDrawRectangle(XtDisplay(w),XtWindow(w),lp->picture_GC,
			       0,0,w->core.width-1, w->core.height-1);
}


static void
Destroy(new)
Widget new;
{
	LabeledPictureWidget lw = (LabeledPictureWidget)new;
	LabeledPicturePart *w = &(lw->labeled_picture);

	if (w->pixmap != AiNullPixmap)
		XFreePixmap(XtDisplay(new),w->pixmap);
	XtReleaseGC(new,w->label_GC);
	XtReleaseGC(new,w->picture_GC);
}


/*
 *   Actions
 */
 
static void
SelectIt(w)
LabeledPictureWidget w;
{
	if (w->labeled_picture.sensitive) {
		w->labeled_picture.status |= ST_SELECT;
		Redisplay(w);
	}
}

static void
UnSelectIt(w)
LabeledPictureWidget w;
{
	if (w->labeled_picture.sensitive) {
		w->labeled_picture.status &= ~ST_SELECT;
		Redisplay(w);
	}
}

static void
ActivateIt(w,event,params,nparams)
LabeledPictureWidget w;
XEvent *event;
String *params;
Cardinal *nparams;
{
	UnSelectIt(w);
	if (w->labeled_picture.sensitive) {
		XtCallCallbacks(w, AiNcallback, (caddr_t)event);
	}
}

/***********  Functions **********/

/* Get label value */
char *
AiGetLabel(w)
LabeledPictureWidget w;
{
	return w->labeled_picture.label;
}

void
AiChangeLabel(w,label)
LabeledPictureWidget w;
String label;
{
	w->labeled_picture.label = label;
}

Pixmap
AiChangePixmap(w,p,resizeWidth,resizeHeight)
LabeledPictureWidget w;
Pixmap p;
Boolean resizeWidth,resizeHeight;
{
	LabeledPicturePart *lp = &(w->labeled_picture);
	Pixmap old = lp->pixmap;
	int width = w->core.width, height = w->core.height;

	lp->pixmap = p;
	AdjustSize(w,True);
	
	if (width != w->core.width || height != w->core.height) {
		XtResizeWidget(w,
			       w->core.width,
			       w->core.height,
			       w->core.border_width);
	}
	return old;
}

Pixmap
AiChangeBitmapData(wig,bit,width,height,resizeWidth,resizeHeight)
Widget wig;
char *bit;
int width,height;
Boolean resizeWidth,resizeHeight;
{
	Pixmap p;
	LabeledPictureWidget w = (LabeledPictureWidget)wig;
	
	p = XCreatePixmapFromBitmapData(
		XtDisplay(wig),
		XtWindow(wig),
		bit,
		width,
		height,
		w->labeled_picture.foreground,
		w->core.background_pixel,
		XtScreen(w)->root_depth);
	if (p == 0)
		return 0;

	return AiChangePixmap(wig,p,resizeWidth,resizeHeight);
}


syntax highlighted by Code2HTML, v. 0.9.1