/*
 * Copyright(c) 1992 Bell Communications Research, Inc. (Bellcore)
 *                        All rights reserved
 * Permission to use, copy, modify and distribute this material for
 * any purpose and without fee is hereby granted, provided that the
 * above copyright notice and this permission notice appear in all
 * copies, and that the name of Bellcore not be used in advertising
 * or publicity pertaining to this material without the specific,
 * prior written permission of an authorized representative of
 * Bellcore.
 *
 * BELLCORE MAKES NO REPRESENTATIONS AND EXTENDS NO WARRANTIES, EX-
 * PRESS OR IMPLIED, WITH RESPECT TO THE SOFTWARE, INCLUDING, BUT
 * NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
 * FITNESS FOR ANY PARTICULAR PURPOSE, AND THE WARRANTY AGAINST IN-
 * FRINGEMENT OF PATENTS OR OTHER INTELLECTUAL PROPERTY RIGHTS.  THE
 * SOFTWARE IS PROVIDED "AS IS", AND IN NO EVENT SHALL BELLCORE OR
 * ANY OF ITS AFFILIATES BE LIABLE FOR ANY DAMAGES, INCLUDING ANY
 * LOST PROFITS OR OTHER INCIDENTAL OR CONSEQUENTIAL DAMAGES RELAT-
 * ING TO THE SOFTWARE.
 *
 * $Id: add.c,v 1.1 1999/08/04 21:24:01 danny Exp $
*/

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

#include <stdio.h>
#include <Xm/Form.h>
#include <Xm/RowColumn.h>
#include <Xm/PushB.h>
#include <Xm/Scale.h>
#include <Xm/MessageB.h>
#include <Xbae/Matrix.h>

/*
 * Add multiple rows or columns at a time.
 * Use option menu to choose whether to add rows or columns.
 * Use the scale to choose how many to add.
 * Select a cell with Shift-Button1 to select the row or column to add after.
 */

void LoadMatrix();
void RowOrientationCB();
void ColumnOrientationCB();
void AddCB();
void DelCB();
void SetNumberCB();
void SetPositionCB();
void defaultCB();
void enterCB();

typedef enum {
    RowOrientation,
    ColumnOrientation
} Orientation;

typedef struct {
    Widget matrix;
    Orientation orientation;
    int number;
    int row, column;
    int num_rows, num_columns;
    Dimension fixed_rows, fixed_columns;
    Dimension trailing_fixed_rows, trailing_fixed_columns;    
} AddDataRec, *AddData;

int
main(argc, argv)
int argc;
char *argv[];
{
    Widget toplevel, form, rc, menu, row, column, option, number, add, del;
    AddDataRec addData;
    XtAppContext app;
    Arg args[8];
    int n;
#ifdef USE_EDITRES
    extern void _XEditResCheckMessages();
#endif

    toplevel = XtVaAppInitialize(&app, "Add",
				 NULL, 0,
				 &argc, argv,
				 NULL,
				 NULL);
#ifdef USE_EDITRES
    XtAddEventHandler( toplevel, (EventMask)0, True,
                       _XEditResCheckMessages, NULL);
#endif
    /*
     * Create a Form to hold everything
     */
    form = XtCreateManagedWidget("form",
                                 xmFormWidgetClass, toplevel,
                                 NULL, 0);

    rc = XtCreateManagedWidget("rc",
			       xmRowColumnWidgetClass, form,
			       NULL, 0);

    /*
     * Create a menu for use in an OptionMenu
     */
    menu = XmCreatePulldownMenu(rc, "menu", NULL, 0);

    /*
     * Create a menu button to select row orientation
     */
    row = XtVaCreateManagedWidget("row",
				  xmPushButtonWidgetClass, menu,
				  NULL);
    XtAddCallback(row, XmNactivateCallback, RowOrientationCB,
		  (XtPointer)&addData);

    /*
     * Create a menu button to select column orientation
     */
    column = XtVaCreateManagedWidget("column",
				     xmPushButtonWidgetClass, menu,
				     NULL);
    XtAddCallback(column, XmNactivateCallback, ColumnOrientationCB,
		  (XtPointer)&addData);

    /*
     * Setup and create the option menu
     */
    n = 0;
    XtSetArg(args[n], XmNsubMenuId, menu); n++;
    XtSetArg(args[n], XmNmenuHistory, row); n++;
    option = XmCreateOptionMenu(rc, "option", args, n);
    XtManageChild(option);

    addData.orientation = RowOrientation;

    /*
     * Create a scale to determine how many to add
     */
    number = XtVaCreateManagedWidget(
	"number", xmScaleWidgetClass, rc,
	XmNvalue, 1,
	XmNleftAttachment, XmATTACH_WIDGET,
	XmNleftWidget, option,
	XmNbottomAttachment, XmATTACH_OPPOSITE_WIDGET,
	XmNbottomWidget, option,
	NULL);

    XtAddCallback(number, XmNvalueChangedCallback, SetNumberCB,
		  (XtPointer)&addData);
    addData.number = 1;

    /*
     * Create a button to do the adding
     */
    add = XtVaCreateManagedWidget(
	"add", xmPushButtonWidgetClass, rc,
	XmNleftAttachment, XmATTACH_WIDGET,
	XmNleftWidget, number,
	XmNbottomAttachment, XmATTACH_OPPOSITE_WIDGET,
	XmNbottomWidget, number,
	NULL);

    XtAddCallback(add, XmNactivateCallback, AddCB, (XtPointer)&addData);

    del = XtVaCreateManagedWidget(
	"Delete", xmPushButtonWidgetClass, rc,
	XmNleftAttachment, XmATTACH_WIDGET,
	XmNleftWidget, add,
	XmNbottomAttachment,XmATTACH_OPPOSITE_WIDGET,
	XmNbottomWidget, add,
	NULL);

    XtAddCallback(del, XmNactivateCallback, DelCB, (XtPointer)&addData);

    /*
     * Create a Matrix widget.
     */

    addData.matrix = XtVaCreateManagedWidget(
	"mw", xbaeMatrixWidgetClass, form,
	XmNleftAttachment, XmATTACH_FORM,
	XmNrightAttachment, XmATTACH_FORM,
	XmNbottomAttachment, XmATTACH_FORM,
	XmNtopAttachment, XmATTACH_WIDGET,
	XmNtopWidget, rc,
	NULL);

    XtAddCallback(addData.matrix, XmNselectCellCallback, SetPositionCB,
		  (XtPointer)&addData);
    XtAddCallback(addData.matrix, XmNdefaultActionCallback, defaultCB,
		  (XtPointer)&addData);
    XtAddCallback(addData.matrix, XmNenterCellCallback, enterCB,
		  (XtPointer)&addData);

    XtVaGetValues(addData.matrix,
		  XmNrows, &addData.num_rows,
		  XmNcolumns, &addData.num_columns,
		  XmNfixedRows, &addData.fixed_rows,
		  XmNfixedColumns, &addData.fixed_columns,
		  XmNtrailingFixedRows, &addData.trailing_fixed_rows,
		  XmNtrailingFixedColumns, &addData.trailing_fixed_columns,
		  NULL);

    addData.row = 0;
    addData.column = 0;
    /*
     * Load the matrix with default values
     */
    LoadMatrix(&addData);

    XtRealizeWidget(toplevel);
    XtAppMainLoop(app);

    return 0;
}

/* ARGSUSED */
void
RowOrientationCB(w, addData, call_data)
Widget w;
AddData addData;
XtPointer call_data;
{
    if (addData->orientation != RowOrientation) {
	addData->orientation = RowOrientation;
	XbaeMatrixDeselectColumn(addData->matrix, addData->column);
    }
}

/* ARGSUSED */
void
ColumnOrientationCB(w, addData, call_data)
Widget w;
AddData addData;
XtPointer call_data;
{
    if (addData->orientation != ColumnOrientation)
    {
	addData->orientation = ColumnOrientation;
	XbaeMatrixDeselectRow(addData->matrix, addData->row);
    }
}

/* ARGSUSED */
void
enterCB(w, addData, cbs)
Widget w;
AddData addData;
XbaeMatrixEnterCellCallbackStruct *cbs;
{
#if 0
    int rows, cols;
    char str[16];
#endif

    if( cbs->row == 1 || cbs->row == 4 || cbs->column == 1 )
    {
	cbs->doit = True;
#if 0
	XtVaGetValues(w, XmNrows, &rows, XmNcolumns, &cols, NULL);
	(void)sprintf(str, "%d", rand() % 16381);
	XbaeMatrixSetCell(w, rows - 1, cols / 2, str);
	printf("set cell to %s\n", str);
#endif
    }
    else
	cbs->doit = False;
}

/* ARGSUSED */
void
defaultCB(w, addData, cbs)
Widget w;
AddData addData;
XbaeMatrixDefaultActionCallbackStruct *cbs;
{
    static Widget dialog = NULL;
    XmString xm_string;
    Arg args[1];
    char buf[ 64 ];
    
    /* set the label for the dialog */
    sprintf( buf, "Double click in row %d, column %d", cbs->row, cbs->column );
    xm_string = XmStringCreateSimple( buf );
    XtSetArg(args[0], XmNmessageString, xm_string);

    /* Create the InformationDialog as child of w */
    if( !dialog )
    	dialog = XmCreateInformationDialog(w, "info", args, 1);
    else
        XtSetValues(dialog, args, 1);

    /* no longer need the compound string, free it */
    XmStringFree(xm_string);

    /* manage the dialog */
    XtManageChild(dialog);
    XtPopup(XtParent(dialog), XtGrabNone);
   
}

/* ARGSUSED */
void
AddCB(w, addData, call_data)
Widget w;
AddData addData;
XtPointer call_data;
{
    String *rows, *columns;
    short *widths;
    char buf[BUFSIZ];
    int i, j, length;

    /*
     * Add rows or columns
     */

    switch (addData->orientation) {

    case RowOrientation:
	rows = (String *) XtMalloc(addData->num_columns * addData->number *
				   sizeof(String));
	for (i = 0; i < addData->number; i++)
	    for (j = 0; j < addData->num_columns; j++) {
		sprintf(buf, "addrow %d, %d", i, j);
		rows[i * addData->num_columns + j] = XtNewString(buf);
	    }

	XbaeMatrixAddRows(addData->matrix, addData->num_rows - 1,
			  rows, NULL, NULL, addData->number);

	length = addData->num_columns * addData->number;
	for (i = 0; i < length; i++)
	    XtFree(rows[i]);
	XtFree((XtPointer)rows);

	addData->num_rows += addData->number;
	break;

    case ColumnOrientation:
	widths = (short *) XtMalloc(addData->number * sizeof(short));
	for (i = 0; i < addData->number; i++)
	    widths[i] = 15;
	
	columns = (String *) XtMalloc(addData->num_rows * addData->number *
				      sizeof(String));
	for (i = 0; i < addData->num_rows; i++)
	    for (j = 0; j < addData->number; j++) {
		sprintf(buf, "addcol %d, %d", i, j);
		columns[i * addData->number + j] = XtNewString(buf);
	    }

	XbaeMatrixAddColumns(addData->matrix, addData->num_columns - 1,
			     columns, NULL, widths, NULL, NULL, NULL, NULL,
			     addData->number);

	XtFree((XtPointer)widths);
	length = addData->num_rows * addData->number;
	for (i = 0; i < length; i++)
	    XtFree(columns[i]);
	XtFree((XtPointer)columns);

	addData->num_columns += addData->number;
	break;
    }
}

/* ARGSUSED */
void
DelCB(w, addData, call_data)
Widget w;
AddData addData;
XtPointer call_data;
{
    /*
     * Delete rows or columns
     */

    switch (addData->orientation) {

    case RowOrientation:
	if( addData->num_rows - addData->fixed_rows -
	    addData->trailing_fixed_rows - addData->number <= 0 )
	    return;

	XbaeMatrixDeleteRows(addData->matrix, addData->num_rows -
			     addData->number, addData->number);

	addData->num_rows -= addData->number;
	break;

    case ColumnOrientation:
	if( addData->num_columns - addData->fixed_columns -
	    addData->trailing_fixed_columns - addData->number <= 0 )
	    return;
    
	XbaeMatrixDeleteColumns(addData->matrix, addData->num_columns -
				addData->number, addData->number);

	addData->num_columns -= addData->number;
	break;
    }
}

/* ARGSUSED */
void
SetNumberCB(w, addData, call_data)
Widget w;
AddData addData;
XmScaleCallbackStruct *call_data;
{
    addData->number = call_data->value;
}

/* ARGSUSED */
void
SetPositionCB(w, addData, call_data)
Widget w;
AddData addData;
XbaeMatrixSelectCellCallbackStruct *call_data;
{
    switch (addData->orientation) {
    case RowOrientation:
	XbaeMatrixUnhighlightRow(addData->matrix, addData->row);
	addData->row = call_data->row;
	addData->column = call_data->column;
	XbaeMatrixHighlightRow(addData->matrix, addData->row);
	break;
    case ColumnOrientation:
	XbaeMatrixUnhighlightColumn(addData->matrix, addData->column);
	addData->row = call_data->row;
	addData->column = call_data->column;
	XbaeMatrixHighlightColumn(addData->matrix, addData->column);
	break;
    }

}

void
LoadMatrix(addData)
AddData addData;
{
    int i, j;
    char buf[BUFSIZ];
    String *rowArrays, **cells;

    cells = (String **)XtMalloc(addData->num_rows * sizeof(String *));
    rowArrays = (String *)XtMalloc(addData->num_rows * addData->num_columns *
				   sizeof(String));

    for (i = 0; i < addData->num_rows; i++) {
	cells[i] = &rowArrays[i * addData->num_columns];
	    for (j = 0; j < addData->num_columns; j++) {
		sprintf(buf, "%d, %d", i, j);
		rowArrays[i * addData->num_columns + j] = XtNewString(buf);
	    }
    }

    XtVaSetValues(addData->matrix,
		  XmNcells,	cells,
		  NULL);

    for (i = 0; i < addData->num_rows; i++)
	for (j = 0; j < addData->num_columns; j++)
	    XtFree(rowArrays[i * addData->num_columns + j]);

    XtFree((XtPointer) rowArrays);
    XtFree((XtPointer) cells);
}
