[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