Logo Search packages:      
Sourcecode: jackbeat version File versions  Download package

grid.c

/*
 *   Jackbeat - JACK sequencer
 *    
 *   Copyright (c) 2004-2008 Olivier Guilyardi <olivier {at} samalyse {dot} com>
 *    
 *   This program is free software; you can redistribute it and/or modify
 *   it under the terms of the GNU General Public License as published by
 *   the Free Software Foundation; either version 2 of the License, or
 *   (at your option) any later version.
 *
 *   This program is distributed in the hope that it will be useful,
 *   but WITHOUT ANY WARRANTY; without even the implied warranty of
 *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 *   GNU General Public License for more details.
 *
 *   You should have received a copy of the GNU General Public License
 *   along with this program; if not, write to the Free Software
 *   Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
 *
 *   SVN:$Id: grid.c 121 2008-01-10 00:17:40Z olivier $
 */

#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <gdk/gdkkeysyms.h>
#include "grid.h"

#define GRID_ANIM_INTERVAL 30 
#define GRID_ANIM_LEVEL_TIMEOUT 31
#define GRID_ANIM_LEVEL_FADEOUT 300
#define GRID_ANIM_LEVEL_MAGNIFY 2

#define GRID_CELL_MASK 0x100

#define DEBUG(M, ...) { \
  printf("GRD %s(): ", __func__); \
  printf(M, ## __VA_ARGS__); printf("\n"); \
}

typedef struct grid_gc_t 
{
  GdkGC     *values[64];
  GdkGC     *pointer;
  GdkGC     *on;
  GdkGC     *off;
  GdkGC     *background;
  GdkGC     *mask_border;
  GdkGC     *mask_bg;
  GdkGC     *ruler_label;
} grid_gc_t;

typedef struct grid_color_t 
{
  unsigned char  red;
  unsigned char  green;
  unsigned char  blue;
} grid_color_t;

#define COLOR_TO_RGB(color) color.red, color.green, color.blue 

typedef struct grid_theme_t 
{
  grid_color_t  level_min;
  grid_color_t  level_max;
  grid_color_t  beat_on;
  grid_color_t  beat_off;
  grid_color_t  background;
  grid_color_t  pointer_border;
  grid_color_t  mask_border;
  grid_color_t  mask_background;
  grid_color_t  ruler_label;
  
} grid_theme_t;

#define GRID_DEFAULT_THEME {  \
  {0xcc,0xcc,0xcc},  \
  {0xff,0xc6,0x00},  \
  {0xcc,0xcc,0xcc},  \
  {0xed,0xed,0xed},  \
  {0xff,0xff,0xff},  \
  {0xa0,0x96,0xea},  \
  {0xa0,0x96,0xea},  \
  {0xff,0xff,0xff},  \
  {0x00,0x00,0x00},  \
}

typedef struct grid_cell_t
{
  int value;
  int mask;
  float level;
  float next_level;
  float timecount;
} grid_cell_t;

struct grid_t 
{
  /* Parameters */
  int       min_cell_width;
  int       min_cell_height;
  int       max_cell_width;
  int       max_cell_height;
  int       col_spacing; 
  int       row_spacing; 
  int       y_padding; 
  int       col_num;
  int       row_num;
  int       group_col_num;
  int       group_spacing;
  int       header_height;
  int       header_label_y;

  /* Callbacks */
  grid_cell_changed_callback_t  value_changed_cb;
  void                          *value_changed_data;
  grid_cell_changed_callback_t  mask_changed_cb;
  void                          *mask_changed_data;
  
  /* Allocated objects and generated cells */
  GtkWidget *area;
  GdkPixmap *pixmap;
  int       pixmap_width;
  int       pixmap_height;
  grid_cell_t **cells;
  int       cell_width;
  int       cell_height;
  int       last_row;
  int       last_col;
  int       last_value;
  int       last_mask;
  int       pointed_col;
  int       pointed_row;
  int       mask_pointer;
  int       animation_tag;

  grid_gc_t *gc;
  grid_theme_t *theme;
};

static gboolean grid_configure_event (GtkWidget *widget, GdkEventConfigure *event, grid_t *grid);
static gboolean grid_expose_event (GtkWidget *widget, GdkEventExpose *event, grid_t *grid);
static void     grid_size_request_event (GtkWidget *widget, GtkRequisition *requisition, grid_t *grid);
static gboolean grid_button_press_event (GtkWidget *widget, GdkEventButton *event, grid_t *grid);
static gboolean grid_motion_notify_event (GtkWidget *widget, GdkEventMotion *event, grid_t *grid);
static gboolean grid_key_action_event (GtkWidget * widget, GdkEventKey *event, grid_t * grid);
static void     grid_area_create (grid_t *grid);
gboolean        grid_animation_cb (gpointer data);
static void     grid_draw_cell (grid_t *grid, int col, int row, int Xnotify, int direct);
static void     grid_draw_all (grid_t *grid);

grid_t *grid_new ()
{
  grid_t *grid = malloc (sizeof (grid_t));
  grid->col_num = 0;
  grid->row_num = 0;
  grid->group_col_num = 4;
  grid->group_spacing = 8;
  grid->header_height = 10;
  grid->header_label_y = 0;
  grid->min_cell_width = 10;
  grid->min_cell_height = 10;
  grid->max_cell_width = 1000;
  grid->max_cell_height = 1000;
  grid->col_spacing = 4;
  grid->row_spacing = 6;
  grid->y_padding = 2;
  grid->cells = NULL;
  grid->pixmap = NULL;
  grid->last_row = -1;
  grid->last_col = -1;
  grid->last_value = -1;
  grid->last_mask = -1;
  grid->pointed_row = -1;
  grid->pointed_col = -1;
  grid->value_changed_cb = NULL;
  grid->mask_changed_cb = NULL;
  grid->area = NULL;
  grid->animation_tag = g_timeout_add (GRID_ANIM_INTERVAL, grid_animation_cb, (gpointer) grid);
  grid->gc = NULL;
  return grid;
}

void
grid_resize (grid_t *grid, int col_num, int row_num)
{
  int row;

  if (grid->area != NULL) { DEBUG("grid->area is set"); }
  else { DEBUG("grid->area is NOT set"); }
  
  //printf ("Setting array size to %d rows\n", row_num);
  grid->cells = realloc (grid->cells, row_num * sizeof (grid_cell_t*));
  
  int col;
  int common_col_num = col_num < grid->col_num ? col_num : grid->col_num;
  int common_row_num = row_num < grid->row_num ? row_num : grid->row_num;
  grid_cell_t *row_ptr;
  for (row = 0; row < common_row_num; row++)
  {
   //printf ("Setting array size of %d cols on row %d\n", col_num, row);
   row_ptr = grid->cells[row];
   grid->cells[row] = calloc (col_num, sizeof (grid_cell_t));
   memcpy (grid->cells[row], row_ptr, common_col_num * sizeof (grid_cell_t));
   for (col = grid->col_num; col < col_num; col++) 
    {
     grid->cells[row][col].level = -1;
     grid->cells[row][col].next_level = -1;
     grid->cells[row][col].mask = 1;
     grid->cells[row][col].value = 0;
     grid->cells[row][col].timecount = 0;
    }
   // free row_ptr ?
  }
  
  for (row = grid->row_num; row < row_num; row++)
  {
   //printf ("Creating array of %d cols on row %d\n", col_num, row);
   grid->cells[row] = calloc (col_num, sizeof (grid_cell_t));
   for (col = 0; col < col_num; col++) 
    {
     grid->cells[row][col].level = -1;
     grid->cells[row][col].next_level = -1;
     grid->cells[row][col].mask = 1;
     grid->cells[row][col].value = 0;
     grid->cells[row][col].timecount = 0;
    }
  }

  grid->col_num = col_num;
  grid->row_num = row_num;

  
}

void
grid_set_value (grid_t *grid, int col, int row, int value)
{
  grid->cells[row][col].value = value; 
  grid_draw_cell (grid, col, row, 1, 0);
}

void
grid_set_mask (grid_t *grid, int col, int row, int mask)
{
  grid->cells[row][col].mask = mask; 
  grid_draw_cell (grid, col, row, 1, 0);
}

GtkWidget *
grid_get_widget (grid_t *grid)
{
 if (!grid->area) grid_area_create (grid);
 return grid->area;
}

void
grid_set_value_changed_callback (grid_t *grid, grid_cell_changed_callback_t callback, void *data)
{
  grid->value_changed_cb    = callback;
  grid->value_changed_data  = data;
}

void
grid_set_mask_changed_callback (grid_t *grid, grid_cell_changed_callback_t callback, void *data)
{
  grid->mask_changed_cb   = callback;
  grid->mask_changed_data = data;
}

void
grid_highlight_cell (grid_t *grid, int col, int row, float level)
{
  if ((level > 1) || (level < 0)) 
  {
    DEBUG("Warning - Bad level: %f", level);
  }
  level = level * GRID_ANIM_LEVEL_MAGNIFY;
  if (level > 1) level = 1;
  if (col != -1)
   {
    if (1) // level > grid->cells[row][col].level)
     {
      int x = level * 63;
      int y = grid->cells[row][col].level * 63;
      if (x != y)
       {
        grid->cells[row][col].level = level;
        grid->cells[row][col].next_level = -1;
        grid_draw_cell (grid, col, row, 0, 1);
       }
     }
    else if (level < grid->cells[row][col].level)
     {
      grid->cells[row][col].next_level = level;
     }
    grid->cells[row][col].timecount = 0;
   }
  else
   {
    int i;
    for (i = 0; i < grid->col_num; i++) grid->cells[row][i].next_level = -1;
   }
}

void
grid_set_cell_size (grid_t *grid, int min_width, int max_width, int min_height, int max_height)
{
  if ((grid->min_cell_width != min_width) || (grid->max_cell_width != max_width) ||
      (grid->min_cell_height != min_height) || (grid->max_cell_height != max_height))
  {
    DEBUG("Setting cell size : width (min/max): %d/%d, height (min/max): %d/%d",
           min_width, max_width, min_height, max_height);
    grid->min_cell_width = min_width;
    grid->max_cell_width = max_width;
    grid->min_cell_height = min_height;
    grid->max_cell_height = max_height;

    grid_draw_all (grid);
    if (grid->area) gtk_widget_queue_resize (grid->area);
  }
}

void
grid_set_spacing (grid_t *grid, int col_spacing, int row_spacing)
{
  if ((grid->col_spacing != col_spacing) || (grid->row_spacing != row_spacing))
  {
    DEBUG("Setting spacing: col: %d, row: %d", col_spacing, row_spacing);
    grid->col_spacing = col_spacing;
    grid->row_spacing = row_spacing;

    grid_draw_all (grid);
    if (grid->area) gtk_widget_queue_resize (grid->area);
  }
} 

void
grid_set_column_group_size (grid_t *grid, int column_num)
{
  grid->group_col_num = column_num;
  grid_draw_all (grid);
  if (grid->area) gtk_widget_queue_resize (grid->area);
}

void
grid_set_header_height (grid_t *grid, int height)
{
  grid->header_height = height;
  grid_draw_all (grid);
  if (grid->area) gtk_widget_queue_resize (grid->area);
}

void
grid_set_header_label_ypos (grid_t *grid, int y)
{
  grid->header_label_y = y;
}

/* PRIVATE */

static void
grid_area_finalized_cb (void *data, GObject *junk)
{
  grid_t *grid = (grid_t *)data;
  grid->area = NULL;
}

static void
grid_area_create (grid_t *grid)
{
  grid->area = gtk_drawing_area_new ();
  g_object_weak_ref (G_OBJECT (grid->area), grid_area_finalized_cb, grid);
   
  g_signal_connect (G_OBJECT (grid->area), "expose-event",
                  G_CALLBACK (grid_expose_event), grid);
  g_signal_connect (G_OBJECT (grid->area),"configure-event",
                  G_CALLBACK (grid_configure_event), grid);
  g_signal_connect (G_OBJECT (grid->area),"size-request",
                  G_CALLBACK (grid_size_request_event), grid);
  g_signal_connect (G_OBJECT (grid->area),"button-press-event",
                  G_CALLBACK (grid_button_press_event), grid);
  g_signal_connect (G_OBJECT (grid->area),"motion-notify-event",
                  G_CALLBACK (grid_motion_notify_event), grid);
  GTK_WIDGET_SET_FLAGS (grid->area, GTK_CAN_FOCUS);          
  g_signal_connect (G_OBJECT (grid->area), "key_press_event",
                    G_CALLBACK (grid_key_action_event), grid);
  g_signal_connect (G_OBJECT (grid->area), "key_release_event",
                    G_CALLBACK (grid_key_action_event), grid);

  gtk_widget_set_events (grid->area, GDK_EXPOSURE_MASK
                   // | GDK_LEAVE_NOTIFY_MASK
                   | GDK_KEY_PRESS_MASK
                   | GDK_BUTTON_PRESS_MASK
                   | GDK_POINTER_MOTION_MASK
                   | GDK_POINTER_MOTION_HINT_MASK);
}

static int
grid_col2x (grid_t *grid, int col)
{
  return grid->col_spacing + col * (grid->col_spacing + grid->cell_width)
         + col / grid->group_col_num * (grid->group_spacing - grid->col_spacing);
}

static int
grid_row2y (grid_t *grid, int row)
{
  return grid->header_height + grid->y_padding + row * (grid->row_spacing + grid->cell_height);
}


int
grid_x2col (grid_t *grid, int x)
{
  int col;
  int group_width = grid->group_col_num * (grid->cell_width + grid->col_spacing) - grid->col_spacing;
  int group = (x - grid->col_spacing) / (group_width + grid->group_spacing);
  x -= group * (group_width + grid->group_spacing);
  if (x >= group_width) 
    col = -1;
  else
   {
    col = x / (grid->cell_width + grid->col_spacing);
    if (x >= col * (grid->cell_width + grid->col_spacing) + grid->cell_width) col = -1;
    else col += group * grid->group_col_num;
   }
  
  return col;
}

int
grid_y2row (grid_t *grid, int y)
{
  y -= grid->header_height;
  int row = y / (grid->cell_height + grid->row_spacing);
  if (y < row * (grid->cell_height + grid->row_spacing) + grid->y_padding) row = -1;
  return row;
}

static void
grid_draw_mask_icon (grid_t *grid, int col, int row, const char icon[8][8])
{
  int i,j;

  int x = grid_col2x (grid, col) + (grid->cell_width - 8) / 2;
  int y = grid_row2y (grid, row) + (grid->cell_height - 8) / 2;

  for (i = 0; i < 8; i++)
    for (j = 0; j < 8; j++)
      if (icon[j][i])
        gdk_draw_point (grid->pixmap, grid->gc->mask_border, x + i, y + j);
}

static void
grid_draw_mask (grid_t *grid, int col, int row)
{
  const char icon[8][8] = 
  { 
    {1,1,1,1,1,1,1,1},
    {1,1,1,1,1,1,1,1},
    {1,1,1,1,1,1,1,1},
    {1,1,1,1,1,1,1,1},
    {1,1,1,1,1,1,1,1},
    {1,1,1,1,1,1,1,1},
    {1,1,1,1,1,1,1,1},
    {1,1,1,1,1,1,1,1} 
  };
 
  grid_draw_mask_icon (grid, col, row, icon);
}

static void
grid_draw_mask_pointer (grid_t *grid, int col, int row)
{
  const char icon[8][8] = 
  { 
    {1,1,1,1,1,1,1,1},
    {1,0,0,0,0,0,0,1},
    {1,0,0,0,0,0,0,1},
    {1,0,0,0,0,0,0,1},
    {1,0,0,0,0,0,0,1},
    {1,0,0,0,0,0,0,1},
    {1,0,0,0,0,0,0,1},
    {1,1,1,1,1,1,1,1} 
  };
  
  grid_draw_mask_icon (grid, col, row, icon);
}

static void
grid_draw_cell (grid_t *grid, int col, int row, int Xnotify, int direct)
{
  if ((col >= grid->col_num) || (row >= grid->row_num))
      DEBUG("OUT OF BOUNDS: col/col_num: %d/%d ; row/row_num: %d/%d", col, grid->col_num, row, grid->row_num);

  if (grid->pixmap)
   {
    GdkGC *gc;                                                                  
    int int_level;
    if (0) // histogram
     {
      gc = grid->cells[row][col].value ? grid->gc->on : grid->gc->off;
      gdk_draw_rectangle (grid->pixmap, gc, TRUE, 
                          grid_col2x (grid, col),
                          grid_row2y (grid, row),
                          grid->cell_width, 
                          grid->cell_height);
      if (grid->cells[row][col].level != -1)
       {
        gc = grid->gc->values[63]; 
        int_level = grid->cells[row][col].level * ((float) grid->cell_height);
        gdk_draw_rectangle (grid->pixmap, gc, TRUE, 
                            grid_col2x (grid, col),
                            grid_row2y (grid, row) + grid->cell_height - int_level,
                            grid->cell_width, 
                            int_level);
       }
     }
    else // scale
     {
      if (grid->cells[row][col].level == -1) 
        gc = grid->cells[row][col].value ? grid->gc->on : grid->gc->off;
      else 
       {
        int_level = grid->cells[row][col].level * (float) 63;
        gc = grid->gc->values[int_level];
       }
      gdk_draw_rectangle (grid->pixmap, gc, TRUE, 
                          grid_col2x (grid, col),
                          grid_row2y (grid, row),
                          grid->cell_width, 
                          grid->cell_height);
     }

    if (!grid->cells[row][col].mask) grid_draw_mask (grid, col, row); 

    if (grid->pixmap && grid->area)
     {
      if (Xnotify)
        gtk_widget_queue_draw_area (grid->area,                   
                            grid_col2x (grid, col),
                            grid_row2y (grid, row),
                            grid->cell_width, 
                            grid->cell_height);
      else if (direct)
        gdk_draw_drawable (
          grid->area->window,                 
          grid->area->style->fg_gc[GTK_WIDGET_STATE (grid->area)],
          grid->pixmap,
          grid_col2x (grid, col),
          grid_row2y (grid, row),
          grid_col2x (grid, col),
          grid_row2y (grid, row),
          grid->cell_width, 
          grid->cell_height);
     }

   }
   
}

static void
grid_set_default_theme (grid_t *grid)
{
  static grid_theme_t theme = GRID_DEFAULT_THEME;
  grid->theme = &theme;
}

static GdkGC *
grid_make_gc (grid_t *grid, GdkColormap *colormap, unsigned char red, unsigned char green, unsigned char blue)
{
  GdkGC *gc = gdk_gc_new (grid->pixmap);
  GdkColor color;
  gdk_gc_set_colormap (gc, colormap);
  color.red = red << 8;
  color.green = green << 8;
  color.blue = blue << 8;
  gdk_gc_set_rgb_fg_color (gc, &color);
  return gc;
}


static void
grid_create_graphic_contexts (grid_t *grid)
{
  DEBUG ("Creating graphic contexts");
  grid->gc = malloc (sizeof (grid_gc_t));
  int i;
  GdkColormap *colormap = gdk_colormap_new (gdk_visual_get_system (), TRUE);
  for (i = 0; i < 64; i++)
    grid->gc->values[i] = grid_make_gc (grid, colormap, 
      i * (grid->theme->level_max.red - grid->theme->level_min.red) / 63 + grid->theme->level_min.red, 
      i * (grid->theme->level_max.green - grid->theme->level_min.green) / 63 + grid->theme->level_min.green, 
      i * (grid->theme->level_max.blue - grid->theme->level_min.blue) / 63 + grid->theme->level_min.blue);
  
  grid->gc->on          = grid_make_gc (grid, colormap, COLOR_TO_RGB (grid->theme->beat_on));
  grid->gc->off         = grid_make_gc (grid, colormap, COLOR_TO_RGB (grid->theme->beat_off));
  grid->gc->background  = grid_make_gc (grid, colormap, COLOR_TO_RGB (grid->theme->background));
  grid->gc->pointer     = grid_make_gc (grid, colormap, COLOR_TO_RGB (grid->theme->pointer_border));
  grid->gc->mask_border = grid_make_gc (grid, colormap, COLOR_TO_RGB (grid->theme->mask_border));
  grid->gc->mask_bg     = grid_make_gc (grid, colormap, COLOR_TO_RGB (grid->theme->mask_background));
  grid->gc->ruler_label     = grid_make_gc (grid, colormap, COLOR_TO_RGB (grid->theme->ruler_label));
}

static void
grid_draw_cell_pointer (grid_t *grid, int col, int row, int is_mask)
{
  if (grid->pixmap)
   {
    GdkGC *gc = grid->gc->pointer;
    if (is_mask)
     {
      grid_draw_mask_pointer (grid, col, row); 
     }
    else
     {
      gdk_draw_rectangle (grid->pixmap, gc, FALSE, 
                          grid_col2x (grid, col),
                          grid_row2y (grid, row),
                          grid->cell_width - 1, 
                          grid->cell_height - 1);
     }
    
    gtk_widget_queue_draw_area (grid->area,                 
                        grid_col2x (grid, col),
                        grid_row2y (grid, row),
                        grid->cell_width, 
                        grid->cell_height);
   }
   
}

static void
grid_draw_ruler (grid_t *grid)
{
  int col;
  char str[8];
  if (grid->area) 
  {
    for (col=0; col < grid->col_num; col += grid->group_col_num)
    {
      if ((grid->group_col_num > 1) || (col % 2 == 0))
      {
        snprintf (str, 8, "%d", col);
        PangoLayout *layout = gtk_widget_create_pango_layout (grid->area, str);
        gdk_draw_layout (grid->pixmap, grid->gc->ruler_label, grid_col2x (grid, col), grid->header_label_y, layout);
      }
    }
  }
}

static void
grid_draw_all (grid_t *grid)
{
  if (grid->pixmap) 
  {
    DEBUG("Drawing background");
    gdk_draw_rectangle (grid->pixmap, grid->gc->background, TRUE, 0, 0, 
                        grid->pixmap_width, grid->pixmap_height);

    DEBUG("Drawing cells");
    int row, col;
    for (row = 0; row < grid->row_num; row++)
      for (col = 0; col < grid->col_num; col++)
        grid_draw_cell (grid, col, row, 0, 0);

    DEBUG("Drawing ruler");
    grid_draw_ruler (grid);
/*
    PangoLayout *layout = gtk_widget_create_pango_layout (grid->area, "Ceci est un test");
    gdk_draw_layout (grid->pixmap, grid->gc->mask_border, 10, grid->header_label_y, layout);
  */  
   
    if (grid->area) {
      DEBUG("Area exists, queuing draw");
      gtk_widget_queue_draw_area (grid->area, 0, 0, grid->pixmap_width, grid->pixmap_height);
    } else {
      DEBUG("Area doesn't exist, not queuing draw");
    }
  }
}

static int
grid_get_minimum_width (grid_t *grid)
{
  return grid->col_num * grid->min_cell_width + (grid->col_num + 1) * grid->col_spacing
         + (grid->col_num / grid->group_col_num - 1) * (grid->group_spacing - grid->col_spacing);
}

static int
grid_get_minimum_height (grid_t *grid)
{
  return grid->row_num * grid->min_cell_height + (grid->row_num - 1) * grid->row_spacing
         + grid->y_padding * 2 + grid->header_height;
}

static gboolean
grid_configure_event (GtkWidget *widget, GdkEventConfigure *event, grid_t *grid)
{
  if (grid->pixmap) g_object_unref (grid->pixmap);

  int minimum_width = grid_get_minimum_width (grid);
  grid->pixmap_width = (minimum_width > widget->allocation.width) 
                     ?  minimum_width : widget->allocation.width;

  grid->cell_width = (grid->pixmap_width - (grid->col_num + 1) * grid->col_spacing
                       - (grid->col_num / grid->group_col_num - 1) * (grid->group_spacing - grid->col_spacing))
                     / grid->col_num;
  grid->cell_width = grid->cell_width > grid->max_cell_width ? grid->max_cell_width : grid->cell_width;
  DEBUG("configure width - minimum/allocated/cell: %d/%d/%d", minimum_width, widget->allocation.width, grid->cell_width);
 
  int minimum_height = grid_get_minimum_height (grid);
  grid->header_height += widget->allocation.height - minimum_height;
  minimum_height = grid_get_minimum_height (grid);
  grid->pixmap_height =  (minimum_height > widget->allocation.height) 
                      ? minimum_height : widget->allocation.height;
  
  grid->cell_height = (grid->pixmap_height - (grid->row_num - 1) * grid->row_spacing - grid->y_padding * 2 
                       - grid->header_height) 
                      / grid->row_num;
  grid->cell_height = grid->cell_height > grid->max_cell_height ? grid->max_cell_height : grid->cell_height;
  DEBUG("configure height - minimum/allocated/cell: %d/%d/%d", minimum_height, widget->allocation.height, grid->cell_height);

  DEBUG ("Creating pixmap of size %d x %d", grid->pixmap_width, grid->pixmap_height);
  grid->pixmap = gdk_pixmap_new (widget->window, grid->pixmap_width, grid->pixmap_height, -1);

  if (!grid->gc) 
  {
    grid_set_default_theme (grid);
    grid_create_graphic_contexts (grid);
  }
 
  grid_draw_all (grid);
  
  return TRUE;
}

static gboolean
grid_expose_event (GtkWidget *widget, GdkEventExpose *event, grid_t *grid)
{
  // FIXME: need to indicate focus
  // see: http://developer.gnome.org/doc/API/2.0/gtk/GtkDrawingArea.html
  gdk_draw_drawable(widget->window,
                widget->style->fg_gc[GTK_WIDGET_STATE (widget)],
                grid->pixmap,
                event->area.x, event->area.y,
                event->area.x, event->area.y,
                event->area.width, event->area.height);

  return FALSE;
}

static void
grid_size_request_event (GtkWidget *widget, GtkRequisition *requisition, grid_t *grid)
{
  requisition->width  = grid_get_minimum_width (grid);
  requisition->height = grid_get_minimum_height (grid);
  DEBUG("size request called - width/height: %d/%d", requisition->width, requisition->height);
}

static void
grid_toggle_cell (grid_t *grid, int col, int row, int mask)
{
  if (mask)  
   {
    grid->last_mask = grid->cells[row][col].mask = grid->cells[row][col].mask ? 0 : 1;
    grid_draw_cell (grid, col, row, 1, 0);
    grid->last_col = col;
    grid->last_row = row;
    if (grid->mask_changed_cb)
      grid->mask_changed_cb (grid->mask_changed_data, col, row, grid->cells[row][col].mask);
   }
  else 
   {
    grid->last_value = grid->cells[row][col].value = grid->cells[row][col].value ? 0 : 1;
    grid_draw_cell (grid, col, row, 1, 0);
    grid->last_col = col;
    grid->last_row = row;
    if (grid->value_changed_cb)
      grid->value_changed_cb (grid->value_changed_data, col, row, grid->cells[row][col].value);
   }
}

static void
grid_move_pointer (grid_t *grid, int col, int row, int mask)
{
  if (grid->pointed_col != -1 && grid->pointed_row != -1 &&
      (grid->pointed_col != col || grid->pointed_row != row || grid->mask_pointer != mask))
    grid_draw_cell (grid, grid->pointed_col, grid->pointed_row, 1, 0);
  grid_draw_cell_pointer (grid, col, row, mask);

  grid->pointed_col = col;
  grid->pointed_row = row;
  grid->mask_pointer = mask;
}

static gboolean
grid_button_press_event (GtkWidget *widget, GdkEventButton *event, grid_t *grid)
{
  if (event->button == 1 && grid->pixmap != NULL)
   {
    int col = grid_x2col (grid, event->x);
    int row = grid_y2row (grid, event->y);
    if (col >= 0 && col < grid->col_num && row >= 0 && row < grid->row_num)
     {
      grid_toggle_cell (grid, col, row, event->state & GDK_SHIFT_MASK);
      grid_move_pointer (grid, col, row, event->state & GDK_SHIFT_MASK);
     }
   }
  
  return TRUE;
}

static gboolean
grid_motion_notify_event (GtkWidget *widget, GdkEventMotion *event, grid_t *grid)
{
  int x, y;
  GdkModifierType state;

  if (grid->pixmap == NULL) return TRUE;
  
  if (event->is_hint)
    gdk_window_get_pointer (event->window, &x, &y, &state);
  else
   {
    x = event->x;
    y = event->y;
    state = event->state;
   }
    
  int col = grid_x2col (grid, x);
  int row = grid_y2row (grid, y);

  if (col >= 0 && col < grid->col_num && row >= 0 && row < grid->row_num)
   {
    if (state & GDK_BUTTON1_MASK && ((col != grid->last_col) || (row != grid->last_row)))
     {
      if ((state & GDK_SHIFT_MASK) && (grid->cells[row][col].mask != grid->last_mask))  
        grid_toggle_cell (grid, col, row, 1);
      else if (grid->cells[row][col].value != grid->last_value)
        grid_toggle_cell (grid, col, row, 0);
     } 

    grid_move_pointer (grid, col, row, event->state & GDK_SHIFT_MASK);
   }
 
  gtk_widget_grab_focus (grid->area);

  return TRUE;
}

static gboolean
grid_key_action_event (GtkWidget * widget, GdkEventKey *event, grid_t * grid)
{
  int col = grid->pointed_col;
  int row = grid->pointed_row;
  int mask = event->state & GDK_SHIFT_MASK;
  int toggle = 0;
  if (event->type == GDK_KEY_PRESS) 
   {
    switch (event->keyval) 
     {
      case GDK_Up:    row--; break;
      case GDK_Right: col++; break;
      case GDK_Down:  row++; break;
      case GDK_Left:  col--; break;
      case GDK_Shift_L:  
      case GDK_Shift_R:  
        mask = 1;
        break;
      case GDK_Return:
        toggle = 1;
        break;
      //FIXME: what about GDK_Delete as an eraser?
      default:
        return FALSE;
     }
   }
  else
   {
    switch (event->keyval) 
     {
      case GDK_Shift_L:  
      case GDK_Shift_R:  
        mask = 0;
        break;
      default:
        return FALSE;
     }
   }
  if (toggle) 
   {
    if (col >= 0 && col < grid->col_num && row >= 0 && row < grid->row_num)
      grid_toggle_cell (grid, col, row, mask);
   }
  else
   {
    row = row < 0 ? 0 : (row >= grid->row_num ? grid->row_num - 1  : row);
    col = col < 0 ? 0 : (col >= grid->col_num ? grid->col_num - 1  : col);
   }

  grid_move_pointer (grid, col, row, mask);

  return TRUE;
}

gboolean
grid_animation_cb (gpointer data)
{
  grid_t *grid = (grid_t *)data;

  if (!grid->area) return TRUE;
  
  int col, row; 
  grid_cell_t *colp;
  for (row = 0; row < grid->row_num; row++)
    for (col = 0; col < grid->col_num; col++)
     {
      colp = grid->cells[row] + col;
      if (colp->level != colp->next_level)
       {
        if (colp->timecount > GRID_ANIM_LEVEL_TIMEOUT)
         {
          if (colp->timecount - GRID_ANIM_LEVEL_TIMEOUT > GRID_ANIM_LEVEL_FADEOUT)
           {
            colp->level = colp->next_level;
           }
          else
           {
            float next_level = colp->next_level == -1 ? 0 : colp->next_level;
            colp->level -= (colp->level - next_level) 
                            * (colp->timecount - GRID_ANIM_LEVEL_TIMEOUT) / (float) GRID_ANIM_LEVEL_FADEOUT;
           }
          grid_draw_cell (grid, col, row, 0, 1);
         }
       }
      if (colp->timecount < 5000) colp->timecount += GRID_ANIM_INTERVAL;
     }
  return TRUE;
}

Generated by  Doxygen 1.6.0   Back to index