/*
 * AUTHOR: Jay Schmidgall <jay@spdbump.sungardss.com>
 *
 * WARNING: This example uses some private xbae routines.
 *
 * $Id: select-push.c,v 1.1 1999/08/04 21:24:22 danny Exp $
 */

/*
 * WARNING
 * This program demonstrates a possible use of the individual
 * cell and row shadow types resources. However, it does this
 * in a rather improper fashion, by using a few of the matrix's
 * internal functions. But the overall effect is rather slick :).
 */


#ifdef HAVE_CONFIG_H
#include <XbaeConfig.h>
#endif

#include <Xbae/Matrix.h>
#include <Xbae/MatrixP.h>
#include <Xbae/Utils.h>
#include <Xbae/Draw.h>
#include <Xbae/Shadow.h>
#include <Xm/Form.h>
#include <Xm/Label.h>

typedef struct {
    int	last_selected;
    int armed;
} SelectedData;

void
LoadMatrix(w)
Widget w;
{
    String *cells[10];
    static String rows[10][10] = {
	{ "0,Zero", "0,One",  "0,Two",    "0,Three",  "0,Four",
	  "0,Five", "0,Six",  "0, Seven", "0, Eight", "0, Nine" },
	{ "1,Zero", "1,One",  "1,Two",    "1,Three",  "1,Four",
	  "1,Five", "1,Six",  "1, Seven", "1, Eight", "1, Nine" },
	{ "2,Zero", "2,One",  "2,Two",    "2,Three",  "2,Four",
	  "2,Five", "2,Six",  "2, Seven", "2, Eight", "2, Nine" },
	{ "3,Zero", "3,One",  "3,Two",    "3,Three",  "3,Four",
	  "3,Five", "3,Six",  "3, Seven", "3, Eight", "3, Nine" },
	{ "4,Zero", "4,One",  "4,Two",    "4,Three",  "4,Four",
	  "4,Five", "4,Six",  "4, Seven", "4, Eight", "4, Nine" },
	{ "5,Zero", "5,One",  "5,Two",    "5,Three",  "5,Four",
	  "5,Five", "5,Six",  "5, Seven", "5, Eight", "5, Nine" },
	{ "6,Zero", "6,One",  "6,Two",    "6,Three",  "6,Four",
	  "6,Five", "6,Six",  "6, Seven", "6, Eight", "6, Nine" },
	{ "7,Zero", "7,One",  "7,Two",    "7,Three",  "7,Four",
	  "7,Five", "7,Six",  "7, Seven", "7, Eight", "7, Nine" },
	{ "8,Zero", "8,One",  "8,Two",    "8,Three",  "8,Four",
	  "8,Five", "8,Six",  "8, Seven", "8, Eight", "8, Nine" },
	{ "9,Zero", "9,One",  "9,Two",    "9,Three",  "9,Four",
	  "9,Five", "9,Six",  "9, Seven", "9, Eight", "9, Nine" }
    };

    cells[0] = &rows[0][0];
    cells[1] = &rows[1][0];
    cells[2] = &rows[2][0];
    cells[3] = &rows[3][0];
    cells[4] = &rows[4][0];
    cells[5] = &rows[5][0];
    cells[6] = &rows[6][0];
    cells[7] = &rows[7][0];
    cells[8] = &rows[8][0];
    cells[9] = &rows[9][0];
    
    XtVaSetValues(w,
		  XmNcells,     cells,
		  NULL);
}


void drawRowShadow(mw, row, redraw)
XbaeMatrixWidget mw;
int row;
unsigned char redraw;
{
    int col;
    for(col=0;col<mw->matrix.columns;col++)
	xbaeDrawCell(mw, row, col);
}


void
cbSelect(w, client, call)
Widget w;
XtPointer client;
XtPointer call;
{
    XbaeMatrixWidget mw = (XbaeMatrixWidget) w;
    XbaeMatrixSelectCellCallbackStruct *cbs =
	(XbaeMatrixSelectCellCallbackStruct *) call;
    SelectedData *sd = (SelectedData*) client;
    int row, column;

    if (1 != cbs->num_params) return;

    XbaeMatrixGetCurrentCell(w, &row, &column);
    XbaeMatrixCancelEdit(w, True);

    xbaeClearCell(mw, row, column);
    xbaeDrawCell(mw, row, column);
    drawRowShadow(mw, row);

    if (0 == strcmp(cbs->params[0], "select"))
    {
	unsigned char *shadows = NULL;
	int rows;

	XtVaGetValues(w,
		      XmNrowShadowTypes, &shadows,
		      XmNrows, &rows,
		      NULL);

	if (row != sd->last_selected)
	    drawRowShadow(mw, row);
	    
	if (-1 != sd->last_selected)
	    shadows[sd->last_selected] = XmSHADOW_OUT;

	if (cbs->row != sd->last_selected)
	    shadows[cbs->row] = (XmSHADOW_OUT == shadows[cbs->row] ?
				 XmSHADOW_IN : XmSHADOW_OUT);

	if (-1 != sd->last_selected)
	    drawRowShadow(mw, sd->last_selected);
	
	if (cbs->row != sd->last_selected)
	{
	    drawRowShadow(mw, cbs->row);
	    sd->last_selected = cbs->row;
	}
	else
	    sd->last_selected = -1;
    }
    else if (0 == strcmp(cbs->params[0], "edit"))
    {
	XbaeMatrixEditCell(w, cbs->row, cbs->column);
    }
}


void
cbActivate(w, client, call)
Widget w;
XtPointer client;
XtPointer call;
{
    XbaeMatrixCommitEdit((Widget) client, True);
}



void
cbFocus(w, client, call)
Widget w;
XtPointer client;
XtPointer call;
{
    Widget ww = (Widget) client;
    XbaeMatrixWidget mw = (XbaeMatrixWidget) ww;
    int row, column;

    XbaeMatrixGetCurrentCell(ww, &row, &column);
    mw->matrix.grid_type = XmGRID_SHADOW_OUT;
    xbaeDrawCell(mw, row, column);
    mw->matrix.grid_type = XmGRID_ROW_SHADOW;
}


void
actCancel(w, event, params, num_params)
Widget w;
XEvent *event;
String *params;
Cardinal *num_params;
{
    int row, column;
    XbaeMatrixWidget mw;

    if (!XtIsSubclass(w, xbaeMatrixWidgetClass))
    {
	w = XtParent(w);
	if (!XtIsSubclass(w, xbaeMatrixWidgetClass))
	    return;
    }
    mw = (XbaeMatrixWidget) w;

    XbaeMatrixGetCurrentCell(w, &row, &column);
    XbaeMatrixCancelEdit(w, True);

    xbaeClearCell(mw, row, column);
    xbaeDrawCell(mw, row, column);
    drawRowShadow(mw, row);
}


void
actConfigure(w, event, params, num_params)
Widget w;
XEvent *event;
String *params;
Cardinal *num_params;
{
    int row, column;
    XbaeMatrixWidget mw;

    if (!XtIsSubclass(w, xbaeMatrixWidgetClass))
    {
	w = XtParent(w);
	if (!XtIsSubclass(w, xbaeMatrixWidgetClass))
	    return;
    }

    mw = (XbaeMatrixWidget) w;    
    XbaeMatrixGetCurrentCell(w, &row, &column);
    if (xbaeIsCellVisible(mw, row, column))
    {
	XmProcessTraversal(mw->matrix.text_field, XmTRAVERSE_CURRENT);
	mw->matrix.grid_type = XmGRID_SHADOW_OUT;
	xbaeDrawCell(mw, row, column);
	mw->matrix.grid_type = XmGRID_ROW_SHADOW;
    }
}


void
actLeave(w, event, params, num_params)
Widget w;
XEvent *event;
String *params;
Cardinal *num_params;
{
    SelectedData *sd;
    XbaeMatrixWidget mw;
    unsigned char *shadows = NULL;

    if (!XtIsSubclass(w, xbaeMatrixWidgetClass))
    {
	w = XtParent(w);
	if (!XtIsSubclass(w, xbaeMatrixWidgetClass))
	    return;
    }
    mw = (XbaeMatrixWidget) w;

    XtVaGetValues((Widget) mw,
		  XmNuserData, &sd,
		  XmNrowShadowTypes, &shadows,
		  NULL);

    if (shadows && (-1 != sd->armed))
    {
	shadows[sd->armed] = XmSHADOW_OUT;
	drawRowShadow((XbaeMatrixWidget) w, sd->armed);
	sd->last_selected = -1;
    }
}


void
actEnter(w, event, params, num_params)
Widget w;
XEvent *event;
String *params;
Cardinal *num_params;
{
    SelectedData *sd;
    XbaeMatrixWidget mw;
    unsigned char *shadows = NULL;

    if (!XtIsSubclass(w, xbaeMatrixWidgetClass))
    {
	w = XtParent(w);
	if (!XtIsSubclass(w, xbaeMatrixWidgetClass))
	    return;
    }
    mw = (XbaeMatrixWidget) w;
    
    XtVaGetValues((Widget) mw,
		  XmNuserData, &sd,
		  XmNrowShadowTypes, &shadows,
		  NULL);
    
    if (shadows && (-1 != sd->armed))
    {
	shadows[sd->armed] = XmSHADOW_IN;
	drawRowShadow((XbaeMatrixWidget) w, sd->armed);
	sd->last_selected = sd->armed;
    }
}



void
actButton1Down(w, event, params, num_params)
Widget w;
XEvent *event;
String *params;
Cardinal *num_params;
{
    int row, column;
    SelectedData *sd;
    XbaeMatrixWidget mw;

    if (!XtIsSubclass(w, xbaeMatrixWidgetClass))
    {
	w = XtParent(w);
	if (!XtIsSubclass(w, xbaeMatrixWidgetClass))
	    return;
    }
    mw = (XbaeMatrixWidget) w;
    
    XtVaGetValues(w, XmNuserData, &sd, NULL);
    if (XbaeMatrixGetEventRowColumn(w, event, &row, &column))
	sd->armed = row;
}

void
actButton1Motion(w, event, params, num_params)
Widget w;
XEvent *event;
String *params;
Cardinal *num_params;
{
    int row, column;
    SelectedData *sd;
    unsigned char *shadows = NULL;
    XbaeMatrixWidget mw;

    if (!XtIsSubclass(w, xbaeMatrixWidgetClass))
    {
	w = XtParent(w);
	if (!XtIsSubclass(w, xbaeMatrixWidgetClass))
	    return;
    }
    mw = (XbaeMatrixWidget) w;

    XtVaGetValues((Widget) mw,
		  XmNuserData, &sd,
		  XmNrowShadowTypes, &shadows,
		  NULL);

    if (sd && (-1 != sd->last_selected))
    {
	XbaeMatrixGetEventRowColumn((Widget) mw, event, &row, &column);
	if ((row != sd->armed) && (XmSHADOW_IN == shadows[sd->armed]))
	{
	    shadows[sd->armed] = XmSHADOW_OUT;
	    drawRowShadow((XbaeMatrixWidget) mw, sd->armed);
	}
	else if ((row == sd->armed) && (XmSHADOW_OUT == shadows[sd->armed]))
	{
	    shadows[sd->armed] = XmSHADOW_IN;
	    drawRowShadow((XbaeMatrixWidget) mw, sd->armed);
	}
    }
}

void
actButton1Up(w, event, params, num_params)
Widget w;
XEvent *event;
String *params;
Cardinal *num_params;
{
    SelectedData *sd;
    unsigned char *shadows = NULL;
    XbaeMatrixWidget mw;

    if (!XtIsSubclass(w, xbaeMatrixWidgetClass))
    {
	w = XtParent(w);
	if (!XtIsSubclass(w, xbaeMatrixWidgetClass))
	    return;
    }
    mw = (XbaeMatrixWidget) w;

    XtVaGetValues((Widget) mw,
		  XmNuserData, &sd,
		  XmNrowShadowTypes, &shadows,
		  NULL);
    
    if (-1 != sd->armed)
    {
	sd->armed = -1;
	if ((-1 != sd->last_selected) && (XmSHADOW_OUT == shadows[sd->last_selected]))
	    sd->last_selected = -1;
    }
}


int
main(argc, argv)
int argc;
char *argv[];
{
    Widget toplevel, form, label, mw, text;
    XmString xms;
    XtAppContext app;
    SelectedData *sd = XtNew(SelectedData);
    static XtActionsRec global_actions[] = {
	{ "actLeave" ,		actLeave },
	{ "actEnter" ,		actEnter },
	{ "actButton1Down",	actButton1Down },
	{ "actButton1Motion",	actButton1Motion },
	{ "actButton1Up",	actButton1Up },
	{ "actConfigure",	actConfigure },
	{ "actCancel",		actCancel }
    };
    static char text_translations[] =
	"#override\n\
Shift ~Ctrl ~Meta ~Alt <Key>Tab:    actCancel()\n\
~Ctrl ~Meta ~Alt <Key>Tab:	    actCancel()\n\
<Key>osfUp:			    actCancel()\n\
<Key>osfDown:			    actCancel()\n\
<Key>osfActivate:		    CommitEdit(True)\n\
~Shift ~Meta ~Alt <Key>Return:	    CommitEdit(True)\n\
<Key>osfCancel:			    actCancel()\n\
<ConfigureNotify>:		    actConfigure()\n\
Shift Ctrl ~Meta ~Alt <Key>Tab:	    actCancel()\n\
Ctrl ~Meta ~Alt <Key>Tab:	    actCancel()";

    unsigned char *row_shadows, **cell_shadows;
    int rows, columns, m;
#ifdef USE_EDITRES
    extern void _XEditResCheckMessages();
#endif


    sd->last_selected = -1;
    sd->armed = -1;

    toplevel = XtVaAppInitialize(&app, "Select-push",
				 NULL, 0,
				 &argc, argv,
				 NULL,
				 NULL);
#ifdef USE_EDITRES
    XtAddEventHandler( toplevel, (EventMask)0, True,
                       _XEditResCheckMessages, NULL);
#endif

    XtAppAddActions(app, global_actions, XtNumber(global_actions));

    form = XtVaCreateWidget("form", xmFormWidgetClass, toplevel,
			    NULL);

    xms = XmStringCreateLtoR("This matrix mimics a list of radio buttons. Button 1 can be used to \"press\" a row.\nShift-Button 1 allows you to edit a cell, though any cell in a fixed row or column\n is non-editable. Pressing Return accepts the edit. Editing another cell or selecting\nanother row or tabbing out of the cell cancels the edit.", XmFONTLIST_DEFAULT_TAG);
    label = XtVaCreateManagedWidget("label", xmLabelWidgetClass, form,
				    XmNtopAttachment,	XmATTACH_FORM,
				    XmNleftOffset,	4,
				    XmNleftAttachment,	XmATTACH_FORM,
				    XmNrightOffset,	4,
				    XmNrightAttachment,	XmATTACH_FORM,
				    XmNlabelString,	xms,
				    NULL);

    XmStringFree(xms);

    mw = XtVaCreateManagedWidget("mw", xbaeMatrixWidgetClass, form,
				 XmNtopOffset,		4,
				 XmNtopWidget,		label,
				 XmNtopAttachment,	XmATTACH_WIDGET,
				 XmNleftOffset,		4,
				 XmNleftAttachment,	XmATTACH_FORM,
				 XmNrightOffset,	4,
				 XmNrightAttachment,	XmATTACH_FORM,
				 XmNbottomOffset,	4,
				 XmNbottomAttachment,	XmATTACH_FORM,
				 XmNuserData,		(XtPointer) sd,
				 XmNtextTranslations,	
				 XtParseTranslationTable(text_translations),
				 NULL);
    
    XtVaGetValues(mw, XmNrows, &rows, XmNcolumns, &columns, NULL);
    row_shadows = (unsigned char *) XtMalloc(sizeof(unsigned char) * rows);
    memset((void*) row_shadows, XmSHADOW_OUT, sizeof(unsigned char) * rows);
    XtVaSetValues(mw, XmNrowShadowTypes, row_shadows, NULL);
    XtFree((XtPointer) row_shadows);

    cell_shadows = (unsigned char **) XtMalloc(sizeof(unsigned char *) * rows);
    for (m = 0; m < rows; m++)
    {
	cell_shadows[m] = (unsigned char *) XtMalloc(sizeof(unsigned char) * columns);
	memset((void*) cell_shadows[m], XmSHADOW_OUT, sizeof(unsigned char) * columns);
    }
    XtVaSetValues(mw, XmNcellShadowTypes, cell_shadows, NULL);
    for (m = 0; m < rows; m++)
	XtFree((XtPointer) cell_shadows[m]);
    XtFree((XtPointer) cell_shadows);

    XtVaGetValues(mw, XmNtextField, &text, NULL);
    XtAddCallback(text, XmNactivateCallback, cbActivate, (XtPointer) mw);
    XtAddCallback(text, XmNfocusCallback, cbFocus, (XtPointer) mw);

    
    XtAddCallback(mw, XmNselectCellCallback, cbSelect, (XtPointer) sd);
    
    LoadMatrix(mw);

    XtManageChild(form);
    XtRealizeWidget(toplevel);
    XtAppMainLoop(app);

    /*NOTREACHED*/
    return 0;
}

