/* buttonmatrix.c */


#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <X11/keysym.h>
#include "lui.h"



#define MIN(A,B)  ( (A) < (B) ? (A) : (B) )


/*
 * NOTES:
 *   Each button in the matrix can have a different color.  This unique
 *     color is only used when the button is turned on.  The X color for
 *     the button is only allocated while the button is on to minimize
 *     colormap allocations.
 */




static void draw_button( LUI_BUTTON_MATRIX *bm, int item, int row, int col )
{
   int x, y;
   int width, height;

   width = bm->bwidth;
   height = bm->bheight;
   x = col * width;
   y = row * height;

   if (bm->labels[item][col]) {
      int len;

/* MJK 12.04.98 */
/* 03Feb98  Phil McDonald       use white on dark or blue-dominated colors */
      GC	text_gc;
      text_gc = ((bm->color[item][col][0] < 0.6) &&
                 (bm->color[item][col][1] < 0.6)) ? LUI_GC_white : LUI_GC_black;

      LUI_DrawFrame( bm->mainwindow, x, y, width-1, height-1, 1,
                     !bm->state[item][col] );

      len = MIN( strlen(bm->labels[item][col]), bm->maxchars );

      if (bm->state[item][col]) {
         /* button is "on" */
         XSetForeground( LUI_Display, LUI_Gc, bm->pixel[item][col] );
         XFillRectangle( LUI_Display, bm->mainwindow, LUI_Gc,
                         x+1, y+1, width-3, height-3 );

         XDrawString( LUI_Display, bm->mainwindow, text_gc,
                     x+2, y+LUI_Font_yoff+1,
                     bm->labels[item][col], len );
/* MJK 12.04.98 */
/*         XDrawString( LUI_Display, bm->mainwindow, LUI_GC_black,
                     x+2, y+LUI_Font_yoff+1,
                     bm->labels[item][col], len );
*/

      }
      else {
         /* button is "off" */
         XFillRectangle( LUI_Display, bm->mainwindow, LUI_GC_gray,
                        x+1, y+1, width-3, height-3 );
         XDrawString( LUI_Display, bm->mainwindow, LUI_GC_black,
                     x+2, y+LUI_Font_yoff+1,
                     bm->labels[item][col], len );
      }
   }
   else {
      /* empty button */
      XFillRectangle( LUI_Display, bm->mainwindow, LUI_GC_gray,
                      x, y, width-1, height-1 );
   }

   XDrawLine( LUI_Display, bm->mainwindow, LUI_GC_black,
              x, y+height-1, x+width-1, y+height-1 );
   XDrawLine( LUI_Display, bm->mainwindow, LUI_GC_black,
              x+width-1, y, x+width-1, y+height-1 );

}



static void draw_matrix( LUI_BUTTON_MATRIX *bm )
{
   int i, j;

   for (i=0;i<bm->viewrows;i++) {
      for (j=0;j<bm->columns;j++) {
         int item = i + bm->toprow;
         if (item<bm->rows) {
            draw_button( bm, item, i, j );
         }
      }
   }
}



static void set_scrollbar( LUI_BUTTON_MATRIX *bm )
{
   double size, pos;

   if (bm->rows <= bm->viewrows) {
      size = 100.0;
      pos = 0.0;
   }
   else {
      size = 100.0 * (double) bm->viewrows / (double) bm->rows;
      pos = 100.0 * bm->toprow / (double) (bm->rows - bm->viewrows);
   }
   LUI_ScrollBarSet( bm->scrollbar, size, pos );
}

void LUI_ButtonMatrixRedraw( LUI_BUTTON_MATRIX *bm)
{
   draw_matrix(bm);
   set_scrollbar(bm);
}



/*
 * Convert a window (x,y) coordinate into a button (row,column).
 */
static void xy_to_rowcol( LUI_BUTTON_MATRIX *bm, int x, int y,
                         int *row, int *col )
{
   *row = y / bm->bheight + bm->toprow;
   *col = x / bm->bwidth;
}



static int matrix_event( LUI_BUTTON_MATRIX *bm, XEvent *event )
{
   switch (event->type) {
      case Expose:
         draw_matrix( bm );
         break;

      case ButtonPress:
         if (bm->callback) {
            int row, col;
            xy_to_rowcol( bm, event->xbutton.x, event->xbutton.y, &row, &col );
/*
            int row = event->xbutton.y / bm->bheight + bm->toprow;
            int col = event->xbutton.x / bm->bwidth;
*/
            if (row < bm->viewrows+bm->toprow) {
               if (bm->labels[row][col]) {
                  (*bm->callback)( bm, row, col, event->xbutton.button );
               }
            }
         }
         break;

      case KeyPress:
         /* Scroll button matrix if too many to see all at once */
         if (bm->rows > bm->viewrows) {
            KeySym keysym;
            XComposeStatus compose;
            char buffer[100];

            XLookupString( &event->xkey, buffer, 100, &keysym, &compose );

            if (keysym==XK_Up && bm->toprow>0) {
               bm->toprow--;
               draw_matrix( bm );
               set_scrollbar( bm );
            }
            else if (keysym==XK_Down && bm->toprow < bm->rows - bm->viewrows) {
               bm->toprow++;
               draw_matrix( bm );
               set_scrollbar( bm );
            }
         }
         break;

      default:
         printf("Error in matrix_event: unexpected event\n");
   }
   return 1;
}



static int sb_event( LUI_SCROLLBAR *sb, float pos )
{
   LUI_BUTTON_MATRIX *bm;
   int oldtop;

   bm = sb->userdata;

   oldtop = bm->toprow;

   bm->toprow = (int) (pos * (bm->rows - bm->viewrows) / 100.0);

   if (oldtop!=bm->toprow) {
      draw_matrix( bm );
   }
   return 1;
}



void LUI_ButtonMatrixDestroy( bm )
LUI_BUTTON_MATRIX *bm;
{
   int i, j;

   /* free the color allocations */
   for (i=0;i<bm->rows;i++) {
      for (j=0;j<bm->columns;j++) {
         if (bm->pixel[i][j]!=0) {
            LUI_FreeColor( bm->pixel[i][j] );
         }

/* MJK 12.04.98 */
/* 24Nov97  Phil McDonald */
         if (bm->labels[i][j]) free (bm->labels[i][j]);

      }
   }

   LUI_ScrollBarDestroy( bm->scrollbar );
   LUI_EventRemove( bm->mainwindow );
   XDestroyWindow( LUI_Display, bm->mainwindow );
   free( bm );
}



LUI_BUTTON_MATRIX *LUI_ButtonMatrixCreate( Window parent, int x, int y,
                                           int width, int height,
                                           int columns )
{
   LUI_BUTTON_MATRIX *bm;
   int xx, yy;

   LUI_LayoutCheck( &x, &y, &width, &height );

   /* allocate struct w/ all fields zeroed */
   bm = (LUI_BUTTON_MATRIX *) calloc( 1, sizeof(LUI_BUTTON_MATRIX) );

   bm->scrollbar = LUI_ScrollBarCreate( parent, x, y, 16, height, 1 );
   LUI_UnlinkWidgetFromWindow( parent, bm->scrollbar );
   bm->scrollbar->userdata = bm;
   LUI_ScrollBarCallback( bm->scrollbar, sb_event );

   bm->mainwindow = XCreateSimpleWindow( LUI_Display, parent,
                                     x+18, y, width-18, height-2,
                                     1, LUI_Color_black, LUI_Color_gray );

   {
      int nx = x+18, ny = y, nw = width-18, nh = height-2;
      LUI_LayoutCheck( &nx, &ny, &nw, &nh );
   }

   bm->x = x+18;
   bm->y = y;
   bm->width = width - 18;
   bm->height = height;
   bm->rows = 0;
   bm->columns = columns;

   bm->toprow = 0;
   bm->bwidth = bm->width / bm->columns;
   bm->bheight = LUI_Font_height + 4;
   bm->viewrows = height / bm->bheight;

   {
      static char str[] = "WWWWWWWWWWWWWW";
      int dir, ascent, descent;
      XCharStruct overall;
      bm->maxchars = 0;
      do {
         bm->maxchars++;
         XTextExtents( LUI_Font, str, bm->maxchars+1, &dir, &ascent,
                       &descent, &overall );

      } while (overall.width < bm->bwidth-2);
   }
        

   bm->callback = NULL;

   LUI_AddWidgetToWindow( parent, bm, (LUI_FNCP) LUI_ButtonMatrixDestroy );

   LUI_EventAdd2( bm->mainwindow, 
                  ExposureMask | ButtonPressMask | KeyPressMask,
                  (LUI_FNCP) matrix_event, bm );

   return bm;
}



/*
 * Add a new row of buttons to the button matrix.
 * Input:  bm - which button matrix
 *         labels - button labels for the row
 *         reds, greens, blues - colors for each button in [0,1]
 */
void LUI_ButtonMatrixAddRow( LUI_BUTTON_MATRIX *bm, char *labels[],
                             float *reds, float *greens, float *blues )
{
   int i;

   if (bm->rows==MAX_BM_ROWS) {
      printf("Error in LUI_ButtonMatrixAddRow: too many rows\n");
      return;
   }

   for (i=0;i<bm->columns;i++) {
      if (labels[i]) {
         bm->labels[bm->rows][i] = strdup( labels[i] );
      }
      else {
         bm->labels[bm->rows][i] = NULL;
      }
      bm->color[bm->rows][i][0] = reds[i];
      bm->color[bm->rows][i][1] = greens[i];
      bm->color[bm->rows][i][2] = blues[i];
      bm->pixel[bm->rows][i] = 0;
      bm->state[bm->rows][i] = 0;
   }

   bm->rows++;

   set_scrollbar( bm );

   draw_matrix( bm );
}



void LUI_ButtonMatrixChangeLabel( LUI_BUTTON_MATRIX *bm,
                                  int row, int column, char *label )
{
   if (bm->labels[row][column]) {
      free( bm->labels[row][column] );
   }
   bm->labels[row][column] = strdup( label );
   draw_matrix( bm );
}


void LUI_ButtonMatrixCallback( LUI_BUTTON_MATRIX *bm, int (*callback)() )
{
   bm->callback = callback;
   bm->context_index = context_index;
}



void LUI_ButtonMatrixSetState( LUI_BUTTON_MATRIX *bm, int row, int col,
                               int state )
{
   /* always free the button's color */
   if (bm->pixel[row][col]>0) {
      LUI_FreeColor( bm->pixel[row][col] );
   }
   bm->pixel[row][col] = 0;

   bm->state[row][col] = state;
   if (state) {
      /* Allocate the button's color */
      float r = bm->color[row][col][0];
      float g = bm->color[row][col][1];
      float b = bm->color[row][col][2];
      bm->pixel[row][col] = LUI_AllocateColor( r, g, b );
   }

   draw_button( bm, row, row-bm->toprow, col );
}



int LUI_ButtonMatrixGetState( LUI_BUTTON_MATRIX *bm, int row, int col )
{
   return bm->state[row][col];
}



/*
 * Set the color of a matrix's button.
 */
void LUI_ButtonMatrixSetColor( LUI_BUTTON_MATRIX *bm, int row, int col,
                               double red, double green, double blue )
{
   bm->color[row][col][0] = red;
   bm->color[row][col][1] = green;
   bm->color[row][col][2] = blue;
   if (bm->state[row][col]) {
      /* force redraw with new color */
      LUI_ButtonMatrixSetState( bm, row, col, 1 );
   }
}



/*
 * Scroll the button matrix so the last row is visible.
 */
void LUI_ButtonMatrixShowBottom( bm )
LUI_BUTTON_MATRIX *bm;
{
   if (bm->viewrows < bm->rows) {
      bm->toprow = bm->rows - bm->viewrows;
      set_scrollbar(bm);
      draw_matrix(bm);
   }
}


/*
 * Change the size of a button matrix window.
 */
void LUI_ButtonMatrixResize( bm, width, height )
LUI_BUTTON_MATRIX *bm;
int width, height;
{
   XResizeWindow( LUI_Display, bm->mainwindow, width-18, height );

   bm->width = width - 10;
   bm->height = height;
   bm->bwidth = bm->width / bm->columns;
   bm->viewrows = height / bm->bheight;

   if (bm->rows <= bm->viewrows) {
      bm->toprow = 0;
   }
   else if (bm->toprow > bm->rows - bm->viewrows) {
      bm->toprow = bm->rows - bm->viewrows;
   }

   LUI_ScrollBarResize( bm->scrollbar, 16, height );
   set_scrollbar( bm );
   draw_matrix( bm );
}



/*
 * Delete all the rows in a button matrix, i.e. empty it.
 */
void LUI_ButtonMatrixEmpty( bm )
LUI_BUTTON_MATRIX *bm;
{
   bm->rows = 0;
   bm->toprow = 0;
   set_scrollbar( bm );
   draw_matrix( bm );
}





syntax highlighted by Code2HTML, v. 0.9.1