/*
 * Copyright(c) 1992 Bell Communications Research, Inc. (Bellcore)
 * Copyright(c) 1995-99 Andrew Lister
 *
 *                        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.
 *
 * MatrixWidget Author: Andrew Wason, Bellcore, aw@bae.bellcore.com
 *
 * $Id: Public.c,v 1.1.1.1 2001/07/18 11:06:00 root Exp $
 */

/*
 * Public.c created by Andrew Lister (7 August, 1995)
 */

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

#include <Xm/Xm.h>
#include <Xm/ScrollBar.h>
#include "MatrixP.h"
#include "Shadow.h"
#include "Draw.h"
#include "ScrollMgr.h"
#include "Actions.h"
#include "Utils.h"
#include "Clip.h"
#include "MCreate.h"

/*
 * Public interface to set_cell method
 */
void
XbaeMatrixSetCell(w, row, column, value)
Widget w;
int row;
int column;
const String value;
{
    /*
     * Make sure w is a Matrix or a subclass
     */
    XtCheckSubclass(w, xbaeMatrixWidgetClass, NULL);

    /*
     * Call the set_cell method
     */
    (*((XbaeMatrixWidgetClass) XtClass(w))->matrix_class.set_cell)
        ((XbaeMatrixWidget)w, row, column, value, True);
}


/*
 * Public interface to edit_cell method
 */
void
XbaeMatrixEditCell(w, row, column)
Widget w;
int row, column;
{
    /*
     * Make sure w is a Matrix or a subclass
     */
    XtCheckSubclass(w, xbaeMatrixWidgetClass, NULL);

    /*
   * Call the edit_cell method
   */
    (*((XbaeMatrixWidgetClass) XtClass(w))->matrix_class.edit_cell)
        ((XbaeMatrixWidget)w, NULL, row, column, NULL, 0);

    XmProcessTraversal(TextChild(((XbaeMatrixWidget) w)), XmTRAVERSE_CURRENT);
}

/*
 * Public interface to select_cell method
 */
void
XbaeMatrixSelectCell(w, row, column)
Widget w;
int row, column;
{
    /*
     * Make sure w is a Matrix or a subclass
     */
    XtCheckSubclass(w, xbaeMatrixWidgetClass, NULL);

    /*
   * Call the select_cell method
   */
    (*((XbaeMatrixWidgetClass) XtClass(w))->matrix_class.select_cell)
        ((XbaeMatrixWidget)w, row, column);
}

/*
 * Public interface to select_row method
 */
void
XbaeMatrixSelectRow(w, row)
Widget w;
int row;
{
    /*
     * Make sure w is a Matrix or a subclass
     */
    XtCheckSubclass(w, xbaeMatrixWidgetClass, NULL);

    /*
   * Call the select_row method
   */
    (*((XbaeMatrixWidgetClass) XtClass(w))->matrix_class.select_row)
        ((XbaeMatrixWidget)w, row);
}

/*
 * Public interface to select_column method
 */
void
XbaeMatrixSelectColumn(w, column)
Widget w;
int column;
{
    /*
     * Make sure w is a Matrix or a subclass
     */
    XtCheckSubclass(w, xbaeMatrixWidgetClass, NULL);

    /*
   * Call the select_column method
   */
    (*((XbaeMatrixWidgetClass) XtClass(w))->matrix_class.select_column)
        ((XbaeMatrixWidget)w, column);
}

/*
 * Public interface to deselect_all method
 */
void
XbaeMatrixDeselectAll(w)
Widget w;
{
    /*
     * Make sure w is a Matrix or a subclass
     */
    XtCheckSubclass(w, xbaeMatrixWidgetClass, NULL);

    /*
     * Call the deselect_all method
     */
    (*((XbaeMatrixWidgetClass) XtClass(w))->matrix_class.deselect_all)
        ((XbaeMatrixWidget)w);
}

/*
 * Public interface to select_all method
 */
void
XbaeMatrixSelectAll(w)
Widget w;
{
    /*
     * Make sure w is a Matrix or a subclass
     */
    XtCheckSubclass(w, xbaeMatrixWidgetClass, NULL);

    /*
     * Call the deselect_all method
     */
    (*((XbaeMatrixWidgetClass) XtClass(w))->matrix_class.select_all)
        ((XbaeMatrixWidget)w);
}

/*
 * Public interface to deselect_cell method
 */
void
XbaeMatrixDeselectCell(w, row, column)
Widget w;
int row;
int column;
{
    /*
     * Make sure w is a Matrix or a subclass
     */
    XtCheckSubclass(w, xbaeMatrixWidgetClass, NULL);

    /*
     * Call the deselect_cell method
     */
    (*((XbaeMatrixWidgetClass) XtClass(w))->matrix_class.deselect_cell)
        ((XbaeMatrixWidget)w, row, column);
}

/*
 * Public interface to deselect_row method
 */
void
XbaeMatrixDeselectRow(w, row)
Widget w;
int row;
{
    /*
     * Make sure w is a Matrix or a subclass
     */
    XtCheckSubclass(w, xbaeMatrixWidgetClass, NULL);

    /*
   * Call the deselect_row method
   */
    (*((XbaeMatrixWidgetClass) XtClass(w))->matrix_class.deselect_row)
        ((XbaeMatrixWidget)w, row);
}

/*
 * Public interface to deselect_column method
 */
void
XbaeMatrixDeselectColumn(w, column)
Widget w;
int column;
{
    /*
     * Make sure w is a Matrix or a subclass
     */
    XtCheckSubclass(w, xbaeMatrixWidgetClass, NULL);

    /*
   * Call the deselect_column method
   */
    (*((XbaeMatrixWidgetClass) XtClass(w))->matrix_class.deselect_column)
        ((XbaeMatrixWidget)w, column);
}

/*
 * Public interface to get_cell method
 */
String
XbaeMatrixGetCell(w, row, column)
Widget w;
int row, column;
{
    /*
     * Make sure w is a Matrix or a subclass
     */
    XtCheckSubclass(w, xbaeMatrixWidgetClass, NULL);

    /*
     * Call the get_cell method
     */
    return (*((XbaeMatrixWidgetClass) XtClass(w))->matrix_class.get_cell)
        ((XbaeMatrixWidget)w, row, column);
}

/*
 * Public interface to commit_edit method
 */
Boolean
#if NeedFunctionPrototypes
XbaeMatrixCommitEdit(Widget w, Boolean unmap)
#else
XbaeMatrixCommitEdit(w, unmap)
Widget w;
Boolean unmap;
#endif
{
    /*
     * Make sure w is a Matrix or a subclass
     */
    XtCheckSubclass(w, xbaeMatrixWidgetClass, NULL);

    /*
   * Call the commit_edit method
   */
    return (*((XbaeMatrixWidgetClass) XtClass(w))->matrix_class.commit_edit)
        ((XbaeMatrixWidget)w, NULL, unmap);
}

/*
 * Public interface to cancel_edit method
 */
void
#if NeedFunctionPrototypes
XbaeMatrixCancelEdit(Widget w, Boolean unmap)
#else
XbaeMatrixCancelEdit(w, unmap)
Widget w;
Boolean unmap;
#endif
{
    /*
     * Make sure w is a Matrix or a subclass
     */
    XtCheckSubclass(w, xbaeMatrixWidgetClass, NULL);
    
    /*
     * Call the cancel_edit method
     */
    (*((XbaeMatrixWidgetClass) XtClass(w))->matrix_class.cancel_edit)
        ((XbaeMatrixWidget)w, unmap);
}

/*
 * Public interface to add_rows method
 */
void
XbaeMatrixAddRows(w, position, rows, labels, colors, num_rows)
Widget w;
int position;
String *rows;
String *labels;
Pixel *colors;
int num_rows;
{
    /*
     * Make sure w is a Matrix or a subclass
     */
    XtCheckSubclass(w, xbaeMatrixWidgetClass, NULL);

    /*
   * Call the add_rows method
   */
    (*((XbaeMatrixWidgetClass)XtClass(w))->matrix_class.add_rows)
        ((XbaeMatrixWidget)w, position, rows, labels, colors, NULL, num_rows);
}

/*
 * Public interface to delete_rows method
 */
void
XbaeMatrixDeleteRows(w, position, num_rows)
Widget w;
int position;
int num_rows;
{
    /*
     * Make sure w is a Matrix or a subclass
     */
    XtCheckSubclass(w, xbaeMatrixWidgetClass, NULL);

    /*
     * Call the delete_rows method
     */
    (*((XbaeMatrixWidgetClass) XtClass(w))->matrix_class.delete_rows)
        ((XbaeMatrixWidget)w, position, num_rows);
}

/*
 * Public interface to add_columns method
 */
void
XbaeMatrixAddColumns(w, position, columns, labels, widths, max_lengths,
                     alignments, label_alignments, colors, num_columns)
Widget w;
int position;
String *columns;
String *labels;
short *widths;
int *max_lengths;
unsigned char *alignments;
unsigned char *label_alignments;
Pixel *colors;
int num_columns;
{
    /*
     * Make sure w is a Matrix or a subclass
     */
    XtCheckSubclass(w, xbaeMatrixWidgetClass, NULL);

    /*
   * Call the add_columns method
   */
    (*((XbaeMatrixWidgetClass) XtClass(w))->matrix_class.add_columns)
        ((XbaeMatrixWidget)w, position, columns, labels, widths,
         max_lengths, alignments, label_alignments, colors, NULL, num_columns);
}

/*
 * Public interface to delete_columns method
 */
void
XbaeMatrixDeleteColumns(w, position, num_columns)
Widget w;
int position;
int num_columns;
{
    /*
     * Make sure w is a Matrix or a subclass
     */
    XtCheckSubclass(w, xbaeMatrixWidgetClass, NULL);

    /*
     * Call the delete_columns method
     */
    (*((XbaeMatrixWidgetClass) XtClass(w))->matrix_class.delete_columns)
        ((XbaeMatrixWidget)w, position, num_columns);
}

/*
 * Public interface to set_row_colors method
 */
void
XbaeMatrixSetRowColors(w, position, colors, num_colors)
Widget w;
int position;
Pixel *colors;
int num_colors;
{
    /*
     * Make sure w is a Matrix or a subclass
     */
    XtCheckSubclass(w, xbaeMatrixWidgetClass, NULL);

    /*
   * Call the set_row_colors method
   */
    (*((XbaeMatrixWidgetClass) XtClass(w))->matrix_class.set_row_colors)
        ((XbaeMatrixWidget)w, position, colors, num_colors, False);
}

/*
 * Public interface to set_column_colors method
 */
void
XbaeMatrixSetColumnColors(w, position, colors, num_colors)
Widget w;
int position;
Pixel *colors;
int num_colors;
{
    /*
     * Make sure w is a Matrix or a subclass
     */
    XtCheckSubclass(w, xbaeMatrixWidgetClass, NULL);

    /*
   * Call the set_column_colors method
   */
    (*((XbaeMatrixWidgetClass) XtClass(w))->matrix_class.set_column_colors)
        ((XbaeMatrixWidget)w, position, colors, num_colors, False);
}

/*
 * Public interface to set_cell_color method
 */
void
XbaeMatrixSetCellColor(w, row, column, color)
Widget w;
int row;
int column;
Pixel color;
{
    /*
     * Make sure w is a Matrix or a subclass
     */
    XtCheckSubclass(w, xbaeMatrixWidgetClass, NULL);

    /*
   * Call the set_cell_color method
   */
    (*((XbaeMatrixWidgetClass) XtClass(w))->matrix_class.set_cell_color)
        ((XbaeMatrixWidget)w, row, column, color, False);
}

/*
 * Public interface to set_row_colors method
 */
void
XbaeMatrixSetRowBackgrounds(w, position, colors, num_colors)
Widget w;
int position;
Pixel *colors;
int num_colors;
{
    /*
     * Make sure w is a Matrix or a subclass
     */
    XtCheckSubclass(w, xbaeMatrixWidgetClass, NULL);

    /*
   * Call the set_row_colors method
   */
    (*((XbaeMatrixWidgetClass) XtClass(w))->matrix_class.set_row_colors)
        ((XbaeMatrixWidget)w, position, colors, num_colors, True);
}

/*
 * Public interface to set_column_colors method
 */
void
XbaeMatrixSetColumnBackgrounds(w, position, colors, num_colors)
Widget w;
int position;
Pixel *colors;
int num_colors;
{
    /*
     * Make sure w is a Matrix or a subclass
     */
    XtCheckSubclass(w, xbaeMatrixWidgetClass, NULL);

    /*
   * Call the set_column_colors method
   */
    (*((XbaeMatrixWidgetClass) XtClass(w))->matrix_class.set_column_colors)
        ((XbaeMatrixWidget)w, position, colors, num_colors, True);
}

/*
 * Public interface to set_cell_color method
 */
void
XbaeMatrixSetCellBackground(w, row, column, color)
Widget w;
int row;
int column;
Pixel color;
{
    /*
     * Make sure w is a Matrix or a subclass
     */
    XtCheckSubclass(w, xbaeMatrixWidgetClass, NULL);

    /*
   * Call the set_cell_color method
   */
    (*((XbaeMatrixWidgetClass) XtClass(w))->matrix_class.set_cell_color)
        ((XbaeMatrixWidget)w, row, column, color, True);
}

/*
 * Help the user know what row & column he is in given an x & y (via an event).
 * Return True on success, False on failure.
 */
int
XbaeMatrixGetEventRowColumn(w, event, row, column)
Widget w;
XEvent * event;
int *row;
int *column;
{
    XbaeMatrixWidget mw;
    int x, y;
    CellType cell;

    if (!XtIsSubclass(w, xbaeMatrixWidgetClass))
        return False;

    /* Convert the event to the correct XY for the matrix widget. */
    mw = (XbaeMatrixWidget) w;
    if (!xbaeEventToXY(mw, event, &x, &y, &cell))
        return False;

    /* Convert the point to a row,column. If it does not pick a valid cell,
       then return. */
    if (!xbaeXYToRowCol(mw, &x, &y, row, column, cell))
        return False;

    return True;
}

/*
 * Public interface for xbaeEventToXY()
 */
Boolean
XbaeMatrixEventToXY(w, event, x, y)
Widget w;
XEvent *event;  
int *x;
int *y;
{
    XbaeMatrixWidget mw;
    CellType cell;

    if (!XtIsSubclass(w, xbaeMatrixWidgetClass))
        return False;

    mw = (XbaeMatrixWidget)w;

    if (!xbaeEventToXY(mw, event, x, y, &cell))
        return False;

    return True;
}

/*
 * Public interface for xbaeRowColToXY().  From Philip Aston
 * (philipa@parallax.co.uk)
 */
Boolean
XbaeMatrixRowColToXY(w, row, column, x, y)
Widget w;
int row;
int column;
int *x;
int *y;
{
    XbaeMatrixWidget mw;

    if (!XtIsSubclass(w, xbaeMatrixWidgetClass))
        return False;

    mw = (XbaeMatrixWidget)w;

    xbaeRowColToXY(mw, row, column, x, y);

    return True;
}

/*
 * Help the programmer to know what row & column we are currently at.
 * Set the row & column to -1 if bad widget.  Maybe the program will core. :)
 */
void
XbaeMatrixGetCurrentCell(w, row, column)
Widget w;
int *row;
int *column;
{
    XbaeMatrixWidget mw;

    if (!XtIsSubclass(w, xbaeMatrixWidgetClass))
        *row = *column = -1;
    else
    {
        mw = (XbaeMatrixWidget)w;
        *row = mw->matrix.current_row;
        *column = mw->matrix.current_column;
    }
}


/*
 * Allow the programmer to call the Expose method directly if he feels
 * that it is really needed.
 */
void
XbaeMatrixRefresh(w)
Widget w;
{
    XbaeMatrixWidget mw = (XbaeMatrixWidget)w;
    int x, y;
    
    if (mw->matrix.disable_redisplay)
        return;

    if (!XtIsSubclass(w, xbaeMatrixWidgetClass) || !XtIsRealized((Widget)mw))
        return;

    /*
     * We're about to flush scroll exposure events, so reset the
     * scroll managers.
     */
    xbaeSmDestroyScrollMgr(mw->matrix.matrix_scroll_mgr);
    xbaeSmDestroyScrollMgr(mw->matrix.clip_scroll_mgr);

    mw->matrix.matrix_scroll_mgr = xbaeSmCreateScrollMgr();
    mw->matrix.clip_scroll_mgr = xbaeSmCreateScrollMgr();
 
    /*
     * Don't respond to exposures.
     */
    mw->matrix.disable_redisplay = 1;
 
    /*
     * Flush pending expose events.
     */
    XmUpdateDisplay(w);
 
    /*
     * Respond to exposures.
     */
    mw->matrix.disable_redisplay = 0;
 
    /*
     * Generate expose events on the Matrix to force the redrawing.
     */
    x = 0;
    y = 0;
    if (mw->matrix.column_labels)
        XClearArea(XtDisplay(mw), XtWindow(mw), x, y, mw->core.width,
                   ROW_LABEL_OFFSET(mw), True);
    y += ROW_LABEL_OFFSET(mw);
    if (mw->matrix.row_labels)
        XClearArea(XtDisplay(mw), XtWindow(mw), x, y,
                   COLUMN_LABEL_OFFSET(mw), mw->core.height - x, True);
    x += COLUMN_LABEL_OFFSET(mw);
    if (mw->matrix.fixed_rows)
    {
        /* Clear the top clip completely */
        XClearArea(XtDisplay(mw), XtWindow(TopClip(mw)), 0, 0, 0, 0, True);

        /* Don't forget the corner areas! */
        if (mw->matrix.fixed_columns)
            XClearArea(XtDisplay(mw), XtWindow(mw), x, y,
                       LeftClip(mw)->core.width, TopClip(mw)->core.height,
                       True);
        if (mw->matrix.trailing_fixed_columns)
            XClearArea(XtDisplay(mw), XtWindow(mw),
                       RightClip(mw)->core.x, y, RightClip(mw)->core.width,
                       TopClip(mw)->core.height, True);
    }
    if (mw->matrix.fixed_columns)
        XClearArea(XtDisplay(mw), XtWindow(LeftClip(mw)), 0, 0, 0, 0, True);
    if (mw->matrix.trailing_fixed_columns)
        XClearArea(XtDisplay(mw), XtWindow(RightClip(mw)), 0, 0, 0, 0, True);
    if (mw->matrix.trailing_fixed_rows)
    {
        XClearArea(XtDisplay(mw), XtWindow(BottomClip(mw)), 0, 0, 0, 0, True);
        if (mw->matrix.fixed_columns)
            XClearArea(XtDisplay(mw), XtWindow(mw), x, BottomClip(mw)->core.y,
                       LeftClip(mw)->core.width, BottomClip(mw)->core.height,
                       True);
        if (mw->matrix.trailing_fixed_columns)
            XClearArea(XtDisplay(mw), XtWindow(mw),
                       RightClip(mw)->core.x, BottomClip(mw)->core.y,
                       RightClip(mw)->core.width, BottomClip(mw)->core.height,
                       True);
    }
    /*
     * The areas to the right and bottom of the matrix also need to be
     * exposed. First to do is the right hand side.
     */
    x = COLUMN_POSITION(mw, mw->matrix.columns-1) +
        COLUMN_WIDTH(mw, mw->matrix.columns-1);
    XClearArea(XtDisplay(mw), XtWindow(mw), x, 0, mw->core.width - x,
               mw->core.height, True);
    y = ClipChild(mw)->core.y + ClipChild(mw)->core.height +
        TRAILING_FIXED_ROW_HEIGHT(mw);
    XClearArea(XtDisplay(mw), XtWindow(mw), 0, y, mw->core.width,
               mw->core.height - y, True);
    XbaeClipRedraw(ClipChild(mw));
}

/*
 * Public interface for redrawing one cell
 */
void
XbaeMatrixRefreshCell(w, row, column)
Widget w;
int row;
int column;
{
    XbaeMatrixWidget mw = (XbaeMatrixWidget)w;

    if (!XtIsSubclass(w, xbaeMatrixWidgetClass))
        return;

    if (xbaeIsCellVisible(mw, row, column))
    {
        /* this doesn't seem to be necessary -cg 16/7/99 */
        /*xbaeClearCell(mw, row, column);*/
        xbaeDrawCell(mw, row, column);
    }
}

/*
 * Redraw an entire column
 */
void
XbaeMatrixRefreshColumn(w, column)
Widget w;
int column;
{
    XbaeMatrixWidget mw = (XbaeMatrixWidget)w;
    int row;
    int found = 0;

    if (!XtIsSubclass(w, xbaeMatrixWidgetClass))
        return;

    /* we attempt to be mildly efficient about this */
    if (xbaeIsColumnVisible(mw, column))
    {
        /* fixed always visible */
        for (row = 0; row < mw->matrix.fixed_rows; row++)
            xbaeDrawCell(mw, row, column);
        /* now the scrollable clip */
        for (; row < mw->matrix.rows - mw->matrix.trailing_fixed_rows; row++)
            if (xbaeIsRowVisible(mw, row))
            {
                found = 1;
                xbaeDrawCell(mw, row, column);
            }
            else if (found)
                break;  /* came to the end of the clip */
        /* and finally trailing fixed are always visible */
        for (row = mw->matrix.rows - mw->matrix.trailing_fixed_rows;
             row < mw->matrix.rows; row++)
            xbaeDrawCell(mw, row, column);
    }
}

/*
 * Redraw an entire row
 */
void
XbaeMatrixRefreshRow(w, row)
Widget w;
int row;
{
    XbaeMatrixWidget mw = (XbaeMatrixWidget)w;
    int column;
    int found = 0;

    if (!XtIsSubclass(w, xbaeMatrixWidgetClass))
        return;

    /* we attempt to be mildly efficient about this */
    if (xbaeIsRowVisible(mw, row))
    {
        /* fixed always visible */
        for (column = 0; column < mw->matrix.fixed_columns; column++)
            xbaeDrawCell(mw, row, column);
        /* now the scrollable clip */
        for (; column < mw->matrix.columns - mw->matrix.trailing_fixed_columns;
             column++)
            if (xbaeIsColumnVisible(mw, column))
            {
                found = 1;
                xbaeDrawCell(mw, row, column);
            }
            else if (found)
                break;  /* came to the end of the clip */
        /* and finally trailing fixed are always visible */
        for (column = mw->matrix.columns - mw->matrix.trailing_fixed_columns;
             column < mw->matrix.columns; column++)
            xbaeDrawCell(mw, row, column);
    }
}

/*
 *  XbaeMatrixVisibleRows()
 *
 *  This routine returns the number of rows that are visible in the matrix.
 *
 *  D. Craig Wilson  5-MAY-1995
 *      - Cloned from the local "xbaeAdjustTopRow(mw)" routine.
 */
int
XbaeMatrixVisibleRows(w)
Widget w;
{
    XbaeMatrixWidget    matrix = (XbaeMatrixWidget) w;

    int rows_visible = VISIBLE_HEIGHT(matrix) / ROW_HEIGHT(matrix);

    /*
     *  If we have less than one full row visible, then count it as a full row.
     */
    if (rows_visible == 0)
        rows_visible = 1;

    /*
     *  rows_visible might be inaccurate since Clip may not have been resized.
     *  Test this routine and see if we need to call XbaeMatrixRefresh() to
     *  ensure accuracy.
     */
    else if (rows_visible > matrix->matrix.rows)
        rows_visible = matrix->matrix.rows;

    return rows_visible;

} /* XbaeMatrixVisibleRows */



/*
 *  XbaeMatrixVisibleColumns()
 *
 *  This routine returns the number of columns that are visible in the matrix.
 *
 *  D. Craig Wilson  5-MAY-1995
 *      - Cloned from the local "xbaeAdjustTopRow(mw)" routine.
 */
int
XbaeMatrixVisibleColumns (w)
Widget w;
{
    XbaeMatrixWidget    matrix = (XbaeMatrixWidget)w;

    int left_column;
    int right_column;

    xbaeGetVisibleColumns(matrix, &left_column, &right_column);

    return right_column - left_column + 1;

} /* XbaeMatrixVisibleColumns */

/*
 * Get per-cell user data
 */
XtPointer
XbaeMatrixGetCellUserData(w, row, column)
Widget w;
int row;
int column;
{
    XbaeMatrixWidget mw;
    XtPointer data;

    if (!XtIsSubclass(w, xbaeMatrixWidgetClass))
        return NULL;

    mw = (XbaeMatrixWidget) w;
    if (mw->matrix.cell_user_data)
        data = mw->matrix.cell_user_data[row][column];
    else
        return NULL;

    return data;
}

/*
 * Set per-cell user data
 */
void
XbaeMatrixSetCellUserData(w, row, column, data)
Widget w;
int row;
int column;
XtPointer data;
{
    XbaeMatrixWidget mw;

    if (!XtIsSubclass(w, xbaeMatrixWidgetClass))
        return;

    mw = (XbaeMatrixWidget) w;
    if (! mw->matrix.cell_user_data)
    {
        XtPointer **copy;
        register int i;

        copy = (XtPointer **) XtMalloc(mw->matrix.rows * sizeof(XtPointer*));

        for (i = 0; i < mw->matrix.rows; i++)
            copy[i] = (XtPointer*) XtCalloc(mw->matrix.columns,
                                            sizeof(XtPointer));

        mw->matrix.cell_user_data = copy;
    }

    mw->matrix.cell_user_data[row][column] = data;
    return;
}

/*
 * Get per-row user data
 */
XtPointer
XbaeMatrixGetRowUserData(w, row)
Widget w;
int row;
{
    XbaeMatrixWidget mw;
    XtPointer data;

    if (!XtIsSubclass(w, xbaeMatrixWidgetClass))
        return NULL;

    mw = (XbaeMatrixWidget) w;
    if (mw->matrix.row_user_data)
        data = mw->matrix.row_user_data[row];
    else
        return NULL;

    return data;
}


/*
 * Set per-row user data
 */
void
XbaeMatrixSetRowUserData(w, row, data)
Widget w;
int row;
XtPointer data;
{
    XbaeMatrixWidget mw;

    if (!XtIsSubclass(w, xbaeMatrixWidgetClass))
        return;

    mw = (XbaeMatrixWidget) w;
    if (! mw->matrix.row_user_data)
    {
        XtPointer *copy;

        copy = (XtPointer *) XtCalloc(mw->matrix.rows, sizeof(XtPointer));

        mw->matrix.row_user_data = copy;
    }

    mw->matrix.row_user_data[row]= data;
    return;
}

/*
 * Get per-column user data
 */
XtPointer
XbaeMatrixGetColumnUserData(w, column)
Widget w;
int column;
{
    XbaeMatrixWidget mw;
    XtPointer data;

    if (!XtIsSubclass(w, xbaeMatrixWidgetClass))
        return NULL;

    mw = (XbaeMatrixWidget) w;
    if (mw->matrix.column_user_data)
        data = mw->matrix.column_user_data[column];
    else
        return NULL;

    return data;
}

/*
 * Set per-column user data
 */
void
XbaeMatrixSetColumnUserData(w, column, data)
Widget w;
int column;
XtPointer data;
{
    XbaeMatrixWidget mw;

    if (!XtIsSubclass(w, xbaeMatrixWidgetClass))
        return;

    mw = (XbaeMatrixWidget) w;
    if (! mw->matrix.column_user_data)
    {
        XtPointer *copy;

        copy = (XtPointer *) XtCalloc(mw->matrix.columns, sizeof(XtPointer));

        mw->matrix.column_user_data = copy;
    }

    mw->matrix.column_user_data[column]= data;
    return;
}

#if CELL_WIDGETS
/*
 * Set per-cell widget
 */
void
XbaeMatrixSetCellWidget(w, row, column, widget)
Widget w;
int row;
int column;
Widget widget;
{
    XbaeMatrixWidget mw;

    if (!XtIsSubclass(w, xbaeMatrixWidgetClass))
        return;

    mw = (XbaeMatrixWidget) w;

    if (! mw->matrix.cell_widgets)
    {
        Widget **copy;
        register int i;

        copy = (Widget **) XtMalloc(mw->matrix.rows * sizeof(Widget*));

        for (i = 0; i < mw->matrix.rows; i++)
            copy[i] = (Widget*) XtCalloc(mw->matrix.columns, sizeof(Widget));

        mw->matrix.cell_widgets = copy;
    }

    mw->matrix.cell_widgets[row][column] = widget;
    return;
}
#endif

Boolean
XbaeMatrixIsRowSelected(w, row)
Widget w;
int row;
{
    int col;
    XbaeMatrixWidget mw;

    if (!XtIsSubclass(w, xbaeMatrixWidgetClass))
        return False;

    mw = (XbaeMatrixWidget) w;

    if (row < 0 || row >= mw->matrix.rows)
    {
        XtAppContext appcontext = XtWidgetToApplicationContext(w);
        XtAppError(appcontext,
                   "Invalid row passed to XbaeMatrixIsRowSelected()");
        return False;
    }

    if (!mw->matrix.selected_cells)
        return False;

    /*
     * Check all the cells in the row
     */
    for (col = 0 ; col < mw->matrix.columns ; col++)
        if (! mw->matrix.selected_cells[row][col])
            return False;

    /*
     * Return success
     */
    return True;
}

Boolean
XbaeMatrixIsColumnSelected(w, col)
Widget w;
int col;
{
    int row;
    XbaeMatrixWidget mw;

    if (!XtIsSubclass(w, xbaeMatrixWidgetClass))
        return False;

    mw = (XbaeMatrixWidget) w;

    if (col < 0 || col >= mw->matrix.columns)
    {
        XtAppContext appcontext = XtWidgetToApplicationContext(w);
        XtAppError(appcontext,
                   "Invalid column passed to XbaeMatrixIsColumnSelected()");
        return False;
    }

    if (!mw->matrix.selected_cells)
        return False;

    /*
     * Check all the cells in the row
     */
    for (row = 0 ; row < mw->matrix.rows ; row++)
        if (! mw->matrix.selected_cells[row][col])
            return False;

    /*
     * Return success
     */
    return True;
}

Boolean
XbaeMatrixIsCellSelected(w, row, column)
Widget w;
int row;
int column;
{
    XbaeMatrixWidget mw;

    if (!XtIsSubclass(w, xbaeMatrixWidgetClass))
        return False;

    mw = (XbaeMatrixWidget) w;

    if (column < 0 || column >= mw->matrix.columns || row < 0 ||
        row >= mw->matrix.rows)
    {
        XtAppContext appcontext = XtWidgetToApplicationContext(w);
        XtAppError(
            appcontext,
            "Invalid coordinates passed to XbaeMatrixIsCellSelected()");
        return False;
    }

    if (!mw->matrix.selected_cells)
        return False;

    if (! mw->matrix.selected_cells[row][column])
        return False;

    /*
     * Return success
     */
    return True;
}

int
XbaeMatrixFirstSelectedRow(w)
Widget w;
{
    int i;
    XbaeMatrixWidget mw;

    if (!XtIsSubclass(w, xbaeMatrixWidgetClass))
        return -1;

    mw = (XbaeMatrixWidget) w;

    if (!mw->matrix.selected_cells)
        return -1;

    /*
     * Linear search for first selected
     */
    for (i = 0 ; i < mw->matrix.rows ; i++)
        if (XbaeMatrixIsRowSelected(w, i))
            return i;
    /*
     * No selection - return an invalid row id
     */
    return -1;
}

int
XbaeMatrixFirstSelectedColumn(w)
Widget w;
{
    int i;
    XbaeMatrixWidget mw;

    if (!XtIsSubclass(w, xbaeMatrixWidgetClass))
        return -1;

    mw = (XbaeMatrixWidget) w;

    if (!mw->matrix.selected_cells)
        return -1;

    /*
     * Linear search for first selected
     */
    for (i = 0 ; i < mw->matrix.columns ; i++)
        if (XbaeMatrixIsColumnSelected(w, i))
            return i;
    /*
     * No selection - return an invalid row id
     */
    return -1;
}

void
XbaeMatrixFirstSelectedCell(w, row, column)
Widget w;
int *row;
int *column;
{
    int i, j;
    XbaeMatrixWidget mw;

    if (!XtIsSubclass(w, xbaeMatrixWidgetClass))
    {
        *row = *column = -1;
        return;
    }

    mw = (XbaeMatrixWidget) w;

    if (!mw->matrix.selected_cells)
    {
        *row = -1;
        *column = -1;
        return;
    }

    for (i = 0; i < mw->matrix.rows; i++)
        for (j = 0; j < mw->matrix.columns; j++)
            if (mw->matrix.selected_cells[ i ][ j ])
            {
                *row = i;
                *column = j;
                return;
            }
    *row = *column = -1;
}

int
XbaeMatrixGetNumSelected(w)
Widget w;
{
    XbaeMatrixWidget mw;

    if (!XtIsSubclass(w, xbaeMatrixWidgetClass))
        return 0;

    mw = (XbaeMatrixWidget) w;

    return mw->matrix.selected_cells ? mw->matrix.num_selected_cells : 0;
}


int
XbaeMatrixNumColumns(w)
Widget w;
{
    XbaeMatrixWidget mw;

    if (!XtIsSubclass(w, xbaeMatrixWidgetClass))
        return 0;

    mw = (XbaeMatrixWidget) w;

    return mw->matrix.columns;
}


int
XbaeMatrixNumRows(w)
Widget w;
{
    XbaeMatrixWidget mw;

    if (!XtIsSubclass(w, xbaeMatrixWidgetClass))
        return 0;

    mw = (XbaeMatrixWidget) w;

    return mw->matrix.rows;
}

#if XmVersion >= 1002
void
XbaeMatrixUnhighlightAll(w)
Widget w;
{
    XbaeMatrixWidget mw;
    int row, column;

    if (!XtIsSubclass(w, xbaeMatrixWidgetClass))
        return;
    mw = (XbaeMatrixWidget) w;
    
    if (!mw->matrix.highlighted_cells)
        return;

    mw->matrix.highlight_location = UnhighlightAll;

    for (row = 0; row < mw->matrix.rows; row++)
    {
        for (column = 0; column < mw->matrix.columns; column++)
        {
            /*
             * If the cell is visible and highlighted
             */        
            if (mw->matrix.highlighted_cells[row][column] &&
                xbaeIsCellVisible(mw, row, column))
                xbaeDrawCell(mw, row, column);
            mw->matrix.highlighted_cells[row][column] = HighlightNone;
        }
    }

    mw->matrix.highlight_location = HighlightNone;
}


void
XbaeMatrixHighlightCell(w, row, column)
Widget w;
int row;
int column;
{
    XbaeMatrixWidget mw;

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

    if (row >= mw->matrix.rows || row < 0 ||
        column >= mw->matrix.columns || column < 0)
    {
        XtAppWarningMsg(
            XtWidgetToApplicationContext((Widget) mw),
            "highlightCell", "badIndex", "XbaeMatrix",
            "XbaeMatrix: Row or column out of bounds for HighlightCell.",
            NULL, 0);
        return;
    }

    /*
     * Scroll the cell onto the screen
     */
    if (mw->matrix.scroll_select)
        xbaeMakeCellVisible(mw, row, column);

    if (!mw->matrix.highlighted_cells)
        xbaeCopyHighlightedCells(mw);

    /*
     * Establish location -- must be after scroll,
     * otherwise may not redraw properly
     */
    mw->matrix.highlight_location = HighlightCell;

    /*
     * If the cell is not already highlighted
     */
    if (!(mw->matrix.highlighted_cells[row][column] & HighlightCell))
    {
        mw->matrix.highlighted_cells[row][column] |= HighlightCell;

        /*
         * Only redraw if cell is visible
         */
        if (xbaeIsCellVisible(mw, row, column))
        {
            int x, y;
            Window win;

            /*
             * Get the correct window
             */
            win = xbaeGetCellWindow(mw, &w, row, column);

            /*
             * Convert row,column coordinates to the
             * coordinates relative to the correct window
             */
            xbaeRowColToXY(mw, row, column, &x, &y);

            DRAW_HIGHLIGHT(XtDisplay(mw), win, 
                           mw->manager.highlight_GC,
                           x + mw->matrix.cell_shadow_thickness,
                           y + mw->matrix.cell_shadow_thickness,
                           COLUMN_WIDTH(mw, column) -
                           (2 * mw->matrix.cell_shadow_thickness),
                           ROW_HEIGHT(mw) -
                           (2 * mw->matrix.cell_shadow_thickness),
                           mw->matrix.cell_highlight_thickness,
                           LineSolid);
        }
    }
    mw->matrix.highlight_location = HighlightNone;
}


void
XbaeMatrixUnhighlightCell(w, row, column)
Widget w;
int row;
int column;
{
    XbaeMatrixWidget mw;

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

    if (row >= mw->matrix.rows || row < 0 ||
        column >= mw->matrix.columns || column < 0)
    {
        XtAppWarningMsg(
            XtWidgetToApplicationContext((Widget) mw),
            "unhighlightCell", "badIndex", "XbaeMatrix",
            "XbaeMatrix: Row or column out of bounds for UnhighlightCell.",
            NULL, 0);
        return;
    }

    if (!mw->matrix.highlighted_cells)
        return;
    
    mw->matrix.highlight_location = UnhighlightCell;
    
    if (xbaeIsCellVisible(mw, row, column))
        xbaeDrawCell(mw, row, column);

    mw->matrix.highlighted_cells[row][column] &= ~HighlightCell;

    mw->matrix.highlight_location = HighlightNone;
}


void
XbaeMatrixHighlightRow(w, row)
Widget w;
int row;
{
    XbaeMatrixWidget mw;
    int j, lc, rc;
    Boolean visible;
    unsigned char highlight;

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

    if (row >= mw->matrix.rows || row < 0)
    {
        XtAppWarningMsg(
            XtWidgetToApplicationContext((Widget) mw),
            "highlightRow", "badIndex", "XbaeMatrix",
            "XbaeMatrix: Row out of bounds for HighlightRow.",
            NULL, 0);
        return;
    }

    if (!mw->matrix.highlighted_cells)
        xbaeCopyHighlightedCells(mw);

    /*
     * Scroll the row onto the screen
     */
    if (mw->matrix.scroll_select)
        xbaeMakeRowVisible(mw, row);

    /*
     * Establish location -- must be after scroll,
     * otherwise may not redraw properly
     */
    mw->matrix.highlight_location = HighlightRow;

    /*
     * For each cell in the row, if the cell is not already highlighted,
     * highlight it and redraw it if it is visible
     */
     
    visible = xbaeIsRowVisible(mw, row);
    xbaeGetVisibleColumns(mw, &lc, &rc);
    highlight = (IN_GRID_ROW_MODE(mw) ? HighlightRow : HighlightOther);

    for (j = 0; j < mw->matrix.columns; j++)
    {
        if (!(mw->matrix.highlighted_cells[row][j] & highlight))
        {
            mw->matrix.highlighted_cells[row][j] |= highlight;
                    
            if (visible)
                xbaeDrawCell(mw, row, j);
        }
    }

    mw->matrix.highlight_location = HighlightNone;
}


void
XbaeMatrixUnhighlightRow(w, row)
Widget w;
int row;
{
    XbaeMatrixWidget mw;
    int column, lc, rc;
    Boolean visible;
    unsigned char highlight;

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

    if (row >= mw->matrix.rows || row < 0)
    {
        XtAppWarningMsg(
            XtWidgetToApplicationContext((Widget) mw),
            "highlightRow", "badIndex", "XbaeMatrix",
            "XbaeMatrix: Row out of bounds for UnhighlightRow.",
            NULL, 0);
        return;
    }

    if (!mw->matrix.highlighted_cells)
        return;
    
    mw->matrix.highlight_location = UnhighlightRow;

    visible = xbaeIsRowVisible(mw, row);

    /*
     * For each cell in the row, if the cell is highlighted,
     * unhighlight it and redraw it if it is visible
     */
     
    xbaeGetVisibleColumns(mw, &lc, &rc);
    highlight = (IN_GRID_ROW_MODE(mw) ? HighlightRow : HighlightOther);
    for (column = 0; column < mw->matrix.columns; column++)
    {
        if (mw->matrix.highlighted_cells[row][column] & highlight)
        {
            if (visible)
                xbaeDrawCell(mw, row, column);
            mw->matrix.highlighted_cells[row][column] &= ~highlight;
        }
    }

    mw->matrix.highlight_location = HighlightNone;
}


void
XbaeMatrixHighlightColumn(w, column)
Widget w;
int column;
{
    XbaeMatrixWidget mw;
    int row, tr, br;
    Boolean visible;
    unsigned char highlight;

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

    if (column >= mw->matrix.columns || column < 0)
    {
        XtAppWarningMsg(
            XtWidgetToApplicationContext((Widget) mw),
            "highlightColumn", "badIndex", "XbaeMatrix",
            "XbaeMatrix: Column out of bounds for HighlightColumn.",
            NULL, 0);
        return;
    }
    
    if (!mw->matrix.highlighted_cells)
        xbaeCopyHighlightedCells(mw);
    /*
     * Scroll the row onto the screen
     */
    if (mw->matrix.scroll_select)
        xbaeMakeColumnVisible(mw, column);

    mw->matrix.highlight_location = HighlightColumn;
    
    /*
     * For each cell in the column, if the cell is not already highlighted,
     * highlight it and redraw it if it is visible
     */
     
    visible = xbaeIsColumnVisible(mw, column);
    xbaeGetVisibleRows(mw, &tr, &br);
    highlight =        (IN_GRID_COLUMN_MODE(mw) ? HighlightColumn : HighlightOther);
    for (row = 0; row < mw->matrix.rows; row++)
    {
        if (!(mw->matrix.highlighted_cells[row][column] & highlight))
        {
            mw->matrix.highlighted_cells[row][column] |= highlight;
            
            if (visible)
                xbaeDrawCell(mw, row, column);
        }
    }
    mw->matrix.highlight_location = HighlightNone;
}


void
XbaeMatrixUnhighlightColumn(w, column)
Widget w;
int column;
{
    XbaeMatrixWidget mw;
    int row, tr, br;
    Boolean visible;
    unsigned char highlight;

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

    if (column >= mw->matrix.columns || column < 0)
    {
        XtAppWarningMsg(
            XtWidgetToApplicationContext((Widget) mw),
            "highlightColumn", "badIndex", "XbaeMatrix",
            "XbaeMatrix: Column out of bounds for UnhighlightColumn.",
            NULL, 0);
        return;
    }

    if (!mw->matrix.highlighted_cells)
        return;
    
    mw->matrix.highlight_location = UnhighlightColumn;

    visible = xbaeIsColumnVisible(mw, column);

    /*
     * For each cell in the row, if the cell is highlighted,
     * unhighlight it and redraw it if it is visible.
     */
    xbaeGetVisibleRows(mw, &tr, &br);
    highlight = (IN_GRID_COLUMN_MODE(mw) ? HighlightColumn : HighlightOther);
    for (row = 0; row < mw->matrix.rows; row++)
    {
        if (mw->matrix.highlighted_cells[row][column] & highlight)
        {
            if (visible)
                xbaeDrawCell(mw, row, column);
            mw->matrix.highlighted_cells[row][column] &= ~highlight;
        }
    }
    mw->matrix.highlight_location = HighlightNone;
}
#endif

void
XbaeMatrixDisableRedisplay(w)
Widget w;
{
    XbaeMatrixWidget mw;

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

    mw->matrix.disable_redisplay++ ;
}


void
#if NeedFunctionPrototypes
XbaeMatrixEnableRedisplay(Widget w, Boolean redisplay)
#else
XbaeMatrixEnableRedisplay(w, redisplay)
Widget w;
Boolean redisplay;
#endif
{
    XbaeMatrixWidget mw;

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

    if (mw->matrix.disable_redisplay)
        mw->matrix.disable_redisplay--;

#undef FORCE_REDISPLAY_IF_TRUE
#ifndef FORCE_REDISPLAY_IF_TRUE
    if (redisplay && mw->matrix.disable_redisplay == 0)
        XbaeMatrixRefresh(w);
#else
    if (redisplay)
    {
        mw->matrix.disable_redisplay = 0;
        XbaeMatrixRefresh(w);
    }
#endif
}

/*
 * Public interface for xbaeMakeCellVisible()
 */
void
XbaeMatrixMakeCellVisible(w, row, column)
Widget w;
int row;
int column;
{
    XbaeMatrixWidget mw;

    if (!XtIsSubclass(w, xbaeMatrixWidgetClass))
        return;

    mw = (XbaeMatrixWidget) w;

    xbaeMakeCellVisible(mw, row, column);
}

/*
 * Public interface for xbaeIsRowVisible()
 */
Boolean
XbaeMatrixIsRowVisible(w, row)
Widget w;
int row;
{
    XbaeMatrixWidget mw;

    if (!XtIsSubclass(w, xbaeMatrixWidgetClass))
        return False;

    mw = (XbaeMatrixWidget) w;

    return xbaeIsRowVisible(mw, row);
}

/*
 * Public interface for xbaeIsColumnVisible()
 */
Boolean
XbaeMatrixIsColumnVisible(w, col)
Widget w;
int col;
{
    XbaeMatrixWidget mw;

    if (!XtIsSubclass(w, xbaeMatrixWidgetClass))
        return False;

    mw = (XbaeMatrixWidget) w;

    return xbaeIsColumnVisible(mw, col);
}

/*
 * Public interface for xbaeIsCellVisible()
 */
Boolean
XbaeMatrixIsCellVisible(w, row, col)
Widget w;
int row;
int col;
{
    XbaeMatrixWidget mw;

    if (!XtIsSubclass(w, xbaeMatrixWidgetClass))
        return False;

    mw = (XbaeMatrixWidget) w;

    return xbaeIsCellVisible(mw, row, col);
}

/*
 *  XbaeMatrixVisibleCells()
 *
 *  This routine returns the range of cells that are visible in the matrix.
 *
 */
void
XbaeMatrixVisibleCells(w, top_row, bottom_row, left_column, right_column)
Widget w;
int *top_row;
int *bottom_row;
int *left_column;
int *right_column;
{
    XbaeMatrixWidget mw;

    if (!XtIsSubclass(w, xbaeMatrixWidgetClass))
        return;

    mw = (XbaeMatrixWidget) w;

    xbaeGetVisibleRows(mw, top_row, bottom_row);
    xbaeGetVisibleColumns(mw, left_column, right_column);

}

/*
 * Get the label of the column passed here.
 */
String
XbaeMatrixGetColumnLabel(w, column)
Widget w;
int column;
{
    XbaeMatrixWidget mw;

    if (!XtIsSubclass(w, xbaeMatrixWidgetClass))
        return NULL;

    mw = (XbaeMatrixWidget)w;
        
    if (!(mw->matrix.column_labels) || column > mw->matrix.columns)
        return NULL;
    else
        return mw->matrix.column_labels[column];
}

/*
 * Get the label of the row passed here.
 */
String
XbaeMatrixGetRowLabel(w, row)
Widget w;
int row;
{
    XbaeMatrixWidget mw;

    if (!XtIsSubclass(w, xbaeMatrixWidgetClass))
        return NULL;

    mw = (XbaeMatrixWidget)w;
        
    if (!(mw->matrix.row_labels) || row > mw->matrix.rows)
        return NULL;
    else
        return mw->matrix.row_labels[row];
}

void
XbaeMatrixSetColumnLabel(w, column, value)
Widget w;
int column;
String value;
{
    XbaeMatrixWidget mw;
    ColumnLabelLines lines;
    String copy;
    
    if (!XtIsSubclass(w, xbaeMatrixWidgetClass) || !value)
        return;

    mw = (XbaeMatrixWidget) w;

    /*
     * Setting a column label when none are defined or changing the number
     * of lines in a column label would cause the need for a redraw of the
     * widget so we won't allow it.
     */
    if (!mw->matrix.column_labels)
    {
        XtAppWarningMsg(
            XtWidgetToApplicationContext((Widget) mw),
            "setColumnLabel", "noLabels", "XbaeMatrix",
            "XbaeMatrix: Cannot set column labels when none defined",
            NULL, 0);
        return;
    }

    lines = (ColumnLabelLines)XtMalloc(sizeof(ColumnLabelLinesRec));
    copy = XtNewString(value);
    xbaeParseColumnLabel(copy, lines);

    if (lines->lines != mw->matrix.column_label_lines[column].lines)
    {
        XtAppWarningMsg(
            XtWidgetToApplicationContext((Widget) mw),
            "setColumnLabel", "changeLines", "XbaeMatrix",
            "XbaeMatrix: Cannot change number of lines when setting column labels",
            NULL, 0);
        XtFree((XtPointer)copy);
        XtFree((XtPointer)lines->lengths);
        XtFree((XtPointer)lines);
        return;
    }        
    /*
     * OK to make the change
     */
    XtFree((XtPointer) mw->matrix.column_labels[column]);
    XtFree((XtPointer) mw->matrix.column_label_lines[column].lengths);

    mw->matrix.column_labels[column] = copy;
    mw->matrix.column_label_lines[column] = *lines;

    /*
     * Redraw the column label if it is visible.  No expose event is
     * generated by simply changing the label
     */
    if (xbaeIsColumnVisible(mw, column))
    {
        /*
         * Don't generate expose events and let xbaeRedrawLabelsAndFixed
         * work it out - we know where we need to draw
         */
        XClearArea(XtDisplay(mw), XtWindow(mw), COLUMN_LABEL_OFFSET(mw) +
                   COLUMN_POSITION(mw, column), 0, COLUMN_WIDTH(mw, column),
                   COLUMN_LABEL_HEIGHT(mw), False);
        xbaeDrawColumnLabel(mw, column, False);
    }
    return;
}

void
XbaeMatrixSetRowLabel(w, row, value)
Widget w;
int row;
String value;
{
    XbaeMatrixWidget mw;

    if (!XtIsSubclass(w, xbaeMatrixWidgetClass))
        return;

    mw = (XbaeMatrixWidget) w;

    if (!mw->matrix.row_labels || !value)
    {
        XtAppWarningMsg(
            XtWidgetToApplicationContext((Widget) mw),
            "setRowLabel", "noLabels", "XbaeMatrix",
            "XbaeMatrix: Cannot set row labels when none defined",
            NULL, 0);
        return;
    }
    XtFree((XtPointer) mw->matrix.row_labels[row]);
    mw->matrix.row_labels[row] = XtNewString(value);

    if (xbaeIsRowVisible(mw, row))
    {
        int y;

        if (IS_LEADING_FIXED_ROW(mw, row))
            y = ROW_LABEL_OFFSET(mw) + ROW_HEIGHT(mw) * row;
        else if (IS_TRAILING_FIXED_ROW(mw, row))
            y = TRAILING_FIXED_ROW_LABEL_OFFSET(mw) + ROW_HEIGHT(mw) *
                (row - TRAILING_VERT_ORIGIN(mw));
        else
            y = ROW_LABEL_OFFSET(mw) + ROW_HEIGHT(mw) *
                (row - VERT_ORIGIN(mw));

        XClearArea(XtDisplay(mw), XtWindow(mw), 0, y, ROW_LABEL_WIDTH(mw),
                   ROW_HEIGHT(mw), False);
        
        xbaeDrawRowLabel(mw, row, False);
    }
}

Widget
XbaeCreateMatrix(parent, name, args, ac) 
Widget parent;
String name;
ArgList args;
Cardinal ac;
{ 
    return XtCreateWidget(name, xbaeMatrixWidgetClass, parent, args, ac); 
} 

