[cairo-commit] cairo-demo/gtkcairo_slide COPYING,NONE,1.1 ChangeLog,NONE,1.1 Makefile,NONE,1.1 README,NONE,1.1 cairo_custom.c,NONE,1.1 cairo_custom.h,NONE,1.1 gtkcairo_slide.c,NONE,1.1 puzzle.c,NONE,1.1 puzzle.h,NONE,1.1

OEyvind Kolaas commit at pdx.freedesktop.org
Mon Aug 15 11:12:59 PDT 2005


Committed by: pippin

Update of /cvs/cairo/cairo-demo/gtkcairo_slide
In directory pdx:/tmp/cvs-serv27649/gtkcairo_slide

Added Files:
	COPYING ChangeLog Makefile README cairo_custom.c 
	cairo_custom.h gtkcairo_slide.c puzzle.c puzzle.h 
Log Message:
Initial import of gtkcairo_slide


--- NEW FILE: COPYING ---
(This appears to be a binary file; contents omitted.)

--- NEW FILE: ChangeLog ---
2004-14-02 OEyvind Kolaas <pippin at freedesktop.org> 

	Initial import

--- NEW FILE: Makefile ---
CFLAGS = -Wall
SRCS   = $(wildcard *.c)
OBJS   = $(SRCS:.c=.o)

LIBS   +=`pkg-config --libs gtkcairo`
CFLAGS +=`pkg-config --cflags gtkcairo`
CC     = gcc

all: dep gtkcairo_slide
gtkcairo_slide: $(OBJS)
	$(CC) -o $@ $(OBJS) $(LIBS) $(LDFLAGS)
	@echo

clean:	
	rm -f *~ gtkcairo_slide .depend *.o *.orig
dep: 
	$(MAKE) .depend
.depend: $(SRCS) *.c *.h
	$(CC) -MM $(CFLAGS) $(SRCS) 1>.depend

--- NEW FILE: README ---
gtkcairo_slide - GTK+ widget and application, demonstrating gtkcairo

A cairo implementation of the classic sliding block puzzle, where you're supposed
to slide the blocks into a configuration where the blocks are numbered incrementally.

Dependencies
------------

GtkCairoSlide depends on gtkcairo, and it's dependencies

2004 (c) Øyvind Kolås, pippin at freedesktop.org

--- NEW FILE: cairo_custom.c ---
#include "cairo_custom.h"

void
cairo_rectangle_round ( cairo_t * ct, double x0, double y0, double width, double height, double radius) {
    double x1,y1;
    x1=x0+width;
    y1=y0+height;
    if (!width || !height)
        return;
    if (width/2<radius) {
        if (height/2<radius) {
            cairo_move_to  (ct, x0, (y0 + y1)/2);
            cairo_curve_to (ct, x0 ,y0, x0, y0, (x0 + x1)/2, y0);
            cairo_curve_to (ct, x1, y0, x1, y0, x1, (y0 + y1)/2);
            cairo_curve_to (ct, x1, y1, x1, y1, (x1 + x0)/2, y1);
            cairo_curve_to (ct, x0, y1, x0, y1, x0, (y0 + y1)/2);
        } else {
            cairo_move_to  (ct, x0, y0 + radius);
            cairo_curve_to (ct, x0 ,y0, x0, y0, (x0 + x1)/2, y0);
            cairo_curve_to (ct, x1, y0, x1, y0, x1, y0 + radius);
            cairo_line_to (ct, x1 , y1 - radius);
            cairo_curve_to (ct, x1, y1, x1, y1, (x1 + x0)/2, y1);
            cairo_curve_to (ct, x0, y1, x0, y1, x0, y1- radius);
        }
    } else {
        if (height/2<radius) {
            cairo_move_to  (ct, x0, (y0 + y1)/2);
            cairo_curve_to (ct, x0 , y0, x0 , y0, x0 + radius, y0);
            cairo_line_to (ct, x1 - radius, y0);
            cairo_curve_to (ct, x1, y0, x1, y0, x1, (y0 + y1)/2);
            cairo_curve_to (ct, x1, y1, x1, y1, x1 - radius, y1);
            cairo_line_to (ct, x0 + radius, y1);
            cairo_curve_to (ct, x0, y1, x0, y1, x0, (y0 + y1)/2);
        } else {
            cairo_move_to  (ct, x0, y0 + radius);
            cairo_curve_to (ct, x0 , y0, x0 , y0, x0 + radius, y0);
            cairo_line_to (ct, x1 - radius, y0);
            cairo_curve_to (ct, x1, y0, x1, y0, x1, y0 + radius);
            cairo_line_to (ct, x1 , y1 - radius);
            cairo_curve_to (ct, x1, y1, x1, y1, x1 - radius, y1);
            cairo_line_to (ct, x0 + radius, y1);
            cairo_curve_to (ct, x0, y1, x0, y1, x0, y1- radius);
        }
    }
    cairo_close_path (ct);
}

void
cairo_edgeline (cairo_t * ct,
                double x0,
                double y0,
                double x1,
                double y1,
                double backoff) {
    y0 = y0 - 1;
    y1 = y1 + 1;

    cairo_move_to (ct, x0, y0);
    if (y1 > y0 + backoff / 2) {
        cairo_curve_to (ct, x0, (y0 + y1) / 2, x1, (y0 + y1) / 2, x1, y1);
    } else {

        cairo_curve_to (ct, x0,
                        y0 + (backoff / 4 + (y0 + backoff / 2 - y1)), x1,
                        y1 - (backoff / 4 + (y0 + backoff / 2 - y1)), x1,
                        y1);
    }
}

static double color_fg[5][3];
static double color_bg[5][3];
static double color_light[5][3];
static double color_dark[5][3];
static double color_mid[5][3];
static double color_text[5][3];
static double color_base[5][3];

void
cairo_init_gtk_color (GtkWidget *widget) {
    GtkStateType state;

    for (state = GTK_STATE_NORMAL;state<=GTK_STATE_INSENSITIVE;state++) {
        color_fg[state][0] = widget->style->fg[state].red/65535.0;
        color_fg[state][1] = widget->style->fg[state].green/65535.0;
        color_fg[state][2] = widget->style->fg[state].blue/65535.0;

        color_bg[state][0] = widget->style->bg[state].red/65535.0;
        color_bg[state][1] = widget->style->bg[state].green/65535.0;
        color_bg[state][2] = widget->style->bg[state].blue/65535.0;

        color_light[state][0] = widget->style->light[state].red/65535.0;
        color_light[state][1] = widget->style->light[state].green/65535.0;
        color_light[state][2] = widget->style->light[state].blue/65535.0;

        color_dark[state][0] = widget->style->dark[state].red/65535.0;
        color_dark[state][1] = widget->style->dark[state].green/65535.0;
        color_dark[state][2] = widget->style->dark[state].blue/65535.0;


        color_mid[state][0] = widget->style->mid[state].red/65535.0;
        color_mid[state][1] = widget->style->mid[state].green/65535.0;
        color_mid[state][2] = widget->style->mid[state].blue/65535.0;



        color_text[state][0] = widget->style->text[state].red/65535.0;
        color_text[state][1] = widget->style->text[state].green/65535.0;
        color_text[state][2] = widget->style->text[state].blue/65535.0;


        color_base[state][0] = widget->style->base[state].red/65535.0;
        color_base[state][1] = widget->style->base[state].green/65535.0;
        color_base[state][2] = widget->style->base[state].blue/65535.0;
    }
}

void
cairo_set_gtk_color (cairo_t *ct, const char *item, GtkStateType state) {
    /* these checks should be rearranged descending according to amount of use */
    if (!strcmp (item, "fg")) {
        cairo_set_rgb_color (ct, color_fg[state][0], color_fg[state][1],color_fg[state][2]);
    } else if (!strcmp (item, "bg")) {
        cairo_set_rgb_color (ct, color_bg[state][0], color_bg[state][1],color_bg[state][2]);
    } else if (!strcmp (item, "text")) {
        cairo_set_rgb_color (ct, color_text[state][0], color_text[state][1],color_text[state][2]);
    } else if (!strcmp (item, "light")) {
        cairo_set_rgb_color (ct, color_light[state][0], color_light[state][1],color_light[state][2]);
    } else if (!strcmp (item, "dark")) {
        cairo_set_rgb_color (ct, color_dark[state][0], color_dark[state][1],color_dark[state][2]);
    } else if (!strcmp (item, "mid")) {
        cairo_set_rgb_color (ct, color_mid[state][0], color_mid[state][1],color_mid[state][2]);
    } else if (!strcmp (item, "base")) { /* base */
        cairo_set_rgb_color (ct, color_base[state][0], color_base[state][1],color_base[state][2]);
    } else {
        fprintf (stderr, "Eeek,. unknown item %s passed to cairo_set_gtk_color()\n", item);
    }
}

--- NEW FILE: cairo_custom.h ---
#ifndef CAIRO_CUSTOM_H
#define CAIRO_CUSTOM_H
#include <gtkcairo.h>

void
cairo_rectangle_round ( cairo_t * ct,
                        double x0,
                        double y0,
                        double width,
                        double height,
                        double radius);

void
cairo_edgeline (cairo_t * ct,
                double x0,
                double y0,
                double x1,
                double y1,
                double backoff);
void
cairo_init_gtk_color (GtkWidget *widget);

void
cairo_set_gtk_color (cairo_t *ct, const char *item, GtkStateType state);

#endif /* CAIRO_CUSTOM_H */

--- NEW FILE: gtkcairo_slide.c ---
#include <gtkcairo.h>
#include "puzzle.h"


GtkWidget *win;

static void
puzzle_solved (GtkWidget * widget, gpointer data) {
    GtkWidget *dialog;
    GtkWidget *label;

    dialog = gtk_dialog_new_with_buttons ("GtkCairoSlide - solved",
                                          GTK_WINDOW (win),
                                          GTK_DIALOG_MODAL,
                                          GTK_STOCK_OK, GTK_RESPONSE_ACCEPT,
                                          NULL);

    label = gtk_label_new ("You solved the puzzle!");
    gtk_misc_set_padding (GTK_MISC (label), 20, 20);
    gtk_widget_show (label);


    gtk_container_add (GTK_CONTAINER (GTK_DIALOG(dialog)->vbox), label);
    gtk_widget_show (dialog);

    gtk_dialog_run (GTK_DIALOG (dialog));
    gtk_main_quit ();    
}

static void
show_game (void) {
    GtkWidget *vbox;
    GtkWidget *puzzle;

	win = gtk_window_new (GTK_WINDOW_TOPLEVEL);
	gtk_window_set_title (GTK_WINDOW (win), "Sliding GtkCairo Puzzle");
	g_signal_connect (G_OBJECT (win), "delete-event",
	                  G_CALLBACK (gtk_main_quit), NULL);

	vbox = gtk_vbox_new (FALSE, 6);
	gtk_container_set_border_width (GTK_CONTAINER (vbox), 12);

    puzzle = puzzle_new ();
    
    g_object_set (G_OBJECT (puzzle), "rows", 4, NULL);
    g_object_set (G_OBJECT (puzzle), "cols", 4, NULL);
    g_object_set (G_OBJECT (puzzle), "shuffles", 2048, NULL);
    
	gtk_widget_set_usize (GTK_WIDGET (puzzle), 192, 192);

    g_signal_connect (G_OBJECT (puzzle), "puzzle_solved",
                      G_CALLBACK (puzzle_solved), puzzle);

	gtk_container_add (GTK_CONTAINER (vbox), puzzle);

	gtk_container_add (GTK_CONTAINER (win), vbox);
	gtk_widget_show_all (vbox);
	gtk_widget_show (win);
}

int
main (int argc, char *argv[]) {
	gtk_init (&argc, &argv);
	show_game ();
	gtk_main ();

	return 0;
}


--- NEW FILE: puzzle.c ---
#include <stdlib.h>

#include <gtkcairo.h>
#include "cairo_custom.h"

#include "puzzle.h"
#include "math.h"    /* floor() fabs() */

/** game specific functions, defined at end of this file */

static void
board_init (GtkWidget *widget);

static int
query_pos (Puzzle *puzzle,
              int  x, 
              int  y);

static void
push_block (Puzzle *puzzle, 
               int  block_no, 
            double  xdelta,
            double  ydelta);

static void
draw_board (Puzzle *puzzle, 
           cairo_t *ct);

static gint
board_solved (Puzzle *puzzle);

/** gtkcairo derived widget **/

static GObjectClass *parent_class = NULL;

struct PuzzleItem {
    double x;
    double y;
    char  label[16];  
};

struct _Puzzle {
    GtkCairo gtkcairo;

    double ratio_x;
    double ratio_y;

    double cursorx;
    double cursory;
    
    struct PuzzleItem *item;
    
    gint grabbed;

    gint rows;
    gint cols;
    gint shuffles;
};

struct _PuzzleClass {
    GtkCairoClass parent_class;
};


enum {
    PUZZLE_SOLVED_SIGNAL,
    LAST_SIGNAL
};

enum {
    PUZZLE_ROWS=1,
    PUZZLE_COLS,
    PUZZLE_SHUFFLES
};


static void
set_property (GObject *object,
                guint  property_id,
         const GValue *value,
           GParamSpec *psec) {
           
    Puzzle *puzzle = PUZZLE (object);

    switch (property_id) {
    case PUZZLE_COLS:
            puzzle->cols = g_value_get_int (value);
            board_init (GTK_WIDGET (puzzle));
        break;
    case PUZZLE_ROWS:
            puzzle->rows = g_value_get_int (value);
            board_init (GTK_WIDGET (puzzle));
        break;
    case PUZZLE_SHUFFLES:
            puzzle->shuffles = g_value_get_int (value);
            board_init (GTK_WIDGET (puzzle));
        break;
    default:
        g_assert (FALSE);
        break;
    }
}

static void
get_property (GObject *object,
                guint  property_id,
               GValue *value,
           GParamSpec *pspec) {
           
    Puzzle *puzzle = PUZZLE (object);

    switch (property_id) {
    case PUZZLE_ROWS: 
            g_value_set_int (value, puzzle->rows);
        break;
    case PUZZLE_COLS: 
            g_value_set_int (value, puzzle->rows);
        break;
    case PUZZLE_SHUFFLES: 
            g_value_set_int (value, puzzle->shuffles);
        break;
    default:
        g_assert (FALSE);
        break;
    }
}

static void
finalize (GObject *object) {

    Puzzle *puzzle = PUZZLE (object);

    if (puzzle->item){
        free (puzzle->item);
    }

    parent_class->finalize (object);
}

static gint puzzle_signals[LAST_SIGNAL] = { 0 };

static void
puzzle_class_init (PuzzleClass * class) {
    GObjectClass *gobject_class;

    gobject_class = G_OBJECT_CLASS (class);
    parent_class = g_type_class_peek (GTK_TYPE_CAIRO);

    gobject_class->finalize =    finalize;
    gobject_class->get_property = get_property;
    gobject_class->set_property = set_property;

    g_object_class_install_property (gobject_class,
                                     PUZZLE_ROWS,
                                     g_param_spec_int ("rows",
                                                       "Number of rows",
                                                       "",
                                                       0,
                                                       20,
                                                       0,
                                                       G_PARAM_READWRITE));

    g_object_class_install_property (gobject_class,
                                     PUZZLE_COLS,
                                     g_param_spec_int ("cols",
                                                       "Number of cols",
                                                       "",
                                                       0,
                                                       20,
                                                       0,
                                                       G_PARAM_READWRITE));

    g_object_class_install_property (gobject_class,
                                     PUZZLE_SHUFFLES,
                                     g_param_spec_int ("shuffles",
                                                       "Number of moves shuffling board, (approximate)",
                                                       "",
                                                       0,
                                                       1000000,
                                                       0,
                                                       G_PARAM_READWRITE));

    puzzle_signals[PUZZLE_SOLVED_SIGNAL] =
        g_signal_new ("puzzle_solved",
                      G_TYPE_FROM_CLASS (gobject_class),
                      G_SIGNAL_RUN_FIRST, 0, NULL, NULL,
                      g_cclosure_marshal_VOID__VOID, G_TYPE_NONE, 0, NULL);
}

static void
puzzle_init (Puzzle * puzzle) {
    puzzle->cursorx = 0;
    puzzle->cursory = 0;
    puzzle->rows = 4;
    puzzle->cols = 4;
    puzzle->shuffles = 200;
    puzzle->grabbed = -1;
    puzzle->ratio_x = 1;
    puzzle->ratio_y = 1;
    board_init (GTK_WIDGET (puzzle));
    gtk_widget_show_all (GTK_WIDGET (puzzle));
}

GType
puzzle_get_type (void) {
    static GType ge_type = 0;
    if (!ge_type) {
        static const GTypeInfo ge_info = {
               sizeof (PuzzleClass),
               NULL,
               NULL,
               (GClassInitFunc) puzzle_class_init,
               NULL,
               NULL,
               sizeof (Puzzle),
               0,
               (GInstanceInitFunc) puzzle_init,
        };
        ge_type =
            g_type_register_static (GTK_TYPE_CAIRO,
                                    "Puzzle",
                                    &ge_info,
                                    0);
    }
    return ge_type;
}

/* prototypes for event functions registered with the object */

static gboolean event_press (GtkWidget *widget,
                        GdkEventButton *bev,
                              gpointer  user_data);

static gboolean event_release (GtkWidget *widget,
                          GdkEventButton *bev,
                                gpointer  user_data);

static gboolean event_motion (GtkWidget *widget,
                         GdkEventMotion *mev,
                               gpointer  user_data);

static void puzzle_class_init (PuzzleClass *class);

/* prototpe for the redraw callback we register with gtkcairo */

static void redraw (GtkCairo *gtkcairo,
                     cairo_t *ct,
                    gpointer  user_data);

GtkWidget *
puzzle_new (void) {
    GtkWidget *widget = GTK_WIDGET (g_object_new (puzzle_get_type (), NULL));
    Puzzle *puzzle = PUZZLE (widget);

    gtk_widget_set_events (widget,
                           GDK_EXPOSURE_MASK        |
                           GDK_POINTER_MOTION_HINT_MASK |  /* since we do dragging, let the ui update request redraw events */
                           GDK_BUTTON1_MOTION_MASK  |
                           GDK_BUTTON2_MOTION_MASK  |
                           GDK_BUTTON3_MOTION_MASK  |
                           GDK_BUTTON_PRESS_MASK    |
                           GDK_BUTTON_RELEASE_MASK);

    gtk_widget_set_size_request (widget, 256, 256);

    g_signal_connect (G_OBJECT (widget), "redraw", 
                      G_CALLBACK (redraw), puzzle);

    g_signal_connect (G_OBJECT (widget), "motion_notify_event",
                      G_CALLBACK (event_motion), puzzle);

    g_signal_connect (G_OBJECT (widget), "button_press_event",
                      G_CALLBACK (event_press), puzzle);

    g_signal_connect (G_OBJECT (widget), "button_release_event",
                      G_CALLBACK (event_release), puzzle);

    gtk_widget_show (widget);

    return widget;
}

/* the actually user interface event functions */

static gboolean
event_press (GtkWidget * widget,
             GdkEventButton * bev, gpointer user_data) {
    Puzzle *puzzle = PUZZLE (user_data);

    puzzle->cursorx = bev->x;
    puzzle->cursory = bev->y;

    switch (bev->button) {
        case 1:
              puzzle->grabbed = query_pos (puzzle, bev->x, bev->y);
              
              /* request a redraw, since we've changed the state of our widget */
              gtk_widget_queue_draw (GTK_WIDGET (puzzle));
              break;
    }
    return FALSE;
}

static gboolean
event_release (GtkWidget * widget,
               GdkEventButton * bev, gpointer user_data) {
    Puzzle *puzzle = PUZZLE (user_data);

    switch (bev->button) {
        case 1:
              puzzle->grabbed = -1;

              /* request a redraw, since we've changed the state of our widget */
              gtk_widget_queue_draw (GTK_WIDGET (puzzle));
        break;
    }
    return FALSE;
}

static gboolean
event_motion (GtkWidget * widget,
              GdkEventMotion * mev, gpointer user_data) {
    Puzzle *puzzle = PUZZLE (user_data);

    if (puzzle->grabbed>=0) {
        double xdelta = (mev->x-puzzle->cursorx) / puzzle->ratio_x;
        double ydelta = (mev->y-puzzle->cursory) / puzzle->ratio_y;

        push_block (puzzle, puzzle->grabbed, xdelta, ydelta);

        if (board_solved (puzzle)){
             g_signal_emit (G_OBJECT (puzzle),
                            puzzle_signals
                            [PUZZLE_SOLVED_SIGNAL], 0);            
        }


        /* request a redraw, since we've changed the state of our widget */
        gtk_widget_queue_draw (GTK_WIDGET (puzzle));
        
        puzzle->cursorx = mev->x;
        puzzle->cursory = mev->y;
    }
    return FALSE;
}

/* actual redraw function */

static void
redraw (GtkCairo * gtkcairo, cairo_t * ct, gpointer user_data) {
    GtkWidget *widget = GTK_WIDGET (gtkcairo);
    Puzzle *puzzle = PUZZLE (gtkcairo);

    /* initialize the utility functions for getting colors from the gtk style */
    cairo_init_gtk_color (widget);
    
    puzzle->ratio_x = (double) widget->allocation.width/puzzle->cols;
    puzzle->ratio_y = (double) widget->allocation.height/puzzle->rows;

    draw_board (puzzle, ct);

    if (cairo_status (ct)) {
        fprintf (stderr, "Cairo is unhappy: %s\n",
                 cairo_status_string (ct));
    }

    /* since we are requesting motion hints,. make sure another motion
       event is delivered after redrawing the ui */
    gdk_window_get_pointer (widget->window, NULL, NULL, NULL);
}


/*************************************************************************/
/***         end of GtkCairo example, actual game logic follows        ***/
/*************************************************************************/


static void 
get_empty (Puzzle *puzzle, 
              int *col,
              int *row);

static int
get_at (Puzzle *puzzle,
          gint  col,
          gint  row);

/* shuffle an initialized board (important, do not
   shuffle a board where the coordinates have changed
   since initialization, since the shuffeling presumes that
   the order haven't changed.
*/   

#include <assert.h>

static 
void puzzle_shuffle (Puzzle *puzzle, gint shuffles){
    while (shuffles--) {
      gint empty_x;
      gint empty_y;
      gint block_no;
      gint direction = g_random_int_range (0, 5);
    
      get_empty (puzzle, &empty_x, &empty_y);
      
      switch (direction){
        case 0:
            if ((block_no=get_at (puzzle, empty_x-1, empty_y))>=0  ){
                push_block (puzzle, block_no, 0.5, 0  );
                push_block (puzzle, block_no, 0.5, 0  );
            }
            break;
        case 1:
            if ((block_no=get_at (puzzle, empty_x+1, empty_y))>=0  ){
                push_block (puzzle, block_no, -0.5, 0  );
                push_block (puzzle, block_no, -0.5, 0  );
            }
            break;
        case 2:
            if ((block_no=get_at (puzzle, empty_x, empty_y-1))>=0  ){
                push_block (puzzle, block_no, 0, 0.5);
                push_block (puzzle, block_no, 0, 0.5);
            }
            break;
        case 3:
            if ((block_no=get_at (puzzle, empty_x, empty_y+1))>=0  ){
                push_block (puzzle, block_no, 0, -0.5);
                push_block (puzzle, block_no, 0, -0.5);
            }
            break;
      }
    }
}

static void get_empty (Puzzle *puzzle, gint *col, gint *row){    
    if (!col || !row)
        return;
    for (*row=0; *row < puzzle->rows; (*row)++)
       for (*col=0; *col < puzzle->cols; (*col)++){
            gint item_no;
            gint found=0;
            for (item_no=0;item_no< puzzle->cols*puzzle->rows-1; item_no++){
                if (puzzle->item[item_no].x == *col && puzzle->item[item_no].y == *row){
                    found++;
                }
            }
            if (!found)
                return;
       }
    fprintf (stderr, "ERROR! this code should be unreachable!\n");
}

static int get_at (Puzzle *puzzle, gint col, gint row){    
   gint item_no;
   for (item_no=0;item_no< puzzle->cols*puzzle->rows-1; item_no++){
       if (puzzle->item[item_no].x == col && puzzle->item[item_no].y == row){
            return item_no;
       }
   }
   return -1;
}

/* initialize a board of specified size */
static void
board_init (GtkWidget *widget){
    Puzzle *puzzle = PUZZLE (widget);
    gint row, col;
    
    if (puzzle->item){
        free (puzzle->item);
    }
    puzzle->item=malloc (sizeof (struct PuzzleItem) * puzzle->rows * puzzle->cols);
    for (row=0;row<puzzle->rows;row++){
        for (col=0;col<puzzle->cols;col++){
            if (col==puzzle->cols-1 && row==puzzle->rows-1){
                puzzle->item[row*puzzle->cols + col].x=col;
                puzzle->item[row*puzzle->cols + col].y=row;
                sprintf (puzzle->item[row*puzzle->cols + col].label, "-");
            } else {
                puzzle->item[row*puzzle->cols + col].x=col;
                puzzle->item[row*puzzle->cols + col].y=row;
                sprintf (puzzle->item[row*puzzle->cols + col].label, "%i", row*puzzle->cols + col +1);
            }            
        }
    }
    puzzle_shuffle (puzzle, puzzle->shuffles);
    gtk_widget_queue_draw (GTK_WIDGET (puzzle));    
}

static gint
board_solved (Puzzle *puzzle) {
    gint item_no=0;
    
    for (item_no=0;item_no<puzzle->rows * puzzle->cols-1;item_no++ ) {
        struct PuzzleItem *item= & (puzzle->item [item_no]);
        gint row = item_no/puzzle->cols;
        gint col = item_no%puzzle->cols;
        if ( item->x != col || item->y != row)
            return 0;
    }
    return 1;
}


/* query which block is at the given mouse coordinates, 
   returns -1 if no block was found
*/
static int
query_pos (Puzzle *puzzle, int x, int y) {
    int item_no;
    
    cairo_t *ct = gtk_cairo_get_cairo (GTK_CAIRO (puzzle));

    cairo_save (ct);

    cairo_scale (ct, puzzle->ratio_x, puzzle->ratio_y);
    cairo_translate (ct, 0.5, 0.5);

    for (item_no=0;item_no<puzzle->rows*puzzle->cols-1;item_no++) {
        struct PuzzleItem *item= & (puzzle->item [item_no]);

        cairo_save (ct);
        cairo_rectangle_round (ct, item->x-0.4, item->y-0.4, 0.8, 0.8, 0.4);
        
        if (cairo_in_fill (ct, (x)/ puzzle->ratio_x -0.5, (y) / puzzle->ratio_y -0.5)){
                cairo_restore (ct);
                cairo_restore (ct);
            return item_no;
        }
        cairo_restore (ct);
    }

    cairo_restore (ct);
    return -1;    
}

/* draw the game board using the provided cairo context
*/
static void
draw_board (Puzzle *puzzle, cairo_t *ct) {
    int item_no;

    cairo_save (ct);
    cairo_scale (ct, puzzle->ratio_x, puzzle->ratio_y);
    cairo_translate (ct, 0.5, 0.5);

        
        for (item_no=0;item_no<puzzle->rows*puzzle->cols-1;item_no++) {
            struct PuzzleItem *item= & (puzzle->item [item_no]);
            cairo_save (ct);
               cairo_rectangle_round (ct, item->x-0.36, item->y-0.36, 0.8, 0.8, 0.4);
               cairo_set_line_width (ct, 0.07);
               if (item_no==puzzle->grabbed) {
                  cairo_set_gtk_color (ct, "dark", GTK_STATE_SELECTED);
               } else {
                  if ( fabs(floor(item->x)-item->x)<0.01 && fabs(floor(item->y)-item->y)<0.01) {
                      cairo_set_gtk_color (ct, "dark", GTK_STATE_NORMAL);
                  } else {
                      cairo_set_gtk_color (ct, "dark", GTK_STATE_ACTIVE);
                  }
               }
                
               cairo_stroke (ct);
            cairo_restore (ct);
            cairo_save (ct);
               cairo_rectangle_round (ct, item->x-0.4, item->y-0.4, 0.8, 0.8, 0.4);
               cairo_save (ct);
                  if (item_no==puzzle->grabbed) {
                     cairo_set_gtk_color (ct, "base", GTK_STATE_SELECTED);
                  } else {
                     if ( fabs(floor(item->x)-item->x)<0.01 && fabs(floor(item->y)-item->y)<0.01) {
                         cairo_set_gtk_color (ct, "base", GTK_STATE_NORMAL);
                     } else {
                         cairo_set_gtk_color (ct, "base", GTK_STATE_NORMAL);
                     }
                  }
                  cairo_fill (ct);
               cairo_restore (ct);
               if (item_no==puzzle->grabbed) {
                  cairo_set_gtk_color (ct, "mid", GTK_STATE_SELECTED);
               } else {
                  if ( fabs(floor(item->x)-item->x)<0.01 && fabs(floor(item->y)-item->y)<0.01) {
                      cairo_set_gtk_color (ct, "mid", GTK_STATE_NORMAL);
                  } else {
                      cairo_set_gtk_color (ct, "dark", GTK_STATE_ACTIVE);
                  }
               }
               cairo_set_line_width (ct, 0.05);
               cairo_stroke (ct);
            cairo_restore (ct);
            cairo_save (ct);
                {
                  cairo_text_extents_t extents;
                  cairo_select_font (ct, "sans", 0, 0);
                  cairo_scale_font (ct, 0.35);
                  cairo_text_extents (ct, item->label, &extents);

                  cairo_move_to (ct, item->x-extents.width/2 - extents.x_bearing   , item->y - extents.height/2 - extents.y_bearing);

                }
            
               if (item_no==puzzle->grabbed) {
                  cairo_set_gtk_color (ct, "text", GTK_STATE_SELECTED);
               } else {
                  if ( fabs(floor(item->x)-item->x)<0.01 && fabs(floor(item->y)-item->y)<0.01) {
                      cairo_set_gtk_color (ct, "text", GTK_STATE_NORMAL);
                  } else {
                      cairo_set_gtk_color (ct, "text", GTK_STATE_NORMAL);
                  }
               }
               cairo_show_text (ct, item->label);
            cairo_restore (ct);
        }

    cairo_restore (ct);
}


/* returns which block would be pushed by shifting 'block' the given amount
   in x direction
   
   return: >=0 block id
            -1 none
            -2 going off board
*/            
static int
who_is_pushed_x (Puzzle *puzzle, int block_no, double xdelta){
    int item_no;
    struct PuzzleItem *block= & (puzzle->item [block_no]);

    if (xdelta<0){
        if (block->x+xdelta<0)
            return -2;
    
        for (item_no=0;item_no<puzzle->rows*puzzle->cols-1;item_no++){
            struct PuzzleItem *item= & (puzzle->item [item_no]);
            if (block->x > item->x && block->x + xdelta - 1 <= item->x  
                && item->y < block->y + 1 && item->y > block->y -1  ){
                return item_no;
            }
        }
    } else {
        if (block->x+xdelta>puzzle->cols-1)
            return -2;

        for (item_no=0;item_no<puzzle->rows*puzzle->cols-1;item_no++){
            struct PuzzleItem *item= & (puzzle->item [item_no]);
            if (block->x < item->x && block->x + xdelta + 1 >= item->x   
                && item->y < block->y + 1 && item->y > block->y -1  ){            
                return item_no;
            }
        }
    }
    return -1;
}

/* returns which block would be pushed by shifting 'block' the given amount
   in y direction
   
   return: >=0 block id
            -1 none
            -2 going off board
*/            
static int
who_is_pushed_y (Puzzle *puzzle, int block_no, double ydelta){
    int item_no;
    struct PuzzleItem *block= & (puzzle->item [block_no]);

    if (ydelta<0){
        if (block->y+ydelta<0)
            return -2;
    
        for (item_no=0;item_no<puzzle->rows*puzzle->cols-1;item_no++){
            struct PuzzleItem *item= & (puzzle->item [item_no]);
            if (block->y > item->y && block->y + ydelta - 1 <= item->y   
                && item->x < block->x + 1 && item->x > block->x -1  ){

                return item_no;
            }
        }
    } else {
        if (block->y+ydelta>puzzle->rows-1)
            return -2;

        for (item_no=0;item_no<puzzle->rows*puzzle->cols-1;item_no++){
            struct PuzzleItem *item= & (puzzle->item [item_no]);
            if (block->y < item->y && block->y + ydelta + 1 >= item->y   
                && item->x < block->x + 1 && item->x > block->x -1  ){
                return item_no;
            }
        }
    }        
    return -1;
}

/* attempt to push a block the given delta in x and y directions */
static void
push_block (Puzzle *puzzle, int block_no, double xdelta, double ydelta) {
    struct PuzzleItem *block= & (puzzle->item [block_no]);
    gint pushed_x, pushed_y;
    
    if (floor (block->y)-block->y == 0 && xdelta != 0.0){
       if (xdelta>=0.9)
           xdelta=0.9;
       else if (xdelta <= -0.9)
           xdelta=-0.9;

       pushed_x = who_is_pushed_x (puzzle, block_no, xdelta);

       if ( pushed_x == -1 || pushed_x == -2){        
           block->x += xdelta;
           if (block->x >= puzzle->cols-1) {
               block->x = puzzle->cols-1;
           } else if (block->x < 0) {
               block->x = 0;
           }           
           return;
       }

       if (pushed_x >= 0) {
           push_block (puzzle, pushed_x, xdelta, 0);
           if (fabs (block->x - puzzle->item[pushed_x].x)>1.0001) {
               if (block->x < puzzle->item[pushed_x].x)
                   block->x = puzzle->item[pushed_x].x - 1;
               else
                   block->x = puzzle->item[pushed_x].x + 1;
           }
       }
    }
    
    
    if (floor (block->x)-block->x == 0) {
       if (ydelta>=0.9)
           ydelta=0.9;
       else if (ydelta <= -0.9)
           ydelta=-0.9;

       pushed_y = who_is_pushed_y (puzzle, block_no, ydelta);

       if (pushed_y == -1 || pushed_y == -2) {
           block->y += ydelta;
           if (block->y >= puzzle->rows-1) {
               block->y = puzzle->rows-1;
           } else if (block->y < 0) {
               block->y = 0;
           }
           return;
       }
       
       if (pushed_y >= 0) {
           push_block (puzzle, pushed_y, 0, ydelta);
           if (fabs (block->y - puzzle->item[pushed_y].y)>1.0001) {
               if (block->y < puzzle->item[pushed_y].y)
                   block->y = puzzle->item[pushed_y].y - 1;
               else
                   block->y = puzzle->item[pushed_y].y + 1;
           }
       }
    }
    return;    
}

--- NEW FILE: puzzle.h ---
#ifndef PUZZLE_H
#define PUZZLE_H

#include <gtk/gtk.h>

#ifdef __cplusplus
extern "C" {
#endif                          /* __cplusplus */

#define PUZZLE(obj)          GTK_CHECK_CAST (obj, puzzle_get_type (), Puzzle)
#define PUZZLE_CLASS(klass)  GTK_CHECK_CLASS_CAST (klass, puzzle_get_type (), PuzzleClass)
#define IS_PUZZLE(obj)       GTK_CHECK_TYPE (obj, puzzle_get_type ())

    typedef struct _Puzzle       Puzzle;
    typedef struct _PuzzleClass  PuzzleClass;

    GtkType puzzle_get_type (void);

    GtkWidget *puzzle_new (void);

#ifdef __cplusplus

}
#endif                          /* __cplusplus */
#endif                          /* PUZZLE_H */





More information about the cairo-commit mailing list