[xiph-commits] r10060 - trunk/planarity

xiphmont at svn.xiph.org xiphmont at svn.xiph.org
Sun Sep 25 02:24:37 PDT 2005


Author: xiphmont
Date: 2005-09-25 02:24:23 -0700 (Sun, 25 Sep 2005)
New Revision: 10060

Added:
   trunk/planarity/dialog_finish.c
   trunk/planarity/dialog_finish.h
   trunk/planarity/dialog_level.c
   trunk/planarity/dialog_level.h
   trunk/planarity/dialog_level_icons.c
   trunk/planarity/dialog_pause.c
   trunk/planarity/dialog_pause.h
   trunk/planarity/gameboard_draw_box.c
   trunk/planarity/gameboard_draw_button.c
   trunk/planarity/gameboard_draw_button.h
   trunk/planarity/gameboard_draw_buttonbar.c
   trunk/planarity/gameboard_draw_curtain.c
   trunk/planarity/gameboard_draw_edge.c
   trunk/planarity/gameboard_draw_intersection.c
   trunk/planarity/gameboard_draw_main.c
   trunk/planarity/gameboard_draw_score.c
   trunk/planarity/gameboard_draw_selection.c
   trunk/planarity/gameboard_draw_text.c
   trunk/planarity/gameboard_draw_vertex.c
   trunk/planarity/gameboard_logic.c
   trunk/planarity/gameboard_logic_button.c
   trunk/planarity/gameboard_logic_buttonbar.c
   trunk/planarity/gameboard_logic_buttonbar.h
   trunk/planarity/gameboard_logic_mouse.c
   trunk/planarity/gameboard_logic_push.c
   trunk/planarity/graph_arrange.c
   trunk/planarity/graph_arrange.h
   trunk/planarity/graph_generate.c
   trunk/planarity/graph_generate.h
   trunk/planarity/graph_generate_mesh1.c
   trunk/planarity/graph_score.c
   trunk/planarity/main.h
   trunk/planarity/timer.c
   trunk/planarity/timer.h
Removed:
   trunk/planarity/arrange.c
   trunk/planarity/arrange.h
   trunk/planarity/boardgen.c
   trunk/planarity/box.c
   trunk/planarity/box.h
   trunk/planarity/button_base.c
   trunk/planarity/button_base.h
   trunk/planarity/buttonbar.c
   trunk/planarity/buttonbar.h
   trunk/planarity/buttons.c
   trunk/planarity/buttons.h
   trunk/planarity/finish.c
   trunk/planarity/finish.h
   trunk/planarity/gamestate.c
   trunk/planarity/gamestate.h
   trunk/planarity/generate.c
   trunk/planarity/generate.h
   trunk/planarity/pause.c
   trunk/planarity/pause.h
Modified:
   trunk/planarity/Makefile
   trunk/planarity/gameboard.c
   trunk/planarity/gameboard.h
   trunk/planarity/graph.c
   trunk/planarity/graph.h
   trunk/planarity/levelstate.c
   trunk/planarity/levelstate.h
   trunk/planarity/main.c
   trunk/planarity/version.h
Log:
Fully complete UI, code cleanup.  Remaining tasks:

Move dialog_level static storage to gameboard struct
comments, comments, comments!
hand design about, oh, fifty-odd puzzle levels and new graph gen algos



Modified: trunk/planarity/Makefile
===================================================================
--- trunk/planarity/Makefile	2005-09-24 22:02:43 UTC (rev 10059)
+++ trunk/planarity/Makefile	2005-09-25 09:24:23 UTC (rev 10060)
@@ -16,10 +16,24 @@
 # work for on most modern linux distros without any of the
 # bleeding-edge GTK 2.7/2.8 libs installed.
 
-SRC  = graph.c arrange.c gameboard.c generate.c gamestate.c button_base.c\
-	buttons.c buttonbar.c box.c pause.c finish.c levelstate.c main.c
-OBJ  = graph.o arrange.o gameboard.o generate.o gamestate.o button_base.o\
-	buttons.o buttonbar.o box.o pause.o finish.o levelstate.o main.o
+SRC  = dialog_finish.c gameboard_logic.c dialog_pause.c gameboard_logic_button.c\
+	gameboard.c gameboard_logic_buttonbar.c gameboard_draw_box.c\
+	gameboard_logic_mouse.c gameboard_draw_button.c gameboard_logic_push.c\
+	gameboard_draw_buttonbar.c graph.c gameboard_draw_curtain.c\
+	graph_score.c graph_arrange.c gameboard_draw_edge.c graph_generate.c\
+	gameboard_draw_intersection.c graph_generate_mesh1.c gameboard_draw_main.c\
+	gameboard_draw_score.c main.c gameboard_draw_selection.c\
+	timer.c gameboard_draw_vertex.c levelstate.c dialog_level.c\
+	dialog_level_icons.c gameboard_draw_text.c
+OBJ  = dialog_finish.o gameboard_logic.o dialog_pause.o gameboard_logic_button.o\
+	gameboard.o gameboard_logic_buttonbar.o gameboard_draw_box.o\
+	gameboard_logic_mouse.o gameboard_draw_button.o gameboard_logic_push.o\
+	gameboard_draw_buttonbar.o graph.o gameboard_draw_curtain.o\
+	graph_score.o graph_arrange.o gameboard_draw_edge.o graph_generate.o\
+	gameboard_draw_intersection.o graph_generate_mesh1.o gameboard_draw_main.o\
+	gameboard_draw_score.o main.o gameboard_draw_selection.o\
+	timer.o gameboard_draw_vertex.o levelstate.o dialog_level.o\
+	dialog_level_icons.o gameboard_draw_text.o
 GCF  = `pkg-config --static --cflags "gtk+-2.0 >= 2.7.2"`
 
 # uncomment below for a normal dynamic build
@@ -54,7 +68,7 @@
 	$(STRIP) $(TARGET)
 
 debug:
-	$(MAKE) target CFLAGS="-g -Wall -W -Wno-unused-parameter -D__NO_MATH_INLINES $(GCF) $(ADD_DEF)"
+	$(MAKE) target CFLAGS="-g -Wall -W -Wno-unused-parameter -D__NO_MATH_INLINES $(GCF) $(ADD_DEF)" -lefence
 
 profile:
 	$(MAKE) target CFLAGS="-pg -g -O2 -ffast-math $(GCF) $(ADD_DEF)" LIBS="$(LIBS) -lgprof-helper "
@@ -74,7 +88,7 @@
 
 target:  $(OBJ) 
 	./touch-version
-	$(LD) $(OBJ) $(CFLAGS) -o $(TARGET) $(LIBS) $(LDF) 
+	$(LD) $(OBJ) $(CFLAGS) -o $(TARGET) $(LIBS) $(LDF)
 
 static-target: $(OBJ)
 	./touch-version

Deleted: trunk/planarity/arrange.c
===================================================================
--- trunk/planarity/arrange.c	2005-09-24 22:02:43 UTC (rev 10059)
+++ trunk/planarity/arrange.c	2005-09-25 09:24:23 UTC (rev 10060)
@@ -1,38 +0,0 @@
-#include <math.h>
-
-#include "graph.h"
-#include "arrange.h"
-#include "gamestate.h"
-
-void arrange_verticies_circle(graph *g){
-  vertex *v = g->verticies;
-  int n = g->vertex_num;
-  int bw=get_orig_width();
-  int bh=get_orig_height();
-  int radius=min(bw,bh)*.45;
-  int i;
-  for(i=0;i<n;i++){
-    int x = rint( radius * cos( i*M_PI*2./n));
-    int y = rint( radius * sin( i*M_PI*2./n));
-    move_vertex(g,v,x+(bw>>1),y+(bh>>1));
-    v=v->next;
-  }
-}
-
-void arrange_verticies_mesh(graph *g, int width, int height){
-  vertex *v = g->verticies;
-  int bw=get_orig_width();
-  int bh=get_orig_height();
-  int spacing=min((float)bw/width,(float)bh/height)*.9;
-  int x,y;
-
-  int xpad=(bw - (width-1)*spacing)/2;
-  int ypad=(bh - (height-1)*spacing)/2;
-
-  for(y=0;y<height;y++){
-    for(x=0;x<width;x++){
-      move_vertex(g,v,x*spacing+xpad,y*spacing+ypad);
-      v=v->next;
-    }
-  }
-}

Deleted: trunk/planarity/arrange.h
===================================================================
--- trunk/planarity/arrange.h	2005-09-24 22:02:43 UTC (rev 10059)
+++ trunk/planarity/arrange.h	2005-09-25 09:24:23 UTC (rev 10060)
@@ -1,2 +0,0 @@
-extern void arrange_verticies_circle(graph *g);
-extern void arrange_verticies_mesh(graph *g, int width, int height);

Deleted: trunk/planarity/boardgen.c
===================================================================
--- trunk/planarity/boardgen.c	2005-09-24 22:02:43 UTC (rev 10059)
+++ trunk/planarity/boardgen.c	2005-09-25 09:24:23 UTC (rev 10060)
@@ -1,9 +0,0 @@
-char *board_level_to_name(int level){
-
-
-}
-
-int board_name_to_level(int level){
-
-
-}

Deleted: trunk/planarity/box.c
===================================================================
--- trunk/planarity/box.c	2005-09-24 22:02:43 UTC (rev 10059)
+++ trunk/planarity/box.c	2005-09-25 09:24:23 UTC (rev 10060)
@@ -1,251 +0,0 @@
-#include <math.h>
-#include <gtk/gtk.h>
-#include "graph.h"
-#include "box.h"
-
-void topbox (cairo_t *c, double w, double h){
-  
-  double x0 = B_BORDER+B_RADIUS;
-  double y0 = B_BORDER;
-
-  double x1 = B_BORDER;
-  double y1 = B_BORDER+B_RADIUS;
-
-  double x2 = B_BORDER;
-  double y2 = h-B_BORDER-B_RADIUS;
-
-  double x3 = B_BORDER+B_RADIUS;
-  double y3 = h-B_BORDER;
-
-  double x4 = w/2 - B_HUMP - B_RADIUS;
-  double y4 = h-B_BORDER;
-
-  double x5 = x4+B_RADIUS;
-  double y5 = y4;
-
-
-  double x7 = w/2 + B_HUMP + B_RADIUS;
-  double y7 = h-B_BORDER;
-
-  double x8 = w - B_BORDER -B_RADIUS;
-  double y8 = h-B_BORDER;
-
-  double x9 = w - B_BORDER;
-  double y9 = h-B_BORDER-B_RADIUS;
-
-  double x10 = w - B_BORDER;
-  double y10 = B_BORDER+B_RADIUS;
-
-  double x11 = w - B_BORDER-B_RADIUS;
-  double y11 = B_BORDER;
-
-  cairo_set_line_width(c,B_LINE);
-
-  cairo_move_to  (c, x0, y0);
-  cairo_curve_to (c, x1,y0, x1,y0, x1,y1);
-  cairo_line_to (c, x2,y2);
-  cairo_curve_to (c, x2,y3, x2,y3, x3,y3);
-  cairo_line_to (c, x4,y4);
-
-  {
-    double hh = get_board_height()*.5+50;
-
-    double radius = sqrt( (w*.5-x5) * (w*.5-x5) + (hh-y5) * (hh-y5));
-    double siderad = atan( (x5-w*.5)/(hh-y5));
-
-    double xx = sin(siderad+.05)*radius+w*.5;
-    double yy = -cos(siderad+.05)*radius+hh;
-    double x = sin(siderad+.1)*radius+w*.5;
-    double y = -cos(siderad+.1)*radius+hh;
-
-    cairo_curve_to (c, x4+B_RADIUS,y4, xx,yy, x,y);
-    cairo_arc(c,w*.5,hh,radius,-M_PI*.5+siderad+.1,-M_PI*.5-siderad-.1);
-
-    xx = -sin(siderad+.05)*radius+w*.5;
-    yy = -cos(siderad+.05)*radius+hh;
-    xx = -sin(siderad+.1)*radius+w*.5;
-    yy = -cos(siderad+.1)*radius+hh;
-
-    cairo_curve_to (c, xx,yy, x7-B_RADIUS,y7, x7,y7);
-
-  }
-
-  cairo_line_to  (c, x8,y8);
-  cairo_curve_to (c, x9,y8, x9,y8, x9,y9);
-  cairo_line_to  (c, x10,y10);
-  cairo_curve_to (c, x10,y11, x10,y11, x11,y11);
-  cairo_close_path (c);
-
-  cairo_set_source_rgba (c, B_COLOR);
-  cairo_fill_preserve (c);
-  cairo_set_source_rgba (c, B_LINE_COLOR);
-  cairo_stroke (c);
-
-}
-
-void bottombox (cairo_t *c, double w, double h){
-  
-  double x0 = B_BORDER+B_RADIUS;
-  double y0 = h-B_BORDER;
-
-  double x1 = B_BORDER;
-  double y1 = h-B_BORDER-B_RADIUS;
-
-  double x2 = B_BORDER;
-  double y2 = B_BORDER+B_RADIUS;
-
-  double x3 = B_BORDER+B_RADIUS;
-  double y3 = B_BORDER;
-
-  double x4 = w/2 - B_HUMP - B_RADIUS;
-  double y4 = B_BORDER;
-
-  double x5 = x4+B_RADIUS;
-  double y5 = y4;
-
-
-  double x7 = w/2 + B_HUMP + B_RADIUS;
-  double y7 = B_BORDER;
-
-  double x8 = w - B_BORDER -B_RADIUS;
-  double y8 = B_BORDER;
-
-  double x9 = w - B_BORDER;
-  double y9 = B_BORDER+B_RADIUS;
-
-  double x10 = w - B_BORDER;
-  double y10 = h- B_BORDER -B_RADIUS;
-
-  double x11 = w - B_BORDER-B_RADIUS;
-  double y11 = h-B_BORDER;
-
-  cairo_set_line_width(c,B_LINE);
-
-  cairo_move_to  (c, x0, y0);
-  cairo_curve_to (c, x1,y0, x1,y0, x1,y1);
-  cairo_line_to (c, x2,y2);
-  cairo_curve_to (c, x2,y3, x2,y3, x3,y3);
-  cairo_line_to (c, x4,y4);
-
-  {
-    double hh = get_board_height()*.5+50;
-
-    double radius = sqrt( (w*.5-x5) * (w*.5-x5) + (y5+hh-h) * (y5+hh-h));
-    double siderad = atan( (x5-w*.5)/(y5+hh-h));
-
-    double xx = sin(siderad+.05)*radius+w*.5;
-    double yy = cos(siderad+.05)*radius-hh+h;
-    double x = sin(siderad+.1)*radius+w*.5;
-    double y = cos(siderad+.1)*radius-hh+h;
-
-    cairo_curve_to (c, x4+B_RADIUS,y4, xx,yy, x,y);
-    cairo_arc_negative(c,w*.5,h-hh,radius,M_PI*.5-siderad-.1,M_PI*.5+siderad+.1);
-
-    xx = -sin(siderad+.05)*radius+w*.5;
-    yy = cos(siderad+.05)*radius-hh+h;
-    xx = -sin(siderad+.1)*radius+w*.5;
-    yy = cos(siderad+.1)*radius-hh+h;
-
-    cairo_curve_to (c, xx,yy, x7-B_RADIUS,y7, x7,y7);
-
-  }
-
-  cairo_line_to  (c, x8,y8);
-  cairo_curve_to (c, x9,y8, x9,y8, x9,y9);
-  cairo_line_to  (c, x10,y10);
-  cairo_curve_to (c, x10,y11, x10,y11, x11,y11);
-  cairo_close_path (c);
-
-  cairo_set_source_rgba (c, B_COLOR);
-  cairo_fill_preserve (c);
-  cairo_set_source_rgba (c, B_LINE_COLOR);
-  cairo_stroke (c);
-
-}
-
-void centerbox (cairo_t *c, int x, int y, double w, double h){
-  
-  double x0 = B_BORDER+B_RADIUS;
-  double y0 = h-B_BORDER;
-
-  double x1 = B_BORDER;
-  double y1 = h-B_BORDER-B_RADIUS;
-
-  double x2 = B_BORDER;
-  double y2 = B_BORDER+B_RADIUS;
-
-  double x3 = B_BORDER+B_RADIUS;
-  double y3 = B_BORDER;
-
-  double x8 = w - B_BORDER -B_RADIUS;
-  double y8 = B_BORDER;
-
-  double x9 = w - B_BORDER;
-  double y9 = B_BORDER+B_RADIUS;
-
-  double x10 = w - B_BORDER;
-  double y10 = h- B_BORDER -B_RADIUS;
-
-  double x11 = w - B_BORDER-B_RADIUS;
-  double y11 = h-B_BORDER;
-
-  cairo_save(c);
-  cairo_translate(c,x,y);
-  cairo_set_line_width(c,B_LINE);
-
-  cairo_move_to  (c, x0, y0);
-  cairo_curve_to (c, x1,y0, x1,y0, x1,y1);
-  cairo_line_to (c, x2,y2);
-  cairo_curve_to (c, x2,y3, x2,y3, x3,y3);
-  cairo_line_to (c, x8,y8);
-  cairo_curve_to (c, x9,y8, x9,y8, x9,y9);
-  cairo_line_to  (c, x10,y10);
-  cairo_curve_to (c, x10,y11, x10,y11, x11,y11);
-  cairo_close_path (c);
-
-  cairo_set_source_rgba (c, B_COLOR);
-  cairo_fill_preserve (c);
-  cairo_set_source_rgba (c, B_LINE_COLOR);
-  cairo_stroke (c);
-
-  cairo_restore(c);
-}
-
-void borderbox_path (cairo_t *c, int x, int y, double w, double h){
-  
-  double x0 = x+ B_RADIUS;
-  double y0 = y+ h;
-
-  double x1 = x;
-  double y1 = y+ h-B_RADIUS;
-
-  double x2 = x;
-  double y2 = y+ B_RADIUS;
-
-  double x3 = x+ B_RADIUS;
-  double y3 = y;
-
-  double x8 = x+ w -B_RADIUS;
-  double y8 = y;
-
-  double x9 = x+ w;
-  double y9 = y+ B_RADIUS;
-
-  double x10 = x+ w;
-  double y10 = y+ h -B_RADIUS;
-
-  double x11 = x+ w -B_RADIUS;
-  double y11 = y+h;
-
-  cairo_move_to  (c, x0, y0);
-  cairo_curve_to (c, x1,y0, x1,y0, x1,y1);
-  cairo_line_to (c, x2,y2);
-  cairo_curve_to (c, x2,y3, x2,y3, x3,y3);
-  cairo_line_to (c, x8,y8);
-  cairo_curve_to (c, x9,y8, x9,y8, x9,y9);
-  cairo_line_to  (c, x10,y10);
-  cairo_curve_to (c, x10,y11, x10,y11, x11,y11);
-  cairo_close_path (c);
-
-}
-

Deleted: trunk/planarity/box.h
===================================================================
--- trunk/planarity/box.h	2005-09-24 22:02:43 UTC (rev 10059)
+++ trunk/planarity/box.h	2005-09-25 09:24:23 UTC (rev 10060)
@@ -1,16 +0,0 @@
-#define B_LINE 1
-#define B_BORDER 6.5
-#define B_RADIUS 20
-#define B_HUMP 130
-#define B_COLOR         .1,.1,.7,.1
-#define B_LINE_COLOR     0, 0,.7,.3
-#define TEXT_COLOR      .0,.0,.7,.6
-
-
-#define SCOREHEIGHT 50
-
-
-extern void topbox (cairo_t *c, double w, double h);
-extern void bottombox (cairo_t *c, double w, double h);
-extern void centerbox (cairo_t *c, int x, int y, double w, double h);
-extern void borderbox_path (cairo_t *c, int x, int y, double w, double h);

Deleted: trunk/planarity/button_base.c
===================================================================
--- trunk/planarity/button_base.c	2005-09-24 22:02:43 UTC (rev 10059)
+++ trunk/planarity/button_base.c	2005-09-25 09:24:23 UTC (rev 10060)
@@ -1,606 +0,0 @@
-#include <math.h>
-#include <string.h>
-#include <gtk/gtk.h>
-#include <gdk/gdk.h>
-
-#include "graph.h"
-#include "gameboard.h"
-#include "gamestate.h"
-#include "buttons.h"
-#include "button_base.h"
-
-
-/************************ manage the buttons for buttonbar and dialogs *********************/
-
-buttonstate states[NUMBUTTONS];
-
-int buttons_ready=0;
-static int allclear=1; // actually just a shirt-circuit
-static buttonstate *grabbed=0;
-
-/* determine the x/y/w/h box around the rollover text */
-static GdkRectangle rollover_box(buttonstate *b){
-  GdkRectangle r;
-  int width = get_board_width();
-  int height = get_board_height();
-
-  int x = b->x - b->ex.width/2 + b->ex.x_bearing -2;
-  int y = b->y - BUTTON_RADIUS - BUTTON_TEXT_BORDER + b->ex.y_bearing -2;
-
-  if(x<BUTTON_TEXT_BORDER)x=BUTTON_TEXT_BORDER;
-  if(y<BUTTON_TEXT_BORDER)y=BUTTON_TEXT_BORDER;
-  if(x+b->ex.width >= width - BUTTON_TEXT_BORDER)
-    x = width - BUTTON_TEXT_BORDER - b->ex.width;
-  if(y+b->ex.height >= height - BUTTON_TEXT_BORDER)
-    y = height - BUTTON_TEXT_BORDER - b->ex.height;
-
-  r.x=x;
-  r.y=y;
-  r.width=b->ex.width+5;
-  r.height=b->ex.height+5;
-
-  return r;
-}
-
-/* draw the actual rollover */
-static void stroke_rollover(Gameboard *g, buttonstate *b, cairo_t *c){
-  cairo_matrix_t m;
-
-  cairo_select_font_face (c, "Arial",
-			  CAIRO_FONT_SLANT_NORMAL,
-			  CAIRO_FONT_WEIGHT_BOLD);
-
-  cairo_matrix_init_scale (&m, BUTTON_TEXT_SIZE);
-  cairo_set_font_matrix (c,&m);
-  
-  {
-    GdkRectangle r=rollover_box(b);
-
-    cairo_move_to (c, r.x - b->ex.x_bearing +2, r.y -b->ex.y_bearing+2 );
-
-    cairo_set_line_width(c,3);
-    cairo_set_source_rgba(c,1,1,1,.9);
-    cairo_text_path (c, b->rollovertext);  
-    cairo_stroke(c);
-
-    cairo_set_source_rgba(c,BUTTON_TEXT_COLOR);
-    cairo_move_to (c, r.x - b->ex.x_bearing +2, r.y -b->ex.y_bearing+2 );
-    
-
-    cairo_show_text (c, b->rollovertext);  
-
-  }
-}
-
-/* request an expose for a button region*/
-static void invalidate_button(Gameboard *g,buttonstate *b){
-  GdkRectangle r;
-
-  r.x=b->x-BUTTON_RADIUS-1;
-  r.y=b->y-BUTTON_RADIUS-1;
-  r.width=BUTTON_RADIUS*2+2;
-  r.height=BUTTON_RADIUS*2+2;
-
-  gdk_window_invalidate_rect(g->w.window,&r,FALSE);
-}
-
-/* request an expose for a rollover region*/
-static void invalidate_rollover(Gameboard *g,buttonstate *b){
-  GdkRectangle r = rollover_box(b);
-  invalidate_button(g,b);
-  gdk_window_invalidate_rect(g->w.window,&r,FALSE);
-}
-
-/* like invalidation, but just expands a rectangular region */
-static void expand_rectangle_button(buttonstate *b,GdkRectangle *r){
-  int x=b->x-BUTTON_RADIUS-1;
-  int y=b->y-BUTTON_RADIUS-1;
-  int x2=x+BUTTON_RADIUS*2+2;
-  int y2=y+BUTTON_RADIUS*2+2;
-
-  int rx2=r->x+r->width;
-  int ry2=r->y+r->height;
-
-  if(r->width==0 || r->height==0){
-    r->x=x;
-    r->y=y;
-    r->width=x2-x;
-    r->height=y2-y;
-  }
-
-  if(x<r->x)
-    r->x=x;
-  if(y<r->y)
-    r->y=y;
-  if(rx2<x2)
-    rx2=x2;
-  r->width=rx2-r->x;
-  if(ry2<y2)
-    ry2=y2;
-  r->height=ry2-r->y;
-}
-
-/* like invalidation, but just expands a rectangular region */
-static void expand_rectangle_rollover(buttonstate *b,GdkRectangle *r){
-  GdkRectangle rr = rollover_box(b);
-  int x=rr.x;
-  int y=rr.y;
-  int x2=x+rr.width;
-  int y2=y+rr.height;
-
-  int rx2=r->x+r->width;
-  int ry2=r->y+r->height;
-
-  if(r->width==0 || r->height==0){
-    r->x=x;
-    r->y=y;
-    r->width=x2-x;
-    r->height=y2-y;
-  }
-
-  if(x<r->x)
-    r->x=x;
-  if(y<r->y)
-    r->y=y;
-  if(rx2<x2)
-    rx2=x2;
-  r->width=rx2-r->x;
-  if(ry2<y2)
-    ry2=y2;
-  r->height=ry2-r->y;
-}
-
-/* match x/y to a button if any */
-static buttonstate *find_button(int x,int y){
-  int i;
-  for(i=0;i<NUMBUTTONS;i++){
-    buttonstate *b=states+i;
-    if(b->position)
-      if( (b->x-x)*(b->x-x) + (b->y-y)*(b->y-y) <= BUTTON_RADIUS*BUTTON_RADIUS+4)
-	if(b->x != b->x_inactive)
-	  return b;
-  }
-  return 0;
-}
-
-/* set a button to pressed or lit */
-static void button_set_state(Gameboard *g, buttonstate *b, int rollover, int press){
-  int i;
-  
-  if(b->rollover == rollover && b->press == press)return;
-
-  for(i=0;i<NUMBUTTONS;i++){
-    buttonstate *bb=states+i;
-    if(bb->position){
-      if(bb!=b){
-	if(bb->rollover)
-	  invalidate_rollover(g,bb);
-	if(bb->press)
-	  invalidate_button(g,bb);
-	
-	bb->rollover=0;
-	bb->press=0;
-      }else{
-	if(bb->rollover != rollover)
-	  invalidate_rollover(g,bb);
-	if(bb->press != press)
-	  invalidate_button(g,bb);
-	
-	bb->rollover=rollover;
-	bb->press=press;
-      }
-    }
-  }
-  allclear=0;
-}
-
-/* cache buttons as small surfaces */
-static cairo_surface_t *cache_button(Gameboard *g,
-				     void (*draw)(cairo_t *c, 
-						  double x, 
-						  double y),
-				     double pR,double pG,double pB,double pA,
-				     double fR,double fG,double fB,double fA){
-  cairo_surface_t *ret = 
-    cairo_surface_create_similar (cairo_get_target (g->wc),
-				  CAIRO_CONTENT_COLOR_ALPHA,
-				  BUTTON_RADIUS*2+1,
-				  BUTTON_RADIUS*2+1);
-  
-  cairo_t *c = cairo_create(ret);
-
-  cairo_save(c);
-  cairo_set_operator(c,CAIRO_OPERATOR_CLEAR);
-  cairo_set_source_rgba (c, 1,1,1,1);
-  cairo_paint(c);
-  cairo_restore(c);
-     
-  cairo_set_source_rgba(c,fR,fG,fB,fA);
-  cairo_set_line_width(c,BUTTON_LINE_WIDTH);
-  draw(c,BUTTON_RADIUS+.5,BUTTON_RADIUS+.5);
-
-  cairo_set_source_rgba(c,pR,pG,pB,pA);
-  cairo_stroke(c);
-  
-  cairo_destroy(c);
-  return ret;
-}
-
-/* determine the x/y/w/h box around a button */
-static GdkRectangle button_box(buttonstate *b){
-  GdkRectangle r;
-
-  int x = b->x - BUTTON_RADIUS-1;
-  int y = b->y - BUTTON_RADIUS-1;
-  r.x=x;
-  r.y=y;
-  r.width = BUTTON_RADIUS*2+2;
-  r.height= BUTTON_RADIUS*2+2;
-
-  return r;
-}
-
-/* do the animation math for one button for one frame */
-static int animate_one(buttonstate *b, GdkRectangle *r){
-  int ret=0;
-
-  if(b->target_x_active != b->x_active){
-    ret=1;
-    if(b->target_x_active > b->x_active){
-      b->x_active+=BUTTON_DELTA;
-      if(b->x_active>b->target_x_active)
-	b->x_active=b->target_x_active;
-    }else{
-      b->x_active-=BUTTON_DELTA;
-      if(b->x_active<b->target_x_active)
-	b->x_active=b->target_x_active;
-    }
-  }
-
-  if(b->target_x_inactive != b->x_inactive){
-    ret=1;
-    if(b->target_x_inactive > b->x_inactive){
-      b->x_inactive+=BUTTON_DELTA;
-      if(b->x_inactive>b->target_x_inactive)
-	b->x_inactive=b->target_x_inactive;
-    }else{
-      b->x_inactive-=BUTTON_DELTA;
-      if(b->x_inactive<b->target_x_inactive)
-	b->x_inactive=b->target_x_inactive;
-    }
-  }
-
-  if(b->target_x != b->x){
-    ret=1;
-    expand_rectangle_button(b,r);
-    if(b->rollover)
-      expand_rectangle_rollover(b,r);
-
-    if(b->target_x > b->x){
-      b->x+=BUTTON_DELTA;
-      if(b->x>b->target_x)b->x=b->target_x;
-    }else{
-      b->x-=BUTTON_DELTA;
-      if(b->x<b->target_x)b->x=b->target_x;
-    }
-    expand_rectangle_button(b,r);
-    if(b->rollover)
-      expand_rectangle_rollover(b,r);
-  }
-
-  if(b->alphad != b->x_active - b->x){
-    double alpha=0;
-    if(b->x_inactive>b->x_active){
-      if(b->x<=b->x_active)
-	alpha=1.;
-      else if (b->x>=b->x_inactive)
-	alpha=0.;
-      else
-	alpha = (double)(b->x_inactive-b->x)/(b->x_inactive-b->x_active);
-    }else if (b->x_inactive<b->x_active){
-      if(b->x>=b->x_active)
-	alpha=1.;
-      else if (b->x<=b->x_inactive)
-	alpha=0.;
-      else
-	alpha = (double)(b->x_inactive-b->x)/(b->x_inactive-b->x_active);
-    }
-    if(alpha != b->alpha){
-      ret=1;
-      expand_rectangle_button(b,r);
-      b->alpha=alpha;
-    }
-    b->alphad = b->x_active - b->x;
-  }
-  return ret;
-}
-
-/******************** toplevel abstraction calls *********************/
-
-/* initialize the persistent caches; called once when gameboard is
-   first created */
-void init_buttons(Gameboard *g){
-  memset(states,0,sizeof(states));
-
-  states[0].idle = cache_button(g, path_button_exit, 
-				BUTTON_QUIT_IDLE_PATH, 
-				BUTTON_QUIT_IDLE_FILL);
-  states[0].lit  = cache_button(g, path_button_exit, 
-				BUTTON_QUIT_LIT_PATH, 
-				BUTTON_QUIT_LIT_FILL);
-
-  states[1].idle = cache_button(g, path_button_back, 
-				BUTTON_IDLE_PATH,
-				BUTTON_IDLE_FILL);
-  states[1].lit  = cache_button(g, path_button_back, 
-				BUTTON_LIT_PATH,
-				BUTTON_LIT_FILL);
-  states[2].idle = cache_button(g, path_button_reset, 
-				BUTTON_IDLE_PATH,
-				BUTTON_IDLE_FILL);
-  states[2].lit  = cache_button(g, path_button_reset, 
-				BUTTON_LIT_PATH,
-				BUTTON_LIT_FILL);
-  states[3].idle = cache_button(g, path_button_pause, 
-				BUTTON_IDLE_PATH,
-				BUTTON_IDLE_FILL);
-  states[3].lit  = cache_button(g, path_button_pause, 
-				BUTTON_LIT_PATH,
-				BUTTON_LIT_FILL);
-  states[4].idle = cache_button(g, path_button_help, 
-				BUTTON_IDLE_PATH,
-				BUTTON_IDLE_FILL);
-  states[4].lit  = cache_button(g, path_button_help, 
-				BUTTON_LIT_PATH,
-				BUTTON_LIT_FILL);
-  states[5].idle = cache_button(g, path_button_expand, 
-				BUTTON_IDLE_PATH,
-				BUTTON_IDLE_FILL);
-  states[5].lit  = cache_button(g, path_button_expand, 
-				BUTTON_LIT_PATH,
-				BUTTON_LIT_FILL);
-  states[6].idle = cache_button(g, path_button_shrink, 
-				BUTTON_IDLE_PATH,
-				BUTTON_IDLE_FILL);
-  states[6].lit  = cache_button(g, path_button_shrink, 
-				BUTTON_LIT_PATH,
-				BUTTON_LIT_FILL);
-  states[7].idle = cache_button(g, path_button_lines, 
-				BUTTON_IDLE_PATH,
-				BUTTON_IDLE_FILL);
-  states[7].lit  = cache_button(g, path_button_lines, 
-				BUTTON_LIT_PATH,
-				BUTTON_LIT_FILL);
-  states[8].idle = cache_button(g, path_button_int, 
-				BUTTON_IDLE_PATH,
-				BUTTON_IDLE_FILL);
-  states[8].lit  = cache_button(g, path_button_int, 
-				BUTTON_LIT_PATH,
-				BUTTON_LIT_FILL);
-  states[9].idle = cache_button(g, path_button_check, 
-				BUTTON_CHECK_IDLE_PATH,
-				BUTTON_CHECK_IDLE_FILL);
-  states[9].lit  = cache_button(g, path_button_check, 
-				BUTTON_CHECK_LIT_PATH,
-				BUTTON_CHECK_LIT_FILL);
-  states[10].idle= cache_button(g, path_button_play, 
-				BUTTON_CHECK_IDLE_PATH,
-				BUTTON_CHECK_IDLE_FILL);
-  states[10].lit = cache_button(g, path_button_play, 
-				BUTTON_CHECK_LIT_PATH,
-				BUTTON_CHECK_LIT_FILL);
-}
-
-/* cache the text extents of a rollover */
-void rollover_extents(Gameboard *g, buttonstate *b){
-  
-  cairo_matrix_t m;
-
-  cairo_t *c = cairo_create(g->foreground);
-  cairo_select_font_face (c, "Arial",
-			  CAIRO_FONT_SLANT_NORMAL,
-			  CAIRO_FONT_WEIGHT_BOLD);
-  
-  cairo_matrix_init_scale (&m, BUTTON_TEXT_SIZE);
-  cairo_set_font_matrix (c,&m);
-  
-  cairo_text_extents (c, b->rollovertext, &b->ex);
-
-  cairo_destroy(c);
-}
-
-/* perform a single frame of animation for all buttons/rollovers */
-gboolean animate_button_frame(gpointer ptr){
-  Gameboard *g=(Gameboard *)ptr;
-  GdkRectangle expose;
-  int ret=0,i,pos;
-  
-  if(!g->first_expose)return 1;
-  
-  /* avoid the normal expose event mechanism
-     during the button animations.  This direct method is much faster as
-     it won't accidentally combine exposes over long stretches of
-     alpha-blended surfaces. */
-  
-  for(pos=1;pos<=3;pos++){
-    memset(&expose,0,sizeof(expose));
-    for(i=0;i<NUMBUTTONS;i++)
-      if(states[i].position == pos)
-	ret|=animate_one(states+i,&expose);
-    if(expose.width && expose.height)
-      run_immediate_expose(g,
-			   expose.x,
-			   expose.y,
-			   expose.width,
-			   expose.height);
-  }
-  return ret;
-}
-
-/* the normal expose method for all buttons; called from the master
-   widget's expose */
-void expose_buttons(Gameboard *g,cairo_t *c, int x,int y,int w,int h){
-  int i;
-  
-  for(i=0;i<NUMBUTTONS;i++){
-
-    buttonstate *b=states+i;
-
-    if(b->position){
-      GdkRectangle r = rollover_box(b);
-      GdkRectangle br = button_box(b);
-      
-      if(x < br.x+br.width &&
-	 y < br.y+br.height &&
-	 x+w > br.x &&
-       y+h > br.y) {
-	
-	cairo_set_source_surface(c,
-				 (b->rollover || b->press ? b->lit : b->idle),
-				 br.x,
-				 br.y);
-	
-	if(b->press)
-	  cairo_paint_with_alpha(c,b->alpha);
-	cairo_paint_with_alpha(c,b->alpha);
-	
-      }
-      
-      if(b->rollover || b->press)
-	if(x < r.x+r.width &&
-	   y < r.y+r.height &&
-	   x+w > r.x &&
-	   y+h > r.y)
-	  
-	  stroke_rollover(g,b,c);
-    }
-  }
-}
-
-/* resize the button bar; called from master resize in gameboard */
-void resize_buttons(int oldw,int oldh,int w,int h){
-  int i;
-  int dx=w/2-oldw/2;
-  int dy=h/2-oldh/2;
-
-  for(i=0;i<NUMBUTTONS;i++){
-    if(states[i].position == 2){
-      states[i].x+=dx;
-      states[i].target_x+=dx;
-      states[i].x_active+=dx;
-      states[i].target_x_active+=dx;
-      states[i].x_inactive+=dx;
-      states[i].target_x_inactive+=dx;
-      states[i].y+=dy;
-    }
-  }
-
-  dx=w-oldw;
-  dy=h-oldh;
-
-  for(i=0;i<NUMBUTTONS;i++){
-    if(states[i].position == 1){
-      states[i].y+=dy;
-    }
-  }
-
-  for(i=0;i<NUMBUTTONS;i++){
-    if(states[i].position == 3){
-      states[i].x+=dx;
-      states[i].target_x+=dx;
-      states[i].x_active+=dx;
-      states[i].target_x_active+=dx;
-      states[i].x_inactive+=dx;
-      states[i].target_x_inactive+=dx;
-      states[i].y+=dy;
-    }
-  }
-}
-
-/* clear all buttons to unpressed/unlit */
-void button_clear_state(Gameboard *g){
-  int i;
-  if(!allclear){
-    for(i=0;i<NUMBUTTONS;i++){
-      buttonstate *bb=states+i;
-      if(bb->position){
-	if(bb->rollover)
-	  invalidate_rollover(g,bb);
-	if(bb->press)
-	  invalidate_button(g,bb);
-	
-	bb->rollover=0;
-	bb->press=0;
-      }
-    }
-  }
-  allclear=1;
-  grabbed=0;
-}
-
-/* handles mouse motion events; called out of gameboard's master
-   motion handler; returns nonzero if it used focus */
-int button_motion_event(Gameboard *g, GdkEventMotion *event, int focusable){
-  buttonstate *b = find_button((int)event->x,(int)event->y);
-
-  if(buttons_ready){
-    if(grabbed){
-      if(grabbed==b)
-	button_set_state(g,grabbed,1,1);
-      else
-	button_set_state(g,grabbed,0,0);
-      return 1;
-    }
-    
-    if(focusable && !grabbed){
-      
-      if(b){
-	button_set_state(g,b,1,0);
-	return 1;
-      }
-    }
-    
-    button_clear_state(g);
-  }
-  return 0;
-}
-
-/* handles mouse button press events; called out of gameboard's master
-   mouse handler.  returns nonzero if grabbing focus */
-int button_button_press(Gameboard *g, GdkEventButton *event, int focusable){
-
-  if(buttons_ready){
-    if(focusable){
-      
-      buttonstate *b = find_button((int)event->x,(int)event->y);
-      if(b){
-	button_set_state(g,b,1,1);
-      grabbed = b;
-      return 1;
-      }
-    }
-    
-    button_clear_state(g);
-  }
-  return 0;  
-}
-
-/* handles mouse button press events; called out of gameboard's master
-   mouse handler. returns nonzero if grabbing focus */
-int button_button_release(Gameboard *g, GdkEventButton *event, int focusable){
-  if(focusable && buttons_ready){
-    
-    buttonstate *b = find_button((int)event->x,(int)event->y);
-    if(b && grabbed==b){
-      button_set_state(g,b,1,0);
-      
-      if(b->callback)
-	b->callback(g);
-      
-      
-    }
-  }
-  
-  grabbed=0;
-  return 0;  
-}

Deleted: trunk/planarity/button_base.h
===================================================================
--- trunk/planarity/button_base.h	2005-09-24 22:02:43 UTC (rev 10059)
+++ trunk/planarity/button_base.h	2005-09-25 09:24:23 UTC (rev 10060)
@@ -1,73 +0,0 @@
-#define NUMBUTTONS 11
-
-typedef struct {
-  int position; // 0 inactive
-                // 1 left
-                // 2 center
-                // 3 right
-
-  int target_x;
-  int target_x_inactive;
-  int target_x_active;
-
-  int x;
-  int x_inactive;
-  int x_active;
-  int y;
-
-  cairo_surface_t *idle;
-  cairo_surface_t *lit;
-
-  char *rollovertext;
-  cairo_text_extents_t ex;
-
-  int alphad;
-  double alpha;
-
-  int rollover;
-  int press;
-
-  void (*callback)();
-} buttonstate;
-
-buttonstate states[NUMBUTTONS];
-
-
-extern void init_buttons(Gameboard *g);
-extern void rollover_extents(Gameboard *g, buttonstate *b);
-extern gboolean animate_button_frame(gpointer ptr);
-extern void expose_buttons(Gameboard *g,cairo_t *c,int x, int y, int w, int h);
-extern void resize_buttons(int oldw,int oldh,int w,int h);
-extern int button_motion_event(Gameboard *g, GdkEventMotion *event, int focusable);
-extern int button_button_press(Gameboard *g, GdkEventButton *event, int focusable);
-extern int button_button_release(Gameboard *g, GdkEventButton *event, int focusable);
-extern void button_clear_state(Gameboard *g);
-extern int buttons_ready;
-
-#define BUTTON_QUIT_IDLE_FILL   .7,.1,.1,.3
-#define BUTTON_QUIT_IDLE_PATH   .7,.1,.1,.6
-
-#define BUTTON_QUIT_LIT_FILL    .7,.1,.1,.5
-#define BUTTON_QUIT_LIT_PATH    .7,.1,.1,.6
-
-#define BUTTON_IDLE_FILL        .1,.1,.7,.3
-#define BUTTON_IDLE_PATH        .1,.1,.7,.6
-
-#define BUTTON_LIT_FILL         .1,.1,.7,.6
-#define BUTTON_LIT_PATH         .1,.1,.7,.6
-
-#define BUTTON_CHECK_IDLE_FILL  .1,.5,.1,.3
-#define BUTTON_CHECK_IDLE_PATH  .1,.5,.1,.6
-
-#define BUTTON_CHECK_LIT_FILL   .1,.5,.1,.6
-#define BUTTON_CHECK_LIT_PATH   .1,.5,.1,.6
-
-#define BUTTON_RADIUS                    14
-#define BUTTON_LINE_WIDTH                 1
-#define BUTTON_TEXT_BORDER               15
-#define BUTTON_TEXT_COLOR       .1,.1,.7,.8
-#define BUTTON_TEXT_SIZE            15.,18.
-
-#define BUTTON_ANIM_INTERVAL             15
-#define BUTTON_EXPOSE                    50
-#define BUTTON_DELTA                      3

Deleted: trunk/planarity/buttonbar.c
===================================================================
--- trunk/planarity/buttonbar.c	2005-09-24 22:02:43 UTC (rev 10059)
+++ trunk/planarity/buttonbar.c	2005-09-25 09:24:23 UTC (rev 10060)
@@ -1,221 +0,0 @@
-#include <math.h>
-#include <string.h>
-#include <gtk/gtk.h>
-#include <gdk/gdk.h>
-
-#include "graph.h"
-#include "gameboard.h"
-#include "gamestate.h"
-#include "button_base.h"
-#include "buttonbar.h"
-#include "pause.h"
-
-/************************ the lower button bar *********************/
-
-static int checkbutton_deployed=0;
-static int sweeper=0;
-static gint timer = 0;
-static void (*undeploy_callback)(Gameboard *g);
-
-/* perform a single frame of animation for all buttons/rollovers */
-static gboolean buttonbar_animate_buttons(gpointer ptr){
-  Gameboard *g=(Gameboard *)ptr;
-  int ret=0;
-
-  if(!g->first_expose)return 1;
-
-  ret=animate_button_frame(g);
-
-  if(!ret && timer!=0){
-    g_source_remove(timer);
-    timer=0;
-  }
-  return ret;
-}
-
-/* helper that wraps animate-buttons for removing the buttons at the
-   end of a level */
-static gboolean buttonbar_deanimate_buttons(gpointer ptr){
-  Gameboard *g=(Gameboard *)ptr;
-  int ret=0,i;
-  sweeper-=BUTTON_DELTA;
-  if(sweeper< -(BUTTON_RADIUS +1)){
-    sweeper= -(BUTTON_RADIUS +1);
-  }else
-    ret=1;
-
-  {
-    
-    int width=get_board_width();
-    int w2=width/2;
-    
-    for(i=0;i<NUMBUTTONS;i++){
-      buttonstate *b=states+i;
-      if(b->position){
-	if(b->target_x>w2 && width-sweeper>b->target_x)
-	  b->target_x=width-sweeper;
-	if(b->target_x<w2 && sweeper<b->target_x)
-	  b->target_x=sweeper;
-      }
-    }
-    ret|=animate_button_frame(ptr);
-  }
-  
-  if(!ret)
-    // undeploy finished... call the undeploy callback
-    undeploy_callback(g);
-
-  return ret;
-}
-
-/******************** toplevel abstraction calls *********************/
-
-/* initialize the rather weird little animation engine */
-void setup_buttonbar(Gameboard *g){
-  
-  int count=BUTTONBAR_BORDER;
-  int i;
-  int w=get_board_width();
-  int h=get_board_height();
-
-  states[0].rollovertext="exit gPlanarity";
-  states[1].rollovertext="back to menu";
-  states[2].rollovertext="reset board";
-  states[3].rollovertext="pause";
-  states[4].rollovertext="help / about";
-  states[5].rollovertext="expand";
-  states[6].rollovertext="shrink";
-  states[7].rollovertext="hide/show lines";
-  states[8].rollovertext="mark intersections";
-  states[9].rollovertext="click when finished!";
-
-  states[0].callback = quit;
-  states[2].callback = reset_board;
-  states[3].callback = pause_game;
-  states[4].callback = about_game;
-  states[5].callback = expand;
-  states[6].callback = shrink;
-  states[7].callback = hide_show_lines;
-  states[8].callback = mark_intersections;
-  states[9].callback = finish_board;
-
-  for(i=0;i<NUMBUTTONS;i++)
-    states[i].position=0;
-
-  states[0].position = 1; //left;
-  states[1].position = 1; //left;
-  states[2].position = 1; //left;
-  states[3].position = 1; //left;
-  states[4].position = 1; //left;
-  states[5].position = 3; //right
-  states[6].position = 3; //right
-  states[7].position = 3; //right
-  states[8].position = 3; //right
-  states[9].position = 3; //right
-
-  for(i=0;i<NUMBUTTONS;i++){
-    buttonstate *b=states+i;
-    if(b->position == 1){
-      b->x=0;
-      b->target_x=count;
-      b->x_active=b->target_x_active=count;
-      b->x_inactive=b->target_x_inactive=count-BUTTON_EXPOSE;
-      b->y = h - BUTTONBAR_Y_FROM_BOTTOM;
-      count += BUTTONBAR_SPACING;
-    }
-  }
-  count -= BUTTONBAR_SPACING;
-
-  // assumes we will always have at least as many buttons left as right
-  sweeper=count;
-  
-  count=w-BUTTONBAR_BORDER;
-  for(i=NUMBUTTONS-1;i>=0;i--){
-    buttonstate *b=states+i;
-    if(b->position == 3){
-      b->x=w;
-      b->target_x=count;
-      b->x_active=b->target_x_active=count;
-      b->x_inactive=b->target_x_inactive=count+BUTTON_EXPOSE;
-      b->y = h - BUTTONBAR_Y_FROM_BOTTOM;
-      if(i!=9 || checkbutton_deployed) // special-case the checkbutton
-	count -= BUTTONBAR_SPACING;
-    }
-  }
-  
-  // special-case the checkbutton
-  if(!checkbutton_deployed){
-    states[9].target_x_inactive=states[9].target_x_active+BUTTONBAR_SPACING;
-    states[9].x_inactive=states[9].target_x_inactive;
-    states[9].target_x=states[9].target_x_inactive;
-  }
-
-  for(i=0;i<NUMBUTTONS;i++)
-    if(states[i].position)
-      rollover_extents(g,states+i);
-  
-}
-
-/* effects animated 'rollout' of buttons when level begins */
-void deploy_buttonbar(Gameboard *g){
-  if(!buttons_ready ){
-    if(g->g->active_intersections <= get_objective())
-      checkbutton_deployed=1;
-    else
-      checkbutton_deployed=0;
-    setup_buttonbar(g);
-    timer = g_timeout_add(BUTTON_ANIM_INTERVAL, buttonbar_animate_buttons, (gpointer)g);
-    buttons_ready=1;
-  }
-
-}
-
-/* effects animated rollout of 'check' button */
-void deploy_check(Gameboard *g){
-  if(buttons_ready && !checkbutton_deployed){
-    int i;
-    for(i=5;i<9;i++){
-      states[i].target_x-=BUTTONBAR_SPACING;
-      states[i].target_x_active-=BUTTONBAR_SPACING;
-      states[i].target_x_inactive-=BUTTONBAR_SPACING;
-    }
-    states[9].target_x=states[9].target_x_active;
-    if(timer!=0)
-      g_source_remove(timer);
-    timer = g_timeout_add(BUTTON_ANIM_INTERVAL, buttonbar_animate_buttons, (gpointer)g);
-    checkbutton_deployed=1;
-  }
-}
-
-/* effects animated rollback of 'check' button */
-void undeploy_check(Gameboard *g){
-  if(checkbutton_deployed){
-    int i;
-    for(i=5;i<9;i++){
-      states[i].target_x+=BUTTONBAR_SPACING;
-      states[i].target_x_active+=BUTTONBAR_SPACING;
-      states[i].target_x_inactive+=BUTTONBAR_SPACING;
-    }
-    states[9].target_x=states[9].target_x_inactive;
-    if(timer!=0)
-      g_source_remove(timer);
-    timer = g_timeout_add(BUTTON_ANIM_INTERVAL, buttonbar_animate_buttons, (gpointer)g);
-    checkbutton_deployed=0;
-  }
-}
-
-/* effects animated rollback of buttons at end of level */
-void undeploy_buttonbar(Gameboard *g, void (*callback)(Gameboard *g)){
-  if(timer!=0)
-    g_source_remove(timer);
-  
-  button_clear_state(g);
-  buttons_ready=0;
-  undeploy_callback=callback;
-  checkbutton_deployed=0;
-
-  g_timeout_add(BUTTON_ANIM_INTERVAL, buttonbar_deanimate_buttons, (gpointer)g);
-
-
-}
-

Deleted: trunk/planarity/buttonbar.h
===================================================================
--- trunk/planarity/buttonbar.h	2005-09-24 22:02:43 UTC (rev 10059)
+++ trunk/planarity/buttonbar.h	2005-09-25 09:24:23 UTC (rev 10060)
@@ -1,21 +0,0 @@
-extern void setup_buttonbar(Gameboard *g);
-extern void deploy_buttonbar(Gameboard *g);
-extern void undeploy_buttonbar(Gameboard *g, void (*callback)());
-extern void deploy_check(Gameboard *g);
-extern void undeploy_check(Gameboard *g);
-
-#define BUTTONBAR_Y_FROM_BOTTOM 25
-#define BUTTONBAR_LINE_WIDTH 1
-#define BUTTONBAR_TEXT_BORDER 15
-#define BUTTONBAR_TEXT_COLOR       .1,.1,.7,.8
-#define BUTTONBAR_TEXT_SIZE  15.,18.
-
-#define BUTTONBAR_ANIM_INTERVAL 15
-#define BUTTONBAR_EXPOSE  50
-#define BUTTONBAR_DELTA 3
-
-#define BUTTONBAR_LEFT 5
-#define BUTTONBAR_RIGHT 5
-#define BUTTONBAR_BUTTONS 5
-#define BUTTONBAR_BORDER 35
-#define BUTTONBAR_SPACING 35

Deleted: trunk/planarity/buttons.c
===================================================================
--- trunk/planarity/buttons.c	2005-09-24 22:02:43 UTC (rev 10059)
+++ trunk/planarity/buttons.c	2005-09-25 09:24:23 UTC (rev 10060)
@@ -1,273 +0,0 @@
-#include <math.h>
-#include <gtk/gtk.h>
-
-#include "graph.h"
-#include "gameboard.h"
-#include "buttons.h"
-
-
-/************************* simple round icon drawing *********************/
-
-void path_button_help(cairo_t *c, double x, double y){
-  
-  cairo_save(c);
-  cairo_translate(c,x,y);
-  cairo_set_fill_rule(c,CAIRO_FILL_RULE_EVEN_ODD);
-  cairo_set_line_width(c,1);
-  cairo_arc(c,0,0,14,0,2*M_PI);
-
-  cairo_move_to(c,-9,-2);
-  cairo_curve_to(c,-8,-7, -4.5,-12, 0,-12);
-  cairo_curve_to(c, 7,-12,  9,-6, 9,-4);
-  cairo_curve_to(c, 9,0 ,6,2, 5,2);
-
-  cairo_curve_to(c, 4,2, 2.5,3, 2.5,6);
-  cairo_line_to(c,-2.5,6);
-
-  cairo_curve_to(c,-2.5,3, -3,1, 0,-1);
-  cairo_curve_to(c, 11,-4, -3,-12, -4,-2);
-  cairo_close_path(c);
-
-  cairo_move_to(c,-2.5,8);
-  cairo_line_to(c,2.5,8);
-  cairo_line_to(c,2.5,12);
-  cairo_line_to(c,-2.5,12);
-  cairo_close_path(c);
-  cairo_fill_preserve(c);
-
-  cairo_restore(c);
-
-}
-
-void path_button_pause(cairo_t *c, double x, double y){
-  
-  cairo_save(c);
-  cairo_translate(c,x,y);
-  cairo_set_fill_rule(c,CAIRO_FILL_RULE_EVEN_ODD);
-  cairo_set_line_width(c,1);
-  cairo_arc(c,0,0,14,0,2*M_PI);
-
-  cairo_rectangle(c,-7,-9,5,18);
-  cairo_rectangle(c,2,-9,5,18);
-  cairo_fill_preserve(c);
-  cairo_restore(c);
-
-}
-
-void path_button_exit(cairo_t *c, double x, double y){
-  
-  cairo_save(c);
-  cairo_translate(c,x,y);
-  cairo_set_fill_rule(c,CAIRO_FILL_RULE_EVEN_ODD);
-  cairo_set_line_width(c,1);
-  cairo_arc(c,0,0,14,0,2*M_PI);
-
-  cairo_move_to(c,-6,-9.5);
-  cairo_line_to(c,0,-3.5);
-  cairo_line_to(c,6,-9.5);
-  cairo_line_to(c,9.5,-6);
-  cairo_line_to(c,3.5,0);
-  cairo_line_to(c,9.5,6);
-  cairo_line_to(c,6,9.5);
-  cairo_line_to(c,0,3.5);
-  cairo_line_to(c,-6,9.5);
-  cairo_line_to(c,-9.5,6);
-  cairo_line_to(c,-3.5,0);
-  cairo_line_to(c,-9.5,-6);
-  cairo_close_path(c);
-
-  cairo_fill_preserve(c);
-  cairo_restore(c);
-
-}
-
-void path_button_back(cairo_t *c, double x, double y){
-  
-  cairo_save(c);
-  cairo_translate(c,x,y);
-  cairo_set_fill_rule(c,CAIRO_FILL_RULE_EVEN_ODD);
-  cairo_set_line_width(c,1);
-
-  cairo_arc(c,0,0,14,0,2*M_PI);
-
-  cairo_move_to(c,0,-11);
-  cairo_line_to(c,-9,0);
-  cairo_line_to(c,-3,0);
-  cairo_line_to(c,-3,10);
-  cairo_line_to(c,3,10);
-  cairo_line_to(c,3,0);
-  cairo_line_to(c,9,0);
-  cairo_close_path(c);
-
-  cairo_fill_preserve(c);
-  cairo_restore(c);
-
-}
-
-void path_button_reset(cairo_t *c, double x, double y){
-  
-  cairo_save(c);
-  cairo_translate(c,x,y);
-  cairo_set_fill_rule(c,CAIRO_FILL_RULE_EVEN_ODD);
-  cairo_set_line_width(c,1);
-
-  cairo_arc(c,0,0,14,0,2*M_PI);
-
-  cairo_move_to(c,-11,-5);
-  cairo_line_to(c,-12,1);
-  cairo_line_to(c,-6,1);
-  cairo_line_to(c,0, 11);
-  cairo_line_to(c,6,1);
-  cairo_line_to(c,12,1);
-  cairo_line_to(c,11,-5);
-  cairo_line_to(c,4,-5);
-  cairo_line_to(c,0,2);
-  cairo_line_to(c,-4,-5);
-  cairo_close_path(c);
-
-  cairo_fill_preserve(c);
-  cairo_restore(c);
-
-}
-
-void path_button_expand(cairo_t *c, double x, double y){
-  
-  cairo_save(c);
-  cairo_translate(c,x,y);
-  cairo_set_fill_rule(c,CAIRO_FILL_RULE_EVEN_ODD);
-  cairo_set_line_width(c,1);
-
-  cairo_arc(c,0,0,14,0,2*M_PI);
-
-  cairo_move_to(c,-8.5,-3);
-  cairo_line_to(c,-2.5,-3);
-  cairo_line_to(c,-3,-8.5);
-  cairo_line_to(c,3, -8.5);
-  cairo_line_to(c,3,-3);
-  cairo_line_to(c,8.5,-3);
-  cairo_line_to(c,8.5,3);
-  cairo_line_to(c,3,3);
-  cairo_line_to(c,3,8.5);
-  cairo_line_to(c,-3,8.5);
-  cairo_line_to(c,-3,3);
-  cairo_line_to(c,-8.5,3);
-  cairo_close_path(c);
-
-  cairo_fill_preserve(c);
-  cairo_restore(c);
-
-}
-
-void path_button_shrink(cairo_t *c, double x, double y){
-  
-  cairo_save(c);
-  cairo_translate(c,x,y);
-  cairo_set_fill_rule(c,CAIRO_FILL_RULE_EVEN_ODD);
-  cairo_set_line_width(c,1);
-
-  cairo_arc(c,0,0,14,0,2*M_PI);
-
-  cairo_move_to(c,-10,-3);
-  cairo_line_to(c,10,-3);
-  cairo_line_to(c,10,3);
-  cairo_line_to(c,-10,3);
-  cairo_close_path(c);
-
-  cairo_fill_preserve(c);
-  cairo_restore(c);
-
-}
-
-void path_button_lines(cairo_t *c, double x, double y){
-  
-  cairo_save(c);
-  cairo_translate(c,x,y);
-  cairo_set_fill_rule(c,CAIRO_FILL_RULE_EVEN_ODD);
-  cairo_set_line_width(c,1);
-
-  cairo_arc(c,0,0,14,0,2*M_PI);
-
-  cairo_move_to(c,6,-4);
-  cairo_arc(c,0,-4,6,0,2*M_PI);
-
-  cairo_move_to(c,0,2);
-  cairo_line_to(c,0,10);
-  cairo_close_path(c);
-
-  cairo_move_to(c,2.68328,5.36656-4);
-  cairo_rel_line_to(c,4,8);
-  cairo_close_path(c);
-
-  cairo_move_to(c,-2.68328,5.36656-4);
-  cairo_rel_line_to(c,-4,8);
-  cairo_close_path(c);
-
-  cairo_fill_preserve(c);
-  cairo_restore(c);
-
-}
-
-void path_button_int(cairo_t *c, double x, double y){
-  
-  cairo_save(c);
-  cairo_translate(c,x,y);
-  cairo_set_fill_rule(c,CAIRO_FILL_RULE_EVEN_ODD);
-  cairo_set_line_width(c,1);
-
-  cairo_arc(c,0,0,14,0,2*M_PI);
-
-  cairo_move_to(c,8,0);
-  cairo_line_to(c,0,8);
-  cairo_line_to(c,-8,0);
-  cairo_line_to(c,0,-8);
-
-  cairo_close_path(c);
- 
-  cairo_fill_preserve(c);
-  cairo_restore(c);
-
-}
-
-void path_button_check(cairo_t *c, double x, double y){
-  
-  cairo_save(c);
-  cairo_translate(c,x,y);
-  cairo_set_fill_rule(c,CAIRO_FILL_RULE_EVEN_ODD);
-  cairo_set_line_width(c,1);
-
-  cairo_arc(c,0,0,14,0,2*M_PI);
-
-  cairo_move_to(c,8,-8);
-  cairo_curve_to(c, 7,-7, 11,-7.3, 10,-6);
-  cairo_line_to(c,0,9);
-  cairo_curve_to(c, -1,9.1, -2,11, -3,10);
-  cairo_line_to(c,-11,4);
-  cairo_curve_to(c, -12,3, -10,.5, -9,1);
-  cairo_line_to(c,-3,3);
-
-  cairo_close_path(c);
- 
-  cairo_fill_preserve(c);
-  cairo_restore(c);
-
-}
-
-void path_button_play(cairo_t *c, double x, double y){
-  
-  cairo_save(c);
-  cairo_translate(c,x,y);
-  cairo_set_fill_rule(c,CAIRO_FILL_RULE_EVEN_ODD);
-  cairo_set_line_width(c,1);
-
-  cairo_arc(c,0,0,14,0,2*M_PI);
-
-  cairo_move_to(c,-8,-8);
-  cairo_line_to(c,10,0);
-  cairo_line_to(c,-8,8);
-  cairo_close_path(c);
- 
-  cairo_fill_preserve(c);
-  cairo_restore(c);
-
-}
-

Deleted: trunk/planarity/buttons.h
===================================================================
--- trunk/planarity/buttons.h	2005-09-24 22:02:43 UTC (rev 10059)
+++ trunk/planarity/buttons.h	2005-09-25 09:24:23 UTC (rev 10060)
@@ -1,45 +0,0 @@
-extern void path_button_help(cairo_t *c, double x, double y);
-extern void path_button_back(cairo_t *c, double x, double y);
-extern void path_button_reset(cairo_t *c, double x, double y);
-extern void path_button_pause(cairo_t *c, double x, double y);
-extern void path_button_exit(cairo_t *c, double x, double y);
-extern void path_button_expand(cairo_t *c, double x, double y);
-extern void path_button_shrink(cairo_t *c, double x, double y);
-extern void path_button_lines(cairo_t *c, double x, double y);
-extern void path_button_int(cairo_t *c, double x, double y);
-extern void path_button_check(cairo_t *c, double x, double y);
-extern void path_button_play(cairo_t *c, double x, double y);
- 
-
-#define BUTTON_QUIT_IDLE_FILL   .7,.1,.1,.3
-#define BUTTON_QUIT_IDLE_PATH   .7,.1,.1,.6
-
-#define BUTTON_QUIT_LIT_FILL    .7,.1,.1,.5
-#define BUTTON_QUIT_LIT_PATH    .7,.1,.1,.6
-
-#define BUTTON_IDLE_FILL        .1,.1,.7,.3
-#define BUTTON_IDLE_PATH        .1,.1,.7,.6
-
-#define BUTTON_LIT_FILL         .1,.1,.7,.6
-#define BUTTON_LIT_PATH         .1,.1,.7,.6
-
-#define BUTTON_CHECK_IDLE_FILL  .1,.5,.1,.3
-#define BUTTON_CHECK_IDLE_PATH  .1,.5,.1,.6
-
-#define BUTTON_CHECK_LIT_FILL   .1,.5,.1,.6
-#define BUTTON_CHECK_LIT_PATH   .1,.5,.1,.6
-
-#define BUTTON_RADIUS 14
-#define BUTTON_Y_FROM_BOTTOM 25
-#define BUTTON_LINE_WIDTH 1
-#define BUTTON_TEXT_BORDER 15
-#define BUTTON_TEXT_COLOR       .1,.1,.7,.8
-#define BUTTON_TEXT_SIZE  15.,18.
-
-#define BUTTON_ANIM_INTERVAL 15
-#define BUTTON_LEFT 5
-#define BUTTON_RIGHT 5
-#define BUTTON_BORDER 35
-#define BUTTON_SPACING 35
-#define BUTTON_EXPOSE  50
-#define BUTTON_DELTA 3

Added: trunk/planarity/dialog_finish.c
===================================================================
--- trunk/planarity/dialog_finish.c	2005-09-24 22:02:43 UTC (rev 10059)
+++ trunk/planarity/dialog_finish.c	2005-09-25 09:24:23 UTC (rev 10060)
@@ -0,0 +1,196 @@
+#include <math.h>
+#include <string.h>
+#include <gtk/gtk.h>
+#include <gdk/gdk.h>
+
+#include "graph.h"
+#include "timer.h"
+#include "gameboard.h"
+#include "gameboard_draw_button.h"
+#include "levelstate.h"
+#include "dialog_finish.h"
+#include "dialog_level.h"
+
+static void finish_post (Gameboard *g){
+  // back to buttonbar activity!
+  g->finish_dialog_active=0;
+  pop_curtain(g);
+  levelstate_next();
+  levelstate_go();
+  enter_game(g);
+} 
+
+static void finish_level (Gameboard *g){
+  // back to buttonbar activity!
+  g->finish_dialog_active=0;
+  pop_curtain(g);
+  level_dialog(g,1);
+} 
+
+static void finish_quit (Gameboard *g){
+  gtk_main_quit();
+} 
+
+static void local_go (Gameboard *g){
+  undeploy_buttons(g,finish_post);
+}
+
+static void local_level (Gameboard *g){
+  undeploy_buttons(g,finish_level);
+}
+
+static void local_quit (Gameboard *g){
+  undeploy_buttons(g,finish_quit);
+}
+
+/* initialize the rather weird little animation engine */
+static void setup_finish_buttons(Gameboard *g,int bw, int bh){
+  int i;
+  int w=g->g.width;
+  int h=g->g.height;
+  buttonstate *states=g->b.states;
+
+  states[0].rollovertext="exit gPlanarity";
+  states[1].rollovertext="level selection menu";
+  states[10].rollovertext="play next level!";
+
+  states[0].callback = local_quit;
+  states[1].callback = local_level;
+  states[10].callback = local_go;
+
+  for(i=0;i<NUMBUTTONS;i++)
+    states[i].position=0;
+
+  states[0].position = 2; //center;
+  states[1].position = 2; //center;
+  states[10].position = 2; //center;
+
+  {
+    buttonstate *b=states;
+    b->x = b->x_target = w/2 - bw/2 + FINISH_BUTTON_BORDER;
+    b->y_active = h/2 + bh/2 - FINISH_BUTTON_Y;
+    b->y = b->y_target = b->y_inactive = b->y_active + BUTTON_EXPOSE;
+    b->sweepdeploy = 0;
+  }
+
+  {
+    buttonstate *b=states+1;
+    b->x = b->x_target = w/2;
+    b->y_active = h/2 + bh/2 - FINISH_BUTTON_Y;
+    b->y = b->y_target = b->y_inactive = b->y_active + BUTTON_EXPOSE;
+    b->sweepdeploy = SWEEP_DELTA;
+  }
+
+  {
+    buttonstate *b=states+10;
+    b->x = b->x_target = w/2 + bw/2 - FINISH_BUTTON_BORDER;
+    b->y_active = h/2 + bh/2 - FINISH_BUTTON_Y;
+    b->y = b->y_target = b->y_inactive = b->y_active + BUTTON_EXPOSE;
+    b->sweepdeploy = SWEEP_DELTA*2;
+  }
+
+  for(i=0;i<NUMBUTTONS;i++)
+    if(states[i].position)
+      rollover_extents(g,states+i);  
+}
+
+static void draw_finishbox(Gameboard *g){
+  int w= g->g.width;
+  int h= g->g.height;
+
+  cairo_t *c = cairo_create(g->background);
+  borderbox_path(c,
+		 w/2 - FINISHBOX_WIDTH/2,
+		 h/2 - FINISHBOX_HEIGHT/2,
+		 FINISHBOX_WIDTH,
+		 FINISHBOX_HEIGHT);
+  cairo_set_source_rgb(c,1,1,1);
+  cairo_fill(c);
+
+  centerbox(c,
+	    w/2 - FINISHBOX_WIDTH/2,
+	    h/2 - FINISHBOX_HEIGHT/2,
+	    FINISHBOX_WIDTH,
+	    SCOREHEIGHT);
+
+  centerbox(c,
+	    w/2 - FINISHBOX_WIDTH/2 ,
+	    h/2 + FINISHBOX_HEIGHT/2 - SCOREHEIGHT,
+	    FINISHBOX_WIDTH,
+	    SCOREHEIGHT);
+
+  {
+    cairo_matrix_t ma;
+    char *time = get_timer_string();
+    char buffer[160];
+    int time_bonus=graphscore_get_bonus(&g->g);
+
+    int y;
+
+    cairo_select_font_face (c, "Arial",
+			    CAIRO_FONT_SLANT_NORMAL,
+			    CAIRO_FONT_WEIGHT_BOLD);
+
+    cairo_matrix_init_scale (&ma, 18.,18.);
+    cairo_set_font_matrix (c,&ma);
+    cairo_set_source_rgba (c, TEXT_COLOR);
+
+    y=h/2-FINISHBOX_HEIGHT/2+SCOREHEIGHT/2;
+    render_text_centered(c,"Level Complete!", w/2,y);y+=45;
+
+    cairo_select_font_face (c, "Arial",
+			    CAIRO_FONT_SLANT_NORMAL,
+			    CAIRO_FONT_WEIGHT_NORMAL);
+    cairo_matrix_init_scale (&ma, 16.,16.);
+    cairo_set_font_matrix (c,&ma);
+
+    snprintf(buffer,160,"Elapsed: %s",time);
+    render_bordertext_centered(c,buffer, w/2,y);y+=24;
+
+
+    snprintf(buffer,160,"Objective: %d points",graphscore_get_score(&g->g));
+    render_bordertext_centered(c,buffer, w/2,y);y+=24;
+
+    snprintf(buffer,160,"Time bonus: %d points",time_bonus);
+    render_bordertext_centered(c,buffer, w/2,y);y+=35;
+
+    cairo_select_font_face (c, "Arial",
+			    CAIRO_FONT_SLANT_NORMAL,
+			    CAIRO_FONT_WEIGHT_BOLD);
+
+    snprintf(buffer,160,"Final score: %d points",graphscore_get_score(&g->g)+time_bonus);
+    render_bordertext_centered(c,buffer, w/2,y);y+=24;
+
+    if(graphscore_get_score(&g->g)+time_bonus >= levelstate_get_hiscore()){
+      cairo_set_source_rgba (c, HIGH_COLOR);
+      render_bordertext_centered(c,"A high score!", w/2,y);y+=45;
+      cairo_set_source_rgba (c, TEXT_COLOR);
+    }else{
+      snprintf(buffer,160,"Previous best: %ld points",levelstate_get_hiscore());
+      render_bordertext_centered(c,buffer, w/2,y);y+=45;
+    }
+
+
+    render_bordertext_centered(c,"Total score to date:", w/2,y);
+    y+=21;
+    snprintf(buffer,160,"%ld points",levelstate_total_hiscore());
+    render_bordertext_centered(c,buffer, w/2,y);
+
+  }
+
+  cairo_destroy(c);
+}
+
+void finish_level_dialog(Gameboard *g){
+  g->finish_dialog_active=1;
+
+  // set up new buttons
+  setup_finish_buttons(g,FINISHBOX_WIDTH, FINISHBOX_HEIGHT);
+
+  // draw pausebox
+  push_curtain(g,draw_finishbox);
+
+  // deploy new buttons
+  deploy_buttons(g,0);
+}
+

Added: trunk/planarity/dialog_finish.h
===================================================================
--- trunk/planarity/dialog_finish.h	2005-09-24 22:02:43 UTC (rev 10059)
+++ trunk/planarity/dialog_finish.h	2005-09-25 09:24:23 UTC (rev 10060)
@@ -0,0 +1,6 @@
+#define FINISH_BUTTON_BORDER 35
+#define FINISH_BUTTON_Y 25
+#define FINISHBOX_WIDTH 220
+#define FINISHBOX_HEIGHT 330
+
+extern void finish_level_dialog(Gameboard *g);

Added: trunk/planarity/dialog_level.c
===================================================================
--- trunk/planarity/dialog_level.c	2005-09-24 22:02:43 UTC (rev 10059)
+++ trunk/planarity/dialog_level.c	2005-09-25 09:24:23 UTC (rev 10060)
@@ -0,0 +1,145 @@
+#include <math.h>
+#include <string.h>
+#include <gtk/gtk.h>
+#include <gdk/gdk.h>
+
+#include "graph.h"
+#include "timer.h"
+#include "gameboard.h"
+#include "gameboard_draw_button.h"
+#include "dialog_level.h"
+#include "levelstate.h"
+#include "main.h"
+
+static void unlevel_post (Gameboard *g){
+  g->level_dialog_active=0;
+  pop_curtain(g);
+  levelstate_go();
+  enter_game(g);
+} 
+
+static void unlevel_quit (Gameboard *g){
+  gtk_main_quit();
+} 
+
+static void local_go (Gameboard *g){
+  undeploy_buttons(g,unlevel_post);
+}
+
+static void local_quit (Gameboard *g){
+  undeploy_buttons(g,unlevel_quit);
+}
+
+/* initialize the rather weird little animation engine */
+static void setup_level_buttons(Gameboard *g,int bw, int bh){
+  int i;
+  int w=g->g.width;
+  int h=g->g.height;
+  buttonstate *states=g->b.states;
+
+  states[0].rollovertext="exit gPlanarity";
+  states[2].rollovertext="reset level";
+  states[10].rollovertext="play level!";
+
+  states[0].callback = local_quit;
+  states[2].callback = local_reset;
+  states[10].callback = local_go;
+
+  for(i=0;i<NUMBUTTONS;i++)
+    states[i].position=0;
+
+  states[0].position = 2; //center
+  states[2].position = 0; //undeployed by default (redundant)
+  states[10].position = 2; //center
+
+  {
+    buttonstate *b=states;
+    b->x = b->x_target = w/2 - bw/2 + LEVEL_BUTTON_BORDER;
+    b->y_active = h/2 + bh/2 - LEVEL_BUTTON_Y;
+    b->y = b->y_target = b->y_inactive = b->y_active + BUTTON_EXPOSE;
+    b->sweepdeploy = 0;
+  }
+
+  {
+    buttonstate *b=states+2;
+    b->x = b->x_target = w/2;
+    b->y_active = h/2 + bh/2 - LEVEL_BUTTON_Y;
+    b->y = b->y_target = b->y_inactive = b->y_active + BUTTON_EXPOSE;
+    b->sweepdeploy = SWEEP_DELTA;
+  }
+
+  {
+    buttonstate *b=states+10;
+    b->x = b->x_target = w/2 + bw/2 - LEVEL_BUTTON_BORDER;
+    b->y_active = h/2 + bh/2 - LEVEL_BUTTON_Y;
+    b->y = b->y_target = b->y_inactive = b->y_active + BUTTON_EXPOSE;
+    b->sweepdeploy = SWEEP_DELTA;
+  }
+
+  rollover_extents(g,states);  
+  rollover_extents(g,states+2);  
+  rollover_extents(g,states+10);  
+}
+
+static void draw_levelbox(Gameboard *g){
+  int w= g->g.width;
+  int h= g->g.height;
+
+  cairo_t *c = cairo_create(g->background);
+  borderbox_path(c,
+		 w/2 - LEVELBOX_WIDTH/2,
+		 h/2 - LEVELBOX_HEIGHT/2,
+		 LEVELBOX_WIDTH,
+		 LEVELBOX_HEIGHT);
+  cairo_set_source_rgb(c,1,1,1);
+  cairo_fill(c);
+
+  centerbox(c,
+	    w/2 - LEVELBOX_WIDTH/2,
+	    h/2 - LEVELBOX_HEIGHT/2,
+	    LEVELBOX_WIDTH,
+	    SCOREHEIGHT);
+
+  centerbox(c,
+	    w/2 - LEVELBOX_WIDTH/2,
+	    h/2 + LEVELBOX_HEIGHT/2 - SCOREHEIGHT,
+	    LEVELBOX_WIDTH,
+	    SCOREHEIGHT);
+
+  {
+    cairo_matrix_t ma;
+
+    cairo_select_font_face (c, "Arial",
+			    CAIRO_FONT_SLANT_NORMAL,
+			    CAIRO_FONT_WEIGHT_BOLD);
+    
+    cairo_matrix_init_scale (&ma, 18.,18.);
+    cairo_set_font_matrix (c,&ma);
+    cairo_set_source_rgba (c, TEXT_COLOR);
+
+    render_text_centered(c,"Available Levels", w/2,h/2-LEVELBOX_HEIGHT/2+SCOREHEIGHT/2);
+
+  }
+
+  cairo_destroy(c);
+}
+
+void level_dialog(Gameboard *g, int advance){
+
+  g->level_dialog_active=1;
+  levelstate_write();
+
+  if(advance)
+    levelstate_next();
+
+  // set up new buttons
+  setup_level_buttons(g,LEVELBOX_WIDTH, LEVELBOX_HEIGHT);
+  level_icons_init(g);
+
+  // draw pausebox
+  push_curtain(g,draw_levelbox);
+
+  // deploy new buttons
+  deploy_buttons(g,0);
+}
+

Added: trunk/planarity/dialog_level.h
===================================================================
--- trunk/planarity/dialog_level.h	2005-09-24 22:02:43 UTC (rev 10059)
+++ trunk/planarity/dialog_level.h	2005-09-25 09:24:23 UTC (rev 10060)
@@ -0,0 +1,14 @@
+#define LEVEL_BUTTON_BORDER 35
+#define LEVEL_BUTTON_Y 25
+#define LEVELBOX_WIDTH 556
+#define LEVELBOX_HEIGHT 370
+
+#define ICON_DELTA 20
+
+extern void level_dialog(Gameboard *g,int advance);
+extern void render_level_icons(Gameboard *g, cairo_t *c, int ex,int ey, int ew, int eh);
+extern void level_icons_init(Gameboard *g);
+
+extern void level_mouse_motion(Gameboard *g, int x, int y);
+extern void level_mouse_press(Gameboard *g, int x, int y);
+extern void local_reset (Gameboard *g);

Added: trunk/planarity/dialog_level_icons.c
===================================================================
--- trunk/planarity/dialog_level_icons.c	2005-09-24 22:02:43 UTC (rev 10059)
+++ trunk/planarity/dialog_level_icons.c	2005-09-25 09:24:23 UTC (rev 10060)
@@ -0,0 +1,406 @@
+#include <math.h>
+#include <string.h>
+#include <gtk/gtk.h>
+#include <gdk/gdk.h>
+
+#include "graph.h"
+#include "timer.h"
+#include "gameboard.h"
+#include "gameboard_draw_button.h"
+#include "dialog_level.h"
+#include "levelstate.h"
+#include "main.h"
+
+typedef struct {
+  int num;
+  double alpha;
+  cairo_surface_t *icon;
+
+  int x;
+  int y;
+  int w;
+  int h;
+
+} onelevel;
+
+static onelevel level_icons[5];
+static int center_x;
+static int level_lit;
+static int reset_deployed;
+
+static void draw_forward_arrow(onelevel *l, cairo_t *c,int fill){
+  int w = l->w;
+  int h = l->h;
+  int cx = w/2;
+  int cy = h/2;
+  cairo_save(c);
+  cairo_translate(c,l->x+.5,l->y+.5);
+
+  cairo_set_line_width(c,B_LINE);
+
+  cairo_move_to(c,w-10,cy);
+  cairo_line_to(c,cx,h-10);
+  cairo_line_to(c,cx,(int)h*.75);
+  cairo_line_to(c,10,(int)h*.75);
+  cairo_line_to(c,10,(int)h*.25);
+  cairo_line_to(c,cx,(int)h*.25);
+  cairo_line_to(c,cx,10);
+  cairo_close_path(c);
+
+  if(fill){
+    cairo_set_source_rgba (c, B_COLOR);
+    cairo_fill_preserve (c);
+  }
+  cairo_set_source_rgba (c, B_LINE_COLOR);
+  cairo_stroke (c);
+  cairo_restore(c);
+}
+
+static void draw_backward_arrow(onelevel *l, cairo_t *c,int fill){
+  int w = l->w;
+  int h = l->h;
+  int cx = w/2;
+  int cy = h/2;
+  cairo_save(c);
+  cairo_translate(c,l->x-.5,l->y+.5);
+
+  cairo_set_line_width(c,B_LINE);
+
+  cairo_move_to(c,10,cy);
+  cairo_line_to(c,cx,h-10);
+  cairo_line_to(c,cx,(int)h*.75);
+  cairo_line_to(c,w-10,(int)h*.75);
+  cairo_line_to(c,w-10,(int)h*.25);
+  cairo_line_to(c,cx,(int)h*.25);
+  cairo_line_to(c,cx,10);
+  cairo_close_path(c);
+
+  if(fill){
+    cairo_set_source_rgba (c, B_COLOR);
+    cairo_fill_preserve (c);
+  }
+  cairo_set_source_rgba (c, B_LINE_COLOR);
+  cairo_stroke (c);
+  cairo_restore(c);
+}
+
+static void invalidate_icon(Gameboard *g,onelevel *l){
+  GdkRectangle r;
+  
+  r.x=l->x+center_x;
+  r.y=l->y;
+  r.width=l->w;
+  r.height=l->h;
+
+  gdk_window_invalidate_rect (GTK_WIDGET(g)->window, &r, FALSE);
+}
+
+static void onelevel_init(Gameboard *g, int num, onelevel *l){
+  int current = get_level_num();
+  l->num = num+current;
+
+  if(l->icon)cairo_surface_destroy(l->icon);
+  l->icon = levelstate_get_icon(num + current);
+  l->alpha = 0.;
+
+  l->w  = ICON_WIDTH;
+  l->h  = ICON_HEIGHT;  
+  l->x  =  (g->g.width/2) - (l->w*.5) + num*ICON_WIDTH*1.2;
+  l->y  = (g->g.height - LEVELBOX_HEIGHT) / 2 + 120;
+  
+}
+
+static void deploy_reset_button(Gameboard *g){
+  buttonstate *states=g->b.states;
+
+  if(!reset_deployed){
+    states[10].sweepdeploy += SWEEP_DELTA;
+
+    states[2].position = 2; //activate it
+    states[2].y_target = states[2].y_active;
+   
+    reset_deployed=1;
+  }
+  
+  // even if the button is already 'deployed', the animation may
+  // have been interrupted.  Retrigger.
+
+  if(g->b.buttons_ready){
+    if(g->gtk_timer!=0)
+      g_source_remove(g->gtk_timer);
+    g->gtk_timer = g_timeout_add(BUTTON_ANIM_INTERVAL, animate_button_frame, (gpointer)g);
+  }
+  
+}
+
+static void undeploy_reset_button(Gameboard *g){
+  buttonstate *states=g->b.states;
+
+  if(reset_deployed){
+
+    states[10].sweepdeploy -= SWEEP_DELTA;
+    states[2].y_target = states[2].y_inactive;
+    
+    reset_deployed=0;
+  }
+
+  // even if the button is already 'undeployed', the animation may
+  // have been interrupted.  Retrigger.
+  if(g->b.buttons_ready){
+    if(g->gtk_timer!=0)
+      g_source_remove(g->gtk_timer);
+    g->gtk_timer = g_timeout_add(BUTTON_ANIM_INTERVAL, animate_button_frame, (gpointer)g);
+  } 
+}
+
+static void alpha_update(onelevel *l){
+  int distance = labs(l->x - level_icons[2].x + center_x);
+  double alpha = 1. - (distance/300.);
+  if(alpha<0.)alpha=0.;
+  //if(alpha>1.)alpha=1.;
+  l->alpha = alpha;
+}
+
+void level_icons_init(Gameboard *g){
+  int i;
+  
+  for(i=0;i<5;i++)
+    onelevel_init(g,i-2,level_icons+i);
+
+  center_x = 0;
+  level_lit = 2;
+  reset_deployed = 0;
+
+  if(levelstate_in_progress())
+    deploy_reset_button(g);
+
+}
+
+void render_level_icons(Gameboard *g, cairo_t *c, int ex,int ey, int ew, int eh){
+  if(g->level_dialog_active){
+    int w= g->g.width;
+    int h= g->g.height;
+    int y = h/2-LEVELBOX_HEIGHT/2+SCOREHEIGHT/2;
+    int i;
+
+    int ex2 = ex+ew;
+    int ey2 = ey+eh;
+    
+    for(i=0;i<5;i++){
+      onelevel *l=level_icons+i;
+      alpha_update(l);
+      if(l->num >= 0 && c){
+	
+	int iw = l->w;
+	int ih = l->h;
+	int ix = l->x+center_x;	
+	int iy = l->y;
+	
+	if( l->alpha == 0.) continue;
+
+	if( ix+iw < ex ) continue;
+	if( ix > ex2 ) continue;
+	if( iy+ih < ey ) continue;
+	if( iy > ey2 ) continue;
+
+	if(l->icon){
+	  cairo_set_source_surface(c,l->icon,ix,iy);
+	  cairo_paint_with_alpha(c,l->alpha);
+	}
+
+	if(center_x==0){
+	  if(i==2){
+	    cairo_set_source_rgba (c, B_COLOR);
+	    borderbox_path(c,ix+1.5,iy+1.5,iw-3,ih-3);
+	    cairo_fill (c);
+	  }
+
+	  if(i==1)
+	    draw_backward_arrow(l,c,i==level_lit);
+	  if(i==3)
+	    draw_forward_arrow(l,c,i==level_lit);
+	}
+      }
+    }
+
+    // render level related text
+    if(center_x == 0 && c){
+      char buffer[160];
+      cairo_matrix_t ma;
+
+      // above text
+      snprintf(buffer,160,"Level %d:",get_level_num()+1);
+      cairo_select_font_face (c, "Arial",
+			      CAIRO_FONT_SLANT_NORMAL,
+			      CAIRO_FONT_WEIGHT_BOLD);
+      cairo_matrix_init_scale (&ma, 20.,20.);
+      cairo_set_font_matrix (c,&ma);
+      cairo_set_source_rgba (c, TEXT_COLOR);
+      render_bordertext_centered(c, buffer,w/2,y+45);
+
+      cairo_select_font_face (c, "Arial",
+			      CAIRO_FONT_SLANT_NORMAL,
+			      CAIRO_FONT_WEIGHT_NORMAL);
+      cairo_matrix_init_scale (&ma, 18.,18.);
+      cairo_set_font_matrix (c,&ma);
+      cairo_set_source_rgba (c, TEXT_COLOR);
+      render_bordertext_centered(c, get_level_desc(),w/2,y+70);
+
+      if(levelstate_get_hiscore()==0){
+      cairo_select_font_face (c, "Arial",
+			      CAIRO_FONT_SLANT_ITALIC,
+			      CAIRO_FONT_WEIGHT_NORMAL);
+	snprintf(buffer,160,"[not yet completed]");
+      }else{
+      cairo_select_font_face (c, "Arial",
+			      CAIRO_FONT_SLANT_NORMAL,
+			      CAIRO_FONT_WEIGHT_NORMAL);
+	snprintf(buffer,160,"level high score: %ld",levelstate_get_hiscore());
+      }
+
+      cairo_matrix_init_scale (&ma, 18.,18.);
+      cairo_set_font_matrix (c,&ma);
+      cairo_set_source_rgba (c, TEXT_COLOR);
+      render_bordertext_centered(c, buffer,w/2,y+245);
+
+      snprintf(buffer,160,"total score all levels: %ld",levelstate_total_hiscore());
+      
+      cairo_select_font_face (c, "Arial",
+			      CAIRO_FONT_SLANT_NORMAL,
+			      CAIRO_FONT_WEIGHT_NORMAL);
+      cairo_matrix_init_scale (&ma, 18.,18.);
+      cairo_set_font_matrix (c,&ma);
+      cairo_set_source_rgba (c, TEXT_COLOR);
+      render_bordertext_centered(c, buffer,w/2,y+265);
+
+
+
+
+    }
+  }
+}
+
+static gboolean animate_level_frame(gpointer ptr){
+  Gameboard *g = GAMEBOARD (ptr);
+  int i;
+
+  if(center_x == 0){
+    g_source_remove(g->gtk_timer);
+    g->gtk_timer=0;
+
+    if(levelstate_in_progress())
+      deploy_reset_button(g);
+    else
+      undeploy_reset_button(g);
+    
+    expose_full(g);
+    return 0;
+  }
+
+  for(i=0;i<5;i++)
+    if(level_icons[i].alpha)
+      invalidate_icon(g, level_icons+i);
+
+  if(center_x < 0){
+    center_x += ICON_DELTA;
+    if(center_x > 0) center_x = 0;
+  }else{
+    center_x -= ICON_DELTA;
+    if(center_x < 0) center_x = 0;
+  }
+
+  // trick 'expose'; run it with a region that does nothing in order
+  // to update the visibility flags
+  
+  render_level_icons(g, 0, 0, 0, 0, 0);
+
+  for(i=0;i<5;i++)
+    if(level_icons[i].alpha)
+      invalidate_icon(g, level_icons+i);
+
+  return 1;
+}
+
+static int find_icon(int x, int y){
+  int i;
+  
+  for(i=1;i<4;i++){
+    onelevel *l=level_icons+i;
+    if(l->num>=0){
+      if(x >= l->x && 
+	 x <= l->x + l->w &&
+	 y >= l->y &&
+	 y <= l->y + l->h)
+	return i;
+    }
+  }
+  return 2;
+}
+
+void local_reset (Gameboard *g){
+  levelstate_reset();
+  onelevel_init(g,0,level_icons+2);
+  invalidate_icon(g, level_icons+2);
+  undeploy_reset_button(g);
+}
+
+void level_mouse_motion(Gameboard *g, int x, int y){
+
+  int icon = find_icon(x,y);
+
+  if(icon != level_lit){
+    invalidate_icon(g, level_icons+level_lit);
+    level_lit = icon;
+    invalidate_icon(g, level_icons+level_lit);
+  }
+}
+
+void level_mouse_press(Gameboard *g, int x, int y){
+  int i;
+
+  level_mouse_motion(g, x, y);
+
+  if(level_lit == 1){
+    if(levelstate_prev()){
+      if(level_icons[4].icon)cairo_surface_destroy(level_icons[4].icon);
+      for(i=4;i>=1;i--){
+	level_icons[i].num = level_icons[i-1].num;
+	level_icons[i].icon = level_icons[i-1].icon;
+      }
+      level_icons[0].icon=0;
+      onelevel_init(g,-2,level_icons);
+      
+      if(center_x==0)expose_full(g); // only needed to 'undraw' the text
+
+      center_x = level_icons[1].x - level_icons[2].x;
+    }
+  }
+  
+  if(level_lit == 3){
+    if(levelstate_next()){
+      
+      if(level_icons[0].icon)cairo_surface_destroy(level_icons[0].icon);
+      for(i=0;i<4;i++){
+	level_icons[i].num = level_icons[i+1].num;
+	level_icons[i].icon = level_icons[i+1].icon;
+      }
+      level_icons[4].icon=0;
+      onelevel_init(g,2,level_icons+4);
+      
+      if(center_x==0)expose_full(g); // only needed to 'undraw' the text
+
+      center_x = level_icons[3].x - level_icons[2].x;
+
+    }
+  }
+
+  if(center_x){
+    if(g->gtk_timer)
+      g_source_remove(g->gtk_timer);
+    g->gtk_timer = g_timeout_add(BUTTON_ANIM_INTERVAL, animate_level_frame, (gpointer)g);
+  }
+
+}
+
+  
+  

Added: trunk/planarity/dialog_pause.c
===================================================================
--- trunk/planarity/dialog_pause.c	2005-09-24 22:02:43 UTC (rev 10059)
+++ trunk/planarity/dialog_pause.c	2005-09-25 09:24:23 UTC (rev 10060)
@@ -0,0 +1,232 @@
+#include <math.h>
+#include <string.h>
+#include <gtk/gtk.h>
+#include <gdk/gdk.h>
+
+#include "graph.h"
+#include "timer.h"
+#include "gameboard.h"
+#include "gameboard_draw_button.h"
+#include "dialog_pause.h"
+
+extern char *version;
+
+static void unpause_post (Gameboard *g){
+  // back to buttonbar activity!
+  pop_curtain(g);
+  deploy_buttonbar(g);
+  unpause_timer();
+  g->about_dialog_active=0;
+  g->pause_dialog_active=0;
+} 
+
+static void unpause_quit (Gameboard *g){
+  gtk_main_quit();
+} 
+
+static void local_unpause (Gameboard *g){
+  undeploy_buttons(g,unpause_post);
+}
+
+static void local_quit (Gameboard *g){
+  undeploy_buttons(g,unpause_quit);
+}
+
+/* initialize the rather weird little animation engine */
+static void setup_pause_buttons(Gameboard *g,int bw, int bh){
+  int i;
+  int w=g->g.width;
+  int h=g->g.height;
+  buttonstate *states=g->b.states;
+
+  states[0].rollovertext="exit gPlanarity";
+  states[10].rollovertext="resume game!";
+
+  states[0].callback = local_quit;
+  states[10].callback = local_unpause;
+
+  for(i=0;i<NUMBUTTONS;i++)
+    states[i].position=0;
+
+  states[0].position = 2; //center;
+  states[10].position = 2; //center;
+
+  {
+    buttonstate *b=states;
+    b->x = b->x_target = w/2 - bw/2 + PAUSE_BUTTON_BORDER;
+    b->y_active = h/2 + bh/2 - PAUSE_BUTTON_Y;
+    b->y = b->y_target = b->y_inactive = b->y_active + BUTTON_EXPOSE;
+    b->sweepdeploy = 0;
+  }
+
+  {
+    buttonstate *b=states+10;
+    b->x = b->x_target = w/2 + bw/2 - PAUSE_BUTTON_BORDER;
+    b->y_active = h/2 + bh/2 - PAUSE_BUTTON_Y;
+    b->y = b->y_target = b->y_inactive = b->y_active + BUTTON_EXPOSE;
+    b->sweepdeploy = SWEEP_DELTA;
+  }
+
+  for(i=0;i<NUMBUTTONS;i++)
+    if(states[i].position)
+      rollover_extents(g,states+i);  
+}
+
+static void draw_pausebox(Gameboard *g){
+  int w= g->g.width;
+  int h= g->g.height;
+
+  cairo_t *c = cairo_create(g->background);
+  borderbox_path(c,
+		 w/2 - PAUSEBOX_WIDTH/2,
+		 h/2 - PAUSEBOX_HEIGHT/2,
+		 PAUSEBOX_WIDTH,
+		 PAUSEBOX_HEIGHT);
+  cairo_set_source_rgb(c,1,1,1);
+  cairo_fill(c);
+
+  centerbox(c,
+	    w/2 - PAUSEBOX_WIDTH/2,
+	    h/2 - PAUSEBOX_HEIGHT/2,
+	    PAUSEBOX_WIDTH,
+	    SCOREHEIGHT);
+
+  centerbox(c,
+	    w/2 - PAUSEBOX_WIDTH/2 ,
+	    h/2 + PAUSEBOX_HEIGHT/2 - SCOREHEIGHT,
+	    PAUSEBOX_WIDTH,
+	    SCOREHEIGHT);
+
+  {
+    cairo_matrix_t ma;
+    char *time = get_timer_string();
+
+    cairo_select_font_face (c, "Arial",
+			    CAIRO_FONT_SLANT_NORMAL,
+			    CAIRO_FONT_WEIGHT_BOLD);
+    
+    cairo_matrix_init_scale (&ma, 18.,18.);
+    cairo_set_font_matrix (c,&ma);
+    cairo_set_source_rgba (c, TEXT_COLOR);
+
+    render_text_centered(c,"Game Paused", w/2,h/2-PAUSEBOX_HEIGHT/2+SCOREHEIGHT/2);
+    cairo_select_font_face (c, "Arial",
+			    CAIRO_FONT_SLANT_NORMAL,
+			    CAIRO_FONT_WEIGHT_NORMAL);
+    render_bordertext_centered(c,"Time Elapsed:", w/2,h/2-30);
+    render_bordertext_centered(c,time, w/2,h/2);
+  }
+
+  cairo_destroy(c);
+}
+
+void pause_dialog(Gameboard *g){
+  g->pause_dialog_active=1;
+  // set up new buttons
+  setup_pause_buttons(g,PAUSEBOX_WIDTH, PAUSEBOX_HEIGHT);
+
+  // draw pausebox
+  push_curtain(g,draw_pausebox);
+
+  // deploy new buttons
+  deploy_buttons(g,0);
+}
+
+// the 'about' box is nearly identical, including the fact it pauses the game.
+// we just piggyback it here
+
+static void draw_aboutbox(Gameboard *g){
+  int w= g->g.width;
+  int h= g->g.height;
+
+  cairo_t *c = cairo_create(g->background);
+  borderbox_path(c,
+		 w/2 - ABOUTBOX_WIDTH/2,
+		 h/2 - ABOUTBOX_HEIGHT/2,
+		 ABOUTBOX_WIDTH,
+		 ABOUTBOX_HEIGHT);
+  cairo_set_source_rgb(c,1,1,1);
+  cairo_fill(c);
+
+  centerbox(c,
+	    w/2 - ABOUTBOX_WIDTH/2,
+	    h/2 - ABOUTBOX_HEIGHT/2,
+	    ABOUTBOX_WIDTH,
+	    SCOREHEIGHT);
+
+  centerbox(c,
+	    w/2 - ABOUTBOX_WIDTH/2 ,
+	    h/2 + ABOUTBOX_HEIGHT/2 - SCOREHEIGHT,
+	    ABOUTBOX_WIDTH,
+	    SCOREHEIGHT);
+
+  {
+    cairo_matrix_t ma;
+    int y = h/2-ABOUTBOX_HEIGHT/2+SCOREHEIGHT/2;
+    cairo_select_font_face (c, "Sans",
+			    CAIRO_FONT_SLANT_NORMAL,
+			    CAIRO_FONT_WEIGHT_BOLD);
+
+    cairo_matrix_init_scale (&ma, 18.,18.);
+    cairo_set_font_matrix (c,&ma);
+    cairo_set_source_rgba (c, TEXT_COLOR);
+
+    render_text_centered(c,"gPlanarity", w/2,y);
+    cairo_select_font_face (c, "Sans",
+			    CAIRO_FONT_SLANT_NORMAL,
+			    CAIRO_FONT_WEIGHT_NORMAL);
+    y+=45;
+    render_bordertext_centered(c,"Untangle the mess!", w/2,y);
+    y+=30;
+
+    cairo_matrix_init_scale (&ma, 13.,13.);
+    cairo_set_font_matrix (c,&ma);
+    render_bordertext_centered(c,"Drag verticies to eliminate crossed lines.", w/2,y); y+=16;
+    render_bordertext_centered(c,"The objective may be a complete solution or", w/2,y); y+=16;
+    render_bordertext_centered(c,"getting as close as possible to solving an", w/2,y); y+=16;
+    render_bordertext_centered(c,"unsolvable puzzle.  Work quickly and", w/2,y); y+=16;
+    render_bordertext_centered(c,"exceed the objective for bonus points!", w/2,y); y+=16;
+
+    y+=16;
+    cairo_move_to (c, w/2-100,y);
+    cairo_line_to (c, w/2+100,y);
+    cairo_stroke(c);
+    y+=32;
+
+    cairo_matrix_init_scale (&ma, 12.,13.);
+    cairo_set_font_matrix (c,&ma);
+    render_bordertext_centered(c,"gPlanarity written by Monty <monty at xiph.org>",w/2,y);y+=17;
+    render_bordertext_centered(c,"as a demonstration of Gtk+/Cairo",w/2,y);y+=32;
+
+    render_bordertext_centered(c,"Original Flash version of Planarity by",w/2,y);y+=17;
+    render_bordertext_centered(c,"John Tantalo <john.tantalo at case.edu>",w/2,y);y+=32;
+
+    render_bordertext_centered(c,"Original game concept by Mary Radcliffe",w/2,y);y+=17;
+
+
+    y = h/2+ABOUTBOX_HEIGHT/2-SCOREHEIGHT/2;
+    cairo_select_font_face (c, "Sans",
+			    CAIRO_FONT_SLANT_NORMAL,
+			    CAIRO_FONT_WEIGHT_BOLD);
+
+    cairo_matrix_init_scale (&ma, 10.,11.);
+    cairo_set_font_matrix (c,&ma);
+    render_text_centered(c,version, w/2,y);
+
+  }
+
+  cairo_destroy(c);
+}
+
+void about_dialog(Gameboard *g){
+  g->about_dialog_active=1;
+
+  // set up new buttons
+  setup_pause_buttons(g,ABOUTBOX_WIDTH,ABOUTBOX_HEIGHT);
+
+  // draw about box
+  push_curtain(g,draw_aboutbox);
+
+  // deploy new buttons
+  deploy_buttons(g,0);
+}

Added: trunk/planarity/dialog_pause.h
===================================================================
--- trunk/planarity/dialog_pause.h	2005-09-24 22:02:43 UTC (rev 10059)
+++ trunk/planarity/dialog_pause.h	2005-09-25 09:24:23 UTC (rev 10060)
@@ -0,0 +1,10 @@
+#define PAUSE_BUTTON_BORDER 35
+#define PAUSE_BUTTON_Y 25
+#define PAUSEBOX_WIDTH 180
+#define PAUSEBOX_HEIGHT 250
+
+#define ABOUTBOX_WIDTH 320
+#define ABOUTBOX_HEIGHT 400
+
+extern void pause_dialog(Gameboard *g);
+extern void about_dialog(Gameboard *g);

Deleted: trunk/planarity/finish.c
===================================================================
--- trunk/planarity/finish.c	2005-09-24 22:02:43 UTC (rev 10059)
+++ trunk/planarity/finish.c	2005-09-25 09:24:23 UTC (rev 10060)
@@ -1,250 +0,0 @@
-#include <math.h>
-#include <string.h>
-#include <gtk/gtk.h>
-#include <gdk/gdk.h>
-
-#include "graph.h"
-#include "gameboard.h"
-#include "gamestate.h"
-#include "button_base.h"
-#include "buttonbar.h"
-#include "finish.h"
-#include "box.h"
-
-static int ui_next=0;
-static gint timer;
-static void (*callback)(Gameboard *);
-
-/* perform a single frame of animation for all pause dialog buttons/rollovers */
-static gboolean finish_animate_buttons(gpointer ptr){
-  Gameboard *g=(Gameboard *)ptr;
-  int ret=0;
-
-  ret=animate_button_frame(g);
-
-  if(!ret && timer!=0){
-    g_source_remove(timer);
-    timer=0;
-  }
-
-  if(!ret && callback)
-    // undeploy finished... call the undeploy callback
-    callback(g);
-
-  return ret;
-}
-
-static void finish_post (Gameboard *g){
-  // back to buttonbar activity!
-  ui_next=0;
-  pop_background(g);
-  levelstate_next();
-  levelstate_go();
-  gamestate_go();
-} 
-
-static void finish_quit (Gameboard *g){
-  gtk_main_quit();
-} 
-
-static void undeploy_buttons(Gameboard *g){
-  // undeploy pause buttons
-  button_clear_state(g);
-  buttons_ready=0;
-
-  {
-    buttonstate *b=states; 
-    b->target_x-=BUTTON_EXPOSE;
-  }
-  {
-    buttonstate *b=states+1; 
-    b->target_x-=BUTTON_EXPOSE;
-  }
-  {
-    buttonstate *b=states+10; 
-    b->target_x+=BUTTON_EXPOSE;
-  }
-}
-
-static void local_go (Gameboard *g){
-  undeploy_buttons(g);
-  callback = finish_post;
-  timer = g_timeout_add(BUTTON_ANIM_INTERVAL, finish_animate_buttons, (gpointer)g);
-}
-
-static void local_quit (Gameboard *g){
-  undeploy_buttons(g);
-  callback = finish_quit;
-  timer = g_timeout_add(BUTTON_ANIM_INTERVAL, finish_animate_buttons, (gpointer)g);
-}
-
-/* initialize the rather weird little animation engine */
-static void setup_finish_buttons(Gameboard *g,int bw, int bh){
-  int i;
-  int w=get_board_width();
-  int h=get_board_height();
-
-  states[0].rollovertext="exit gPlanarity";
-  states[1].rollovertext="level menu";
-  states[10].rollovertext="next level!";
-
-  states[0].callback = local_quit;
-  states[1].callback = 0; // for now
-  states[10].callback = local_go;
-
-  for(i=0;i<NUMBUTTONS;i++)
-    states[i].position=0;
-
-  states[0].position = 2; //center;
-  states[1].position = 2; //center;
-  states[10].position = 2; //center;
-
-  {
-    buttonstate *b=states;
-    b->target_x_active=
-      b->x_active=
-      b->target_x_active=
-      b->target_x= 
-      w/2 - bw/2 + FINISH_BUTTON_BORDER;
-    b->x=b->target_x_inactive=b->x_inactive=b->target_x - BUTTON_EXPOSE;
-    b->y = h/2 + bh/2 - FINISH_BUTTON_Y;
-  }
-
-  {
-    buttonstate *b=states+1;
-    b->target_x_active=
-      b->x_active=
-      b->target_x_active=
-      b->target_x= w/2;
-    b->x=b->target_x_inactive=b->x_inactive=b->target_x - BUTTON_EXPOSE;
-    b->y = h/2 + bh/2 - FINISH_BUTTON_Y;
-  }
-
-  {
-    buttonstate *b=states+10;
-    b->target_x_active=
-      b->x_active=
-      b->target_x_active=
-      b->target_x= 
-      w/2 + bw/2 - FINISH_BUTTON_BORDER;
-    b->x=b->target_x_inactive=b->x_inactive=b->target_x + BUTTON_EXPOSE;
-    b->y = h/2 + bh/2 - FINISH_BUTTON_Y;
-  }
-
-  for(i=0;i<NUMBUTTONS;i++)
-    if(states[i].position)
-      rollover_extents(g,states+i);  
-}
-
-static void render_text_centered(cairo_t *c, char *s, int x, int y){
-  cairo_text_extents_t ex;
-
-  cairo_text_extents (c, s, &ex);
-  cairo_move_to (c, x-(ex.width/2)-ex.x_bearing, y-(ex.height/2)-ex.y_bearing);
-  cairo_show_text (c, s);  
-}
-
-static void draw_finishbox(Gameboard *g){
-  int w= get_board_width();
-  int h= get_board_height();
-
-  cairo_t *c = cairo_create(g->background);
-  borderbox_path(c,
-		 w/2 - FINISHBOX_WIDTH/2,
-		 h/2 - FINISHBOX_HEIGHT/2,
-		 FINISHBOX_WIDTH,
-		 FINISHBOX_HEIGHT);
-  cairo_set_source_rgb(c,1,1,1);
-  cairo_fill(c);
-
-  centerbox(c,
-	    w/2 - FINISHBOX_WIDTH/2,
-	    h/2 - FINISHBOX_HEIGHT/2,
-	    FINISHBOX_WIDTH,
-	    SCOREHEIGHT);
-
-  centerbox(c,
-	    w/2 - FINISHBOX_WIDTH/2 ,
-	    h/2 + FINISHBOX_HEIGHT/2 - SCOREHEIGHT,
-	    FINISHBOX_WIDTH,
-	    SCOREHEIGHT);
-
-  {
-    cairo_matrix_t ma;
-    char time[160];
-    char buffer[160];
-    int  ho = get_elapsed() / 3600;
-    int  mi = get_elapsed() / 60 - ho*60;
-    int  se = get_elapsed() - ho*3600 - mi*60;
-    int y;
-    int time_bonus=get_initial_intersections()-get_elapsed();
-    if(time_bonus<0)time_bonus=0;
-
-    if(ho){
-      snprintf(time,160,"%d:%02d:%02d",ho,mi,se);
-    }else if (mi){
-      snprintf(time,160,"%d:%02d",mi,se);
-    }else{
-      snprintf(time,160,"%d seconds",se);
-    }
-
-    cairo_select_font_face (c, "Arial",
-			    CAIRO_FONT_SLANT_NORMAL,
-			    CAIRO_FONT_WEIGHT_BOLD);
-
-    cairo_matrix_init_scale (&ma, 18.,18.);
-    cairo_set_font_matrix (c,&ma);
-    cairo_set_source_rgba (c, TEXT_COLOR);
-
-    y=h/2-FINISHBOX_HEIGHT/2+SCOREHEIGHT/2;
-    render_text_centered(c,"Level Complete!", w/2,y);y+=45;
-
-    cairo_select_font_face (c, "Arial",
-			    CAIRO_FONT_SLANT_NORMAL,
-			    CAIRO_FONT_WEIGHT_NORMAL);
-    cairo_matrix_init_scale (&ma, 16.,16.);
-    cairo_set_font_matrix (c,&ma);
-
-    snprintf(buffer,160,"Elapsed: %s",time);
-    render_text_centered(c,buffer, w/2,y);y+=35;
-
-
-    snprintf(buffer,160,"Score: %d",get_initial_intersections());
-    render_text_centered(c,buffer, w/2,y);y+=24;
-    snprintf(buffer,160,"Bonus: %d",time_bonus);
-    render_text_centered(c,buffer, w/2,y);y+=45;
-
-    cairo_select_font_face (c, "Arial",
-			    CAIRO_FONT_SLANT_NORMAL,
-			    CAIRO_FONT_WEIGHT_BOLD);
-    snprintf(buffer,160,"Total score: %d",get_raw_score());
-    render_text_centered(c,buffer, w/2,y);
-
-  }
-
-  cairo_destroy(c);
-}
-
-static void finish_post_undeploy(Gameboard *g){
-  // set up new buttons
-  setup_finish_buttons(g,FINISHBOX_WIDTH, FINISHBOX_HEIGHT);
-
-  // draw pausebox
-  push_curtain(g,draw_finishbox);
-
-  // deploy new buttons
-  callback=0;
-  timer = g_timeout_add(BUTTON_ANIM_INTERVAL, finish_animate_buttons, (gpointer)g);
-  buttons_ready=1;
-}
-
-void finish_level_dialog(Gameboard *g){
-  // undeploy buttonbar
-  ui_next=1;
-  push_background(g,0);
-  undeploy_buttonbar(g,finish_post_undeploy);
-}
-
-int finish_dialog_active(){
-  return ui_next;
-}

Deleted: trunk/planarity/finish.h
===================================================================
--- trunk/planarity/finish.h	2005-09-24 22:02:43 UTC (rev 10059)
+++ trunk/planarity/finish.h	2005-09-25 09:24:23 UTC (rev 10060)
@@ -1,7 +0,0 @@
-#define FINISH_BUTTON_BORDER 35
-#define FINISH_BUTTON_Y 25
-#define FINISHBOX_WIDTH 200
-#define FINISHBOX_HEIGHT 300
-
-extern void finish_level_dialog(Gameboard *g);
-extern int finish_dialog_active();

Modified: trunk/planarity/gameboard.c
===================================================================
--- trunk/planarity/gameboard.c	2005-09-24 22:02:43 UTC (rev 10059)
+++ trunk/planarity/gameboard.c	2005-09-25 09:24:23 UTC (rev 10060)
@@ -10,937 +10,36 @@
 
 #include "graph.h"
 #include "gameboard.h"
-#include "gamestate.h"
 #include "levelstate.h"
-#include "button_base.h"
-#include "buttonbar.h"
-#include "box.h"
 
 static GtkWidgetClass *parent_class = NULL;
 
-/* vertex area and draw operations ***********************************/
-
-// invalidate the box around a single vertex
-static void invalidate_region_vertex_off(GtkWidget *widget, 
-					 vertex *v, int dx, int dy){
-  if(v){
-    GdkRectangle r;
-    r.x = v->x - V_RADIUS - V_LINE + dx;
-    r.y = v->y - V_RADIUS - V_LINE + dy;
-    r.width =  (V_RADIUS + V_LINE)*2;
-    r.height = (V_RADIUS + V_LINE)*2;
-    
-    gdk_window_invalidate_rect (widget->window, &r, FALSE);
-  }
-}
-
-void invalidate_region_vertex(Gameboard *g, vertex *v){
-  invalidate_region_vertex_off(&g->w,v,0,0);
-}
-
-// invalidate the box around a single line
-static void invalidate_region_edges(GtkWidget *widget, vertex *v){
-  GdkRectangle r;
-  
-  if(v){
-    edge_list *el=v->edges;
-    while (el){
-      edge *e=el->edge;
-      r.x = min(e->A->x,e->B->x) - E_LINE;
-      r.y = min(e->A->y,e->B->y) - E_LINE;
-      r.width  = labs(e->B->x - e->A->x) + 1 + E_LINE*2;
-      r.height = labs(e->B->y - e->A->y) + 1 + E_LINE*2;
-      gdk_window_invalidate_rect (widget->window, &r, FALSE);
-      el=el->next;
-    }
-  }
-}
-
-// invalidate the vertex and attached verticies
-static void invalidate_region_attached(GtkWidget *widget, vertex *v){
-  if(v){
-    edge_list *el=v->edges;
-    while (el){
-      edge *e=el->edge;
-      if(e->A != v)invalidate_region_vertex(GAMEBOARD(widget),e->A);
-      if(e->B != v)invalidate_region_vertex(GAMEBOARD(widget),e->B);
-      el=el->next;
-    }
-    invalidate_region_vertex(GAMEBOARD(widget),v);
-  }
-}
-
-// invalidate the selection box plus enough area to catch any verticies
-static void invalidate_region_selection(GtkWidget *widget){
-  Gameboard *g = GAMEBOARD (widget);
-  GdkRectangle r;
-  r.x = g->selectionx - (V_RADIUS + V_LINE)*2;
-  r.y = g->selectiony - (V_RADIUS + V_LINE)*2;
-  r.width =  g->selectionw + (V_RADIUS + V_LINE)*4;
-  r.height = g->selectionh + (V_RADIUS + V_LINE)*4;
-  
-  gdk_window_invalidate_rect (widget->window, &r, FALSE);
-}
-
-// invalidate the selection box plus enough area to catch any verticies
-static void invalidate_region_verticies_selection(GtkWidget *widget){
-  Gameboard *g = GAMEBOARD (widget);
-  vertex *v=g->g->verticies;
-  while(v){
-    if(v->selected)
-      invalidate_region_vertex_off(widget,v,g->dragx,g->dragy);
-    v=v->next;
-  }
-}
-
-static cairo_surface_t *cache_vertex(Gameboard *g){
-  cairo_surface_t *ret=
-    cairo_surface_create_similar (cairo_get_target (g->wc),
-				  CAIRO_CONTENT_COLOR_ALPHA,
-				  (V_RADIUS+V_LINE)*2,
-				  (V_RADIUS+V_LINE)*2);
-  cairo_t *c = cairo_create(ret);
-  
-  cairo_set_line_width(c,V_LINE);
-  cairo_arc(c,V_RADIUS+V_LINE,V_RADIUS+V_LINE,V_RADIUS,0,2*M_PI);
-  cairo_set_source_rgb(c,V_FILL_IDLE_COLOR);
-  cairo_fill_preserve(c);
-  cairo_set_source_rgb(c,V_LINE_COLOR);
-  cairo_stroke(c);
-
-  cairo_destroy(c);
-  return ret;
-}
-
-static cairo_surface_t *cache_vertex_sel(Gameboard *g){
-  cairo_surface_t *ret=
-    cairo_surface_create_similar (cairo_get_target (g->wc),
-				  CAIRO_CONTENT_COLOR_ALPHA,
-				  (V_RADIUS+V_LINE)*2,
-				  (V_RADIUS+V_LINE)*2);
-  cairo_t *c = cairo_create(ret);
-  
-  cairo_set_line_width(c,V_LINE);
-  cairo_arc(c,V_RADIUS+V_LINE,V_RADIUS+V_LINE,V_RADIUS,0,2*M_PI);
-  cairo_set_source_rgb(c,V_FILL_LIT_COLOR);
-  cairo_fill_preserve(c);
-  cairo_set_source_rgb(c,V_LINE_COLOR);
-  cairo_stroke(c);
-  cairo_arc(c,V_RADIUS+V_LINE,V_RADIUS+V_LINE,V_RADIUS*.5,0,2*M_PI);
-  cairo_set_source_rgb(c,V_FILL_IDLE_COLOR);
-  cairo_fill(c);
-
-  cairo_destroy(c);
-  return ret;
-}
-
-static cairo_surface_t *cache_vertex_grabbed(Gameboard *g){
-  cairo_surface_t *ret=
-    cairo_surface_create_similar (cairo_get_target (g->wc),
-				  CAIRO_CONTENT_COLOR_ALPHA,
-				  (V_RADIUS+V_LINE)*2,
-				  (V_RADIUS+V_LINE)*2);
-  cairo_t *c = cairo_create(ret);
-  
-  cairo_set_line_width(c,V_LINE);
-  cairo_arc(c,V_RADIUS+V_LINE,V_RADIUS+V_LINE,V_RADIUS,0,2*M_PI);
-  cairo_set_source_rgb(c,V_FILL_LIT_COLOR);
-  cairo_fill_preserve(c);
-  cairo_set_source_rgb(c,V_LINE_COLOR);
-  cairo_stroke(c);
-  cairo_arc(c,V_RADIUS+V_LINE,V_RADIUS+V_LINE,V_RADIUS*.5,0,2*M_PI);
-  cairo_set_source_rgb(c,V_FILL_ADJ_COLOR);
-  cairo_fill(c);
-
-  cairo_destroy(c);
-  return ret;
-}
-
-static cairo_surface_t *cache_vertex_lit(Gameboard *g){
-  cairo_surface_t *ret=
-    cairo_surface_create_similar (cairo_get_target (g->wc),
-				  CAIRO_CONTENT_COLOR_ALPHA,
-				  (V_RADIUS+V_LINE)*2,
-				  (V_RADIUS+V_LINE)*2);
-  cairo_t *c = cairo_create(ret);
-  
-  cairo_set_line_width(c,V_LINE);
-  cairo_arc(c,V_RADIUS+V_LINE,V_RADIUS+V_LINE,V_RADIUS,0,2*M_PI);
-  cairo_set_source_rgb(c,V_FILL_LIT_COLOR);
-  cairo_fill_preserve(c);
-  cairo_set_source_rgb(c,V_LINE_COLOR);
-  cairo_stroke(c);
-
-  cairo_destroy(c);
-  return ret;
-}
-
-static cairo_surface_t *cache_vertex_attached(Gameboard *g){
-  cairo_surface_t *ret=
-    cairo_surface_create_similar (cairo_get_target (g->wc),
-				  CAIRO_CONTENT_COLOR_ALPHA,
-				  (V_RADIUS+V_LINE)*2,
-				  (V_RADIUS+V_LINE)*2);
-  cairo_t *c = cairo_create(ret);
-  
-  cairo_set_line_width(c,V_LINE);
-  cairo_arc(c,V_RADIUS+V_LINE,V_RADIUS+V_LINE,V_RADIUS,0,2*M_PI);
-  cairo_set_source_rgb(c,V_FILL_ADJ_COLOR);
-  cairo_fill_preserve(c);
-  cairo_set_source_rgb(c,V_LINE_COLOR);
-  cairo_stroke(c);
-
-  cairo_destroy(c);
-  return ret;
-}
-
-static cairo_surface_t *cache_vertex_ghost(Gameboard *g){
-  cairo_surface_t *ret=
-    cairo_surface_create_similar (cairo_get_target (g->wc),
-				  CAIRO_CONTENT_COLOR_ALPHA,
-				  (V_RADIUS+V_LINE)*2,
-				  (V_RADIUS+V_LINE)*2);
-  cairo_t *c = cairo_create(ret);
-  
-  cairo_set_line_width(c,V_LINE);
-  cairo_arc(c,V_RADIUS+V_LINE,V_RADIUS+V_LINE,V_RADIUS,0,2*M_PI);
-  cairo_set_source_rgba(c,V_LINE_COLOR,.2);
-  cairo_fill_preserve(c);
-  cairo_set_source_rgba(c,V_LINE_COLOR,.4);
-  cairo_stroke(c);
-
-  cairo_destroy(c);
-  return ret;
-}
-
-
-static void draw_vertex(cairo_t *c,vertex *v,cairo_surface_t *s){
-  cairo_set_source_surface(c,
-			   s,
-			   v->x-V_LINE-V_RADIUS,
-			   v->y-V_LINE-V_RADIUS);
-  cairo_paint(c);
-}      
-
-static void setup_background_edge(cairo_t *c){
-  cairo_set_line_width(c,E_LINE);
-  cairo_set_source_rgba(c,E_LINE_B_COLOR);
-}
-
-static void setup_foreground_edge(cairo_t *c){
-  cairo_set_line_width(c,E_LINE);
-  cairo_set_source_rgba(c,E_LINE_F_COLOR);
-}
-
-static void draw_edge(cairo_t *c,edge *e){
-  cairo_move_to(c,e->A->x,e->A->y);
-  cairo_line_to(c,e->B->x,e->B->y);
-}
-
-static void finish_edge(cairo_t *c){
-  cairo_stroke(c);
-}
-
-static void draw_selection_rectangle(Gameboard *g,cairo_t *c){
-  cairo_set_source_rgba(c,SELECTBOX_COLOR);
-  cairo_rectangle(c,g->selectionx,
-		  g->selectiony,
-		  g->selectionw,
-		  g->selectionh);
-  cairo_fill(c);
-}
-
-static void midground_draw(Gameboard *g,cairo_t *c,int x,int y,int w,int h){
-  /* Edges attached to the grabbed vertex are drawn here; they're the
-     inactive edges. */
-  if(g->grabbed_vertex && !g->hide_lines){
-    edge_list *el=g->grabbed_vertex->edges;
-    setup_foreground_edge(c);
-    while(el){
-      edge *e=el->edge;
-      /* no need to check rectangle; if they're to be drawn, they'll
-	 always be in the rect */
-      draw_edge(c,e);
-      el=el->next;
-    }
-    finish_edge(c);
-  }
-  
-  /* verticies are drawn in the foreground */
-  {
-    vertex *v = g->g->verticies;
-    int clipx = x-V_RADIUS;
-    int clipw = x+w+V_RADIUS;
-    int clipy = y-V_RADIUS;
-    int cliph = y+h+V_RADIUS;
-    
-    while(v){
-
-      /* is the vertex in the expose rectangle? */
-      if(v->x>=clipx && v->x<=clipw &&
-	 v->y>=clipy && v->y<=cliph){
-	
-	if(v == g->grabbed_vertex && !g->group_drag) {
-	  draw_vertex(c,v,g->vertex_grabbed);      
-	} else if( v->selected ){
-	  draw_vertex(c,v,g->vertex_sel);
-	} else if ( v == g->lit_vertex){
-	  draw_vertex(c,v,g->vertex_lit);
-	} else if (v->attached_to_grabbed && !g->group_drag){
-	  draw_vertex(c,v,g->vertex_attached);
-	}else
-	  draw_vertex(c,v,g->vertex);
-      }
-      
-      v=v->next;
-    }
-  }
-}
-
-static void background_draw(Gameboard *g,cairo_t *c){
-  int width=get_board_width();
-  int height=get_board_height();
-  edge *e=g->g->edges;
-
-  cairo_set_source_rgb(c,1,1,1);
-  cairo_paint(c);
-    
-  if(!g->hide_lines){
-    setup_background_edge(c);
-    while(e){
-      if(e->active){
-	draw_edge(c,e);
-      }
-      e=e->next;
-    }
-    finish_edge(c);
-  }
-
-  // if there's a a group drag in progress, midground is drawn here
-  if(g->group_drag)
-    midground_draw(g,c,0,0,width,height);
-
-}
-
-static void background_addv(Gameboard *g,cairo_t *c, vertex *v){
-  edge_list *el=v->edges;
-
-  if(!g->hide_lines){
-    setup_background_edge(c);
-    while(el){
-      edge *e=el->edge;
-      
-      if(e->active)
-	draw_edge(c,e);
-      
-      el=el->next;
-    }
-    finish_edge(c);
-  }
-}
-
-static void foreground_draw(Gameboard *g,cairo_t *c,
-			    int x,int y,int width,int height){
-  /* if a group drag is in progress, draw the group ghosted in the foreground */
-
-  if(g->group_drag){ 
-    vertex *v = g->g->verticies;
-    while(v){
-      
-      if( v->selected ){
-	vertex tv;
-	tv.x=v->x+g->dragx;
-	tv.y=v->y+g->dragy;
-	draw_vertex(c,&tv,g->vertex_ghost);
-      }
-
-      v=v->next;
-    }
-  }else
-    midground_draw(g,c,x,y,width,height);    
-
-  if(g->selection_grab)
-    draw_selection_rectangle(g,c);
-}
-
-static void update_background(GtkWidget *widget){
-  Gameboard *g = GAMEBOARD (widget);
-  GdkRectangle r;
-  cairo_t *c = cairo_create(g->background);
-
-  g->delayed_background=0;
-
-  // render the far background plane
-  background_draw(g,c);
-  cairo_destroy(c);
-  
-  r.x=0;
-  r.y=0;
-  r.width=widget->allocation.width;
-  r.height=widget->allocation.height;
-
-  gdk_window_invalidate_rect (widget->window, &r, FALSE);
-    
-}
-
-void update_full(Gameboard *g){
-  update_background(&g->w);
-}
-
-// also updates score
-static void update_background_addv(GtkWidget *widget, vertex *v){
-  Gameboard *g = GAMEBOARD (widget);
-  cairo_t *c = cairo_create(g->background);
-
-  g->delayed_background=0;
-
-  // render the far background plane
-  background_addv(g,c,v);
-  cairo_destroy(c);
-
-  invalidate_region_attached(widget,v);    
-}
-
-static void update_background_delayed(GtkWidget *widget){
-  Gameboard *g = GAMEBOARD (widget);
-  g->delayed_background=1;
-}
-
-static void check_lit(GtkWidget *widget,int x, int y){
-  Gameboard *g = GAMEBOARD (widget);
-  vertex *v = find_vertex(g->g,x,y);
-  if(v!=g->lit_vertex){
-    invalidate_region_vertex(g,v);
-    invalidate_region_vertex(g,g->lit_vertex);
-    g->lit_vertex = v;
-  }
-}
-
-static void score_update(Gameboard *g){
-  /* Score, level, intersections */
-  char level_string[160];
-  char score_string[160];
-  char int_string[160];
-  char obj_string[160];
-  cairo_text_extents_t extentsL;
-  cairo_text_extents_t extentsS;
-  cairo_text_extents_t extentsO;
-  cairo_text_extents_t extentsI;
-  cairo_matrix_t m;
-
-  cairo_t *c = cairo_create(g->forescore);
-
-  if(g->g->active_intersections <= get_objective()){
-    deploy_check(g);
-  }else{
-    undeploy_check(g);
-  }
-
-
-  // clear the pane
-  cairo_save(c);
-  cairo_set_operator(c,CAIRO_OPERATOR_CLEAR);
-  cairo_set_source_rgba (c, 1,1,1,1);
-  cairo_paint(c);
-  cairo_restore(c);
-
-  topbox(c,get_board_width(),SCOREHEIGHT);
-
-  cairo_select_font_face (c, "Arial",
-			  CAIRO_FONT_SLANT_NORMAL,
-			  CAIRO_FONT_WEIGHT_BOLD);
-
-  cairo_matrix_init_scale (&m, 12.,15.);
-  cairo_set_font_matrix (c,&m);
-  cairo_set_source_rgba (c, TEXT_COLOR);
-
-  snprintf(level_string,160,"Level %d: \"%s\"",get_level_num()+1,get_level_name());
-  snprintf(score_string,160,"Score: %d",get_score());
-  snprintf(int_string,160,"Intersections: %d",g->g->active_intersections);
-  snprintf(obj_string,160,"Objective: %s",get_objective_string());
-
-  cairo_text_extents (c, level_string, &extentsL);
-  cairo_text_extents (c, obj_string, &extentsO);
-  cairo_text_extents (c, int_string, &extentsI);
-  cairo_text_extents (c, score_string, &extentsS);
-
-  /*
-  text_h = extentsL.height;
-  text_h = max(text_h,extentsO.height);
-  text_h = max(text_h,extentsI.height);
-  text_h = max(text_h,extentsS.height);
-  */
-
-  int ty1 = 23;
-  int ty2 = 38;
-
-  cairo_move_to (c, 15, ty1);
-  cairo_show_text (c, int_string);  
-  cairo_move_to (c, 15, ty2);
-  cairo_show_text (c, score_string);  
-
-  cairo_move_to (c, get_board_width()-extentsL.width-15, ty1);
-  cairo_show_text (c, level_string);  
-  cairo_move_to (c, get_board_width()-extentsO.width-15, ty2);
-  cairo_show_text (c, obj_string);  
-
-  cairo_destroy(c);
-
-  // slightly lazy
-  {
-    GdkRectangle r;
-    r.x=0;
-    r.y=0;
-    r.width=get_board_width();
-    r.height = SCOREHEIGHT;
-    gdk_window_invalidate_rect (g->w.window, &r, FALSE);
-  }
-}
-
-#define CW 4
-static void cache_curtain(Gameboard *g){
-  int x,y;
-  cairo_t *c;
-  g->curtains=
-    cairo_surface_create_similar (cairo_get_target (g->wc),
-				  CAIRO_CONTENT_COLOR_ALPHA,
-				  CW,CW);
-  
-  c = cairo_create(g->curtains);
-  cairo_save(c);
-  cairo_set_operator(c,CAIRO_OPERATOR_CLEAR);
-  cairo_set_source_rgba (c, 1,1,1,1);
-  cairo_paint(c);
-  cairo_restore(c);
-      
-  cairo_set_line_width(c,1);
-  cairo_set_source_rgba (c, 0,0,0,.5);
-  
-  for(y=0;y<CW;y++){
-    for(x=y&1;x<CW;x+=2){
-      cairo_move_to(c,x+.5,y);
-      cairo_rel_line_to(c,0,1);
-    }
-  }
-  cairo_stroke(c);
-  cairo_destroy(c);
-  
-  g->curtainp=cairo_pattern_create_for_surface (g->curtains);
-  cairo_pattern_set_extend (g->curtainp, CAIRO_EXTEND_REPEAT);
-
-}
-
-static gint mouse_motion(GtkWidget        *widget,
-			 GdkEventMotion   *event){
-  Gameboard *g = GAMEBOARD (widget);
-
-  if(g->button_grabbed || paused_p()){
-    g->button_grabbed = 
-      button_motion_event(g,event,
-			  (!g->lit_vertex && !g->grabbed_vertex && !g->selection_grab));
-
-    if(paused_p())return TRUE;
-  }
-
-  if(!g->button_grabbed){
-    if(g->grabbed_vertex){
-      int x = (int)event->x;
-      int y = (int)event->y;
-      g->dragx = x-g->grabx;
-      g->dragy = y-g->graby;
-      
-      invalidate_region_vertex(g,g->grabbed_vertex);
-      invalidate_region_edges(widget,g->grabbed_vertex);
-      move_vertex(g->g,g->grabbed_vertex,x+g->graboffx,y+g->graboffy);
-      invalidate_region_vertex(g,g->grabbed_vertex);
-      invalidate_region_edges(widget,g->grabbed_vertex);
-    }else if (g->selection_grab){
-      invalidate_region_selection(widget);
-      
-      g->selectionx = min(g->grabx, event->x);
-      g->selectionw = labs(g->grabx - event->x);
-      g->selectiony = min(g->graby, event->y);
-      g->selectionh = labs(g->graby - event->y);
-      select_verticies(g->g,
-		       g->selectionx,g->selectiony,
-		       g->selectionx+g->selectionw-1,
-		       g->selectiony+g->selectionh-1);
-      
-      invalidate_region_selection(widget);
-    }else if(g->group_drag){
-      invalidate_region_verticies_selection(widget);
-      g->dragx = event->x - g->grabx;
-      g->dragy = event->y - g->graby;
-      
-      // if this puts any of the dragged offscreen adjust the drag back
-      // onscreen.  It will avoid confusing/concerning users
-      {
-	vertex *v=g->g->verticies;
-	int w=get_board_width();
-	int h=get_board_height();
-	
-	while(v){
-	  if(v->selected){
-	    if(v->x + g->dragx >= w)
-	      g->dragx=w - v->x -1;
-	    if(v->x + g->dragx < 0 )
-	      g->dragx= -v->x;
-	    if(v->y + g->dragy >= h)
-	      g->dragy=h - v->y -1;
-	    if(v->y + g->dragy < 0 )
-	    g->dragy= -v->y;
-	  }
-	  v=v->next;
-	}
-      }
-      
-      invalidate_region_verticies_selection(widget);
-    }else{
-      check_lit(widget, (int)event->x,(int)event->y);
-      
-      button_motion_event(g,event,
-			  (!g->lit_vertex && !g->grabbed_vertex && !g->selection_grab));
-
-    }
-  }
-  return TRUE;
-}
-
-static gboolean button_press (GtkWidget        *widget,
-			      GdkEventButton   *event){
-  Gameboard *g = GAMEBOARD (widget);
-
-  vertex *v = find_vertex(g->g,(int)event->x,(int)event->y);
-  g->button_grabbed=0;
-
-  if(paused_p()){
-    // only buttongrabs
-    if(button_button_press(g,event,1))
-      g->button_grabbed=1;
-    return TRUE;
-  }
-     
-  if(!g->group_drag && event->state&GDK_SHIFT_MASK){
-    if(v){
-      if(v->selected){
-	v->selected=0;
-	g->selection_active--;
-      }else{
-	v->selected=1;
-	g->selection_active++;
-      }
-      invalidate_region_vertex(g,g->lit_vertex);
-    }else{
-      // addending group drag
-      g->selection_grab=1;
-      g->grabx = (int)event->x;
-      g->graby = (int)event->y;
-      g->selectionx = g->grabx;
-      g->selectionw = 0;
-      g->selectiony = g->graby;
-      g->selectionh = 0;
-    }
-  }else{
-
-    if(g->selection_active){
-      vertex *v = find_vertex(g->g,(int)event->x,(int)event->y);
-      if(v && v->selected){
-	// group drag
-	g->group_drag=1;
-	g->grabx = (int)event->x;
-	g->graby = (int)event->y;
-	g->dragx = 0;
-	g->dragy = 0;
-	// put the verticies into the background for faster update
-	update_background(widget); 
-      }else{
-
-	if(button_button_press(g,event,1)){
-	  g->button_grabbed=1;
-	}else{
-	  deselect_verticies(g->g);
-	  update_background(widget); 
-	  g->selection_active=0;
-	  g->group_drag=0;
-	  check_lit(widget,event->x,event->y);
-	  button_press(widget,event);
-	}
-      }
-
-    }else if(g->lit_vertex){
-      // vertex grab
-      g->grabbed_vertex = g->lit_vertex;
-      g->grabx = (int)event->x;
-      g->graby = (int)event->y;
-      g->graboffx = g->grabbed_vertex->x-g->grabx;
-      g->graboffy = g->grabbed_vertex->y-g->graby;
-      
-      grab_vertex(g->g,g->grabbed_vertex);
-      invalidate_region_attached(widget,g->grabbed_vertex);
-      invalidate_region_edges(widget,g->grabbed_vertex);
-      
-      update_background_delayed(widget);
-    }else{
-      
-      if(button_button_press(g,event,1)){
-	g->button_grabbed=1;
-      }else{
-	// selection grab;
-	g->selection_grab=1;
-	g->grabx = (int)event->x;
-	g->graby = (int)event->y;
-	g->selectionx = g->grabx;
-	g->selectionw = 0;
-	g->selectiony = g->graby;
-	g->selectionh = 0;
-      }
-    }
-  }
-
-  if(!g->button_grabbed)
-    hide_intersections(g);
-  
-  return TRUE;
-}
-
-static gboolean button_release (GtkWidget        *widget,
-				GdkEventButton   *event){
-  Gameboard *g = GAMEBOARD (widget);
-
-  button_button_release(g,event,1);
-  if(paused_p())return TRUE;
-
-  if(g->grabbed_vertex){
-    ungrab_vertex(g->g,g->grabbed_vertex);
-    update_background_addv(widget,g->grabbed_vertex);
-    score_update(g);
-    g->grabbed_vertex = 0;
-  }
-
-  if(g->selection_grab){
-    invalidate_region_selection(widget);
-    g->selection_grab=0;
-    // are there selected verticies? Avoid accidentally misgrabbing one.
-    if(num_selected_verticies(g->g)<=1){
-      g->selection_active=0;
-      deselect_verticies(g->g); // could have grabbed just one
-    }else{
-      commit_volatile_selection();
-      g->selection_active=num_selected_verticies(g->g);
-    }
-  }
-
-  if(g->group_drag){
-    move_selected_verticies(g->g,g->dragx,g->dragy);
-    update_background(widget); // cheating
-    score_update(g);
-    g->group_drag=0;
-  }
-
-  mouse_motion(widget,(GdkEventMotion *)event); // the cast is safe in
-						// this case
-
-  return TRUE;
-}
-
-static void size_request (GtkWidget *widget,GtkRequisition *requisition){
-
-  requisition->width = get_orig_width();
-  requisition->height = get_orig_height();
-
-}
-
 static void gameboard_init (Gameboard *g){
-
   // instance initialization
-
-
+  g->g.width=g->g.orig_width=800;
+  g->g.height=g->g.orig_height=600;
 }
 
 static void gameboard_destroy (GtkObject *object){
   if (GTK_OBJECT_CLASS (parent_class)->destroy)
     (*GTK_OBJECT_CLASS (parent_class)->destroy) (object);
-}
 
-static void draw_intersection(cairo_t *c,double x, double y){
-  cairo_move_to(c,x-INTERSECTION_RADIUS,y);
-  cairo_rel_line_to(c,INTERSECTION_RADIUS,-INTERSECTION_RADIUS);
-  cairo_rel_line_to(c,INTERSECTION_RADIUS,INTERSECTION_RADIUS);
-  cairo_rel_line_to(c,-INTERSECTION_RADIUS,INTERSECTION_RADIUS);
-  cairo_close_path(c);
+  // free local resources
 }
-static void expose_intersections(Gameboard *g,cairo_t *c,
-				 int x,int y,int w,int h){
 
-  if(g->show_intersections){
-    cairo_set_source_rgba (c, INTERSECTION_COLOR);
-    cairo_set_line_width(c, INTERSECTION_LINE_WIDTH);
-
-    double xx=x-(INTERSECTION_LINE_WIDTH*.5 + INTERSECTION_RADIUS);
-    double x2=w+x+(INTERSECTION_LINE_WIDTH + INTERSECTION_RADIUS*2);
-    double yy=y-(INTERSECTION_LINE_WIDTH*.5 + INTERSECTION_RADIUS);
-    double y2=h+y+(INTERSECTION_LINE_WIDTH + INTERSECTION_RADIUS*2);
-
-    // walk the intersection list of all edges; draw if paired is a higher pointer
-    edge *e=g->g->edges;
-    while(e){
-      intersection *i = e->i.next;
-      while(i){
-	if(i->paired > i){
-	  double ix=i->x, iy=i->y;
-    
-	  if(ix >= xx && ix <= x2 && iy >= yy && iy <= y2)
-	    draw_intersection(c,ix,iy);
-	}
-	i=i->next;
-      }
-      e=e->next;
-    }
-    cairo_stroke(c);
-  }
-}
-
-void pop_background(Gameboard *g){
-  if(g->pushed_background){
-    g->pushed_background=0;
-    g->pushed_curtain=0;
-    update_full(g);
-  }
-}
-
-void push_background(Gameboard *g, void(*redraw_callback)(Gameboard *g)){
-  cairo_t *c = cairo_create(g->background);
-  int w = g->w.allocation.width;
-  int h = g->w.allocation.height;
-
-  if(g->pushed_background)
-    pop_background(g);
-
-  g->redraw_callback=redraw_callback;
-
-  foreground_draw(g,c,0,0,w,h);
-
-  // copy in the score and button surfaces
-  cairo_set_source_surface(c,g->forescore,0,0);
-  cairo_rectangle(c, 0,0,w,
-		  min(SCOREHEIGHT,h));
-  cairo_fill(c);
-
-  cairo_set_source_surface(c,g->forebutton,0,g->w.allocation.height-SCOREHEIGHT);
-  cairo_rectangle(c, 0,0,w,h);
-  cairo_fill(c);
-
-  expose_intersections(g,c,0,0,w,h);
-  cairo_destroy(c);
-
-  if(redraw_callback)redraw_callback(g);
-
-  g->pushed_background=1;
-  {
-    GdkRectangle r;
-    r.x=0;
-    r.y=0;
-    r.width=w;
-    r.height=h;
-    
-    gdk_window_invalidate_rect (g->w.window, &r, FALSE);
-  }
-}
-
-void push_curtain(Gameboard *g,void(*redraw_callback)(Gameboard *g)){
-  if(!g->pushed_background)push_background(g,0);
-  if(!g->pushed_curtain){ 
-    cairo_t *c = cairo_create(g->background);
-    int w = g->w.allocation.width;
-    int h = g->w.allocation.height;
-    g->pushed_curtain=1;
-    
-    g->redraw_callback=redraw_callback;
-    cairo_set_source (c, g->curtainp);
-    cairo_paint(c);
-    cairo_destroy(c);
-    
-    if(redraw_callback)redraw_callback(g);
-    
-    {
-      GdkRectangle r;
-      r.x=0;
-      r.y=0;
-      r.width=w;
-      r.height=h;
-      
-      gdk_window_invalidate_rect (g->w.window, &r, FALSE);
-    }
-  }
-}
-
-void run_immediate_expose(Gameboard *g,
-			  int x, int y, int w, int h){
-
-  if (w==0 || h==0) return;
-
-  //fprintf(stderr,"(%d) %d/%d %d/%d\n",g->first_expose,x,w,y,h);
-
-  cairo_t *c = cairo_create(g->foreground);
-
-  // copy background to foreground draw buffer
-  cairo_set_source_surface(c,g->background,0,0);
-  cairo_rectangle(c,x,y,w,h);
-  cairo_fill(c);
-
-  if(!g->pushed_background){
-    foreground_draw(g,c,x,y,w,h);
-
-    // copy in any of the score or button surfaces?
-    if(y<SCOREHEIGHT){
-      cairo_set_source_surface(c,g->forescore,0,0);
-      cairo_rectangle(c, x,y,w,
-		      min(SCOREHEIGHT-y,h));
-      cairo_fill(c);
-    }
-    if(y+h>g->w.allocation.height-SCOREHEIGHT){
-      cairo_set_source_surface(c,g->forebutton,0,g->w.allocation.height-SCOREHEIGHT);
-      cairo_rectangle(c, x,y,w,h);
-      cairo_fill(c);
-    }
-
-    expose_intersections(g,c,x,y,w,h);
-  }
-  
-  expose_buttons(g,c,x,y,w,h);
-
-  cairo_destroy(c);
-  
-  // blit to window
-  cairo_set_source_surface(g->wc,g->foreground,0,0);
-  cairo_rectangle(g->wc,x,y,w,h);
-  cairo_fill(g->wc);
-
-  if(g->delayed_background)update_background(&g->w);
-  g->first_expose=1;
-  //gdk_window_process_all_updates(); 
-}
-
 static gint gameboard_expose (GtkWidget      *widget,
 			      GdkEventExpose *event){
-  
   Gameboard *g = GAMEBOARD (widget);
-  run_immediate_expose(g,event->area.x,event->area.y,
-		       event->area.width,event->area.height);
+  gameboard_draw(g,event->area.x,event->area.y,
+		 event->area.width,event->area.height);
   return FALSE;
 }
 
-static void paint_bottom_box (Gameboard *g){
-  cairo_t *c = cairo_create(g->forebutton);
-  
-  cairo_save(c);
-  cairo_set_operator(c,CAIRO_OPERATOR_CLEAR);
-  cairo_set_source_rgba (c, 1,1,1,1);
-  cairo_paint(c);
-  cairo_restore(c);
-
-  bottombox(c,g->w.allocation.width,SCOREHEIGHT);
-  cairo_destroy(c);
+static void gameboard_size_request (GtkWidget *widget,
+				    GtkRequisition *requisition){
+  Gameboard *g = GAMEBOARD (widget);
+  requisition->width = g->g.orig_width;
+  requisition->height = g->g.orig_height;
 }
 
 static void gameboard_realize (GtkWidget *widget){
@@ -1000,9 +99,6 @@
 				  widget->allocation.width,
 				  widget->allocation.height);  
 
-  // cache the vertex images that are drawn in quantity; the blit with
-  // alpha is much faster than the render
-
   g->vertex = cache_vertex(g);
   g->vertex_lit = cache_vertex_lit(g);
   g->vertex_attached = cache_vertex_attached(g);
@@ -1010,9 +106,9 @@
   g->vertex_sel = cache_vertex_sel(g);
   g->vertex_ghost = cache_vertex_ghost(g);
 
-  update_background(widget); 
-  score_update(g);
-  paint_bottom_box(g);
+  update_full(g); 
+  update_score(g);
+  draw_buttonbar_box(g);
   init_buttons(g);
   cache_curtain(g);
 }
@@ -1023,14 +119,15 @@
   widget->allocation = *allocation;
 
   if (GTK_WIDGET_REALIZED (widget)){
-    int oldw=get_board_width();
-    int oldh=get_board_height();
+    int oldw=g->g.width;
+    int oldh=g->g.height;
 
-    gdk_window_move_resize (widget->window, allocation->x, allocation->y, 
-			    allocation->width, allocation->height);
-    
- 
-     if (g->forescore)
+    g->g.width = allocation->width;
+    g->g.height = allocation->height;
+
+    if(g->wc)cairo_destroy(g->wc);
+
+    if (g->forescore)
       cairo_surface_destroy(g->forescore);
     if (g->forebutton)
       cairo_surface_destroy(g->forebutton);
@@ -1039,6 +136,17 @@
     if (g->foreground)
       cairo_surface_destroy(g->foreground);
     
+    if (g->curtainp)
+      cairo_pattern_destroy(g->curtainp);
+    if (g->curtains)
+      cairo_surface_destroy(g->curtains);
+
+
+    gdk_window_move_resize (widget->window, allocation->x, allocation->y, 
+			    allocation->width, allocation->height);
+    
+    g->wc = gdk_cairo_create(widget->window);
+
     g->background = 
       cairo_surface_create_similar (cairo_get_target (g->wc),
 				    CAIRO_CONTENT_COLOR, // don't need alpha
@@ -1061,9 +169,11 @@
 				    widget->allocation.width,
 				    widget->allocation.height);  
 
+    cache_curtain(g);
+
     // recenter all the verticies; doesn't require recomputation
     {
-      vertex *v=g->g->verticies;
+      vertex *v=g->g.verticies;
       int xd=(widget->allocation.width-oldw)*.5;
       int yd=(widget->allocation.height-oldh)*.5;
 
@@ -1074,21 +184,15 @@
       }
     }
 
-    // also verifies all verticies are onscreen
-    resize_board(allocation->width,allocation->height);
+    // verify all verticies are onscreen
+    check_verticies();
 
-    {
-      int flag = g->pushed_background;
-      if(flag)pop_background(g);
+    draw_buttonbar_box(g);
+    update_score(g);
+    update_full(g);
+    
+    resize_buttons(g,oldw,oldh,widget->allocation.width,widget->allocation.height);
 
-      update_background(widget);
-      paint_bottom_box(g);
-      score_update(g);
-      resize_buttons(oldw,oldh,widget->allocation.width,widget->allocation.height);
-
-      if(flag)push_background(g,g->redraw_callback);
-
-    }
   }
 }
 
@@ -1106,10 +210,10 @@
 
   widget_class->realize = gameboard_realize;
   widget_class->expose_event = gameboard_expose;
-  widget_class->size_request = size_request;
+  widget_class->size_request = gameboard_size_request;
   widget_class->size_allocate = gameboard_size_allocate;
-  widget_class->button_press_event = button_press;
-  widget_class->button_release_event = button_release;
+  widget_class->button_press_event = mouse_press;
+  widget_class->button_release_event = mouse_release;
   widget_class->motion_notify_event = mouse_motion;
 
 }
@@ -1140,93 +244,8 @@
   return gameboard_type;
 }
 
-Gameboard *gameboard_new (graph *gr) {
+Gameboard *gameboard_new (void) {
   GtkWidget *g = GTK_WIDGET (g_object_new (GAMEBOARD_TYPE, NULL));
   Gameboard *gb = GAMEBOARD (g);
-  gb->g=gr;
   return gb;
 }
-
-void gameboard_reset (Gameboard *g) {
-  g->lit_vertex=0;
-  g->grabbed_vertex=0;
-  g->delayed_background=0;
-
-  g->group_drag=0;
-  g->button_grabbed=0;
-  g->selection_grab=0;
-  g->selection_active=num_selected_verticies(g->g);
-
-  update_background(&g->w);
-  score_update(g);
-}
-
-void hide_lines(Gameboard *g){
-  g->hide_lines=1;
-  update_background(&g->w);
-}
-
-void show_lines(Gameboard *g){
-  g->hide_lines=0;
-  update_background(&g->w);
-}
-
-int get_hide_lines(Gameboard *g){
-  return g->hide_lines;
-}
-
-void show_intersections(Gameboard *g){
-  if(!g->show_intersections){
-    g->show_intersections=1;
-    update_background(&g->w);
-  }else{
-    hide_intersections(g);
-  }
-}
-
-void hide_intersections(Gameboard *g){
-  if(g->show_intersections){
-    g->show_intersections=0;
-    update_background(&g->w);
-  }
-}
-
-int selected(Gameboard *g){
-  return g->selection_active;
-}
-
-/***************** save/load gameboard the widget state we want to be persistent **************/
-
-// there are only a few things; lines, intersections
-int gameboard_write(FILE *f, Gameboard *g){
-  
-  if(g->hide_lines)
-    fprintf(f,"hide_lines 1\n");
-  if(g->show_intersections)
-    fprintf(f,"show_intersections 1\n");
-  
-  return 0;
-}
-
-int gameboard_read(FILE *f, Gameboard *g){
-  char *line=NULL;
-  size_t n=0;
-  int i;
-
-  g->hide_lines = 0;
-  g->show_intersections = 0;
-  
-  while(getline(&line,&n,f)>0){
-    if (sscanf(line,"hide_lines %d",&i)==1)
-      if(i)
-	g->hide_lines = 1;
-    
-    if (sscanf(line,"show_intersections %d",&i)==1)
-      if(i)
-	g->show_intersections = 1;
-  }
-
-  free(line);
-
-  return 0;
-}

Modified: trunk/planarity/gameboard.h
===================================================================
--- trunk/planarity/gameboard.h	2005-09-24 22:02:43 UTC (rev 10059)
+++ trunk/planarity/gameboard.h	2005-09-25 09:24:23 UTC (rev 10060)
@@ -1,10 +1,8 @@
 #include <glib.h>
 #include <glib-object.h>
-#include <gtk/gtkcontainer.h>
-#include <gtk/gtksignal.h>
-#include <gtk/gtkdrawingarea.h>
+#include <gtk/gtk.h>
+#include <gdk/gdk.h>
 
-
 #define V_RADIUS 8
 #define V_LINE 2
 #define V_LINE_COLOR         0, 0, 0
@@ -18,9 +16,25 @@
 #define SELECTBOX_COLOR     .2,.8,.8,.3
 
 #define INTERSECTION_COLOR  1,.1,.1,.8
-#define INTERSECTION_RADIUS 6
+#define INTERSECTION_RADIUS 10
 #define INTERSECTION_LINE_WIDTH 2
 
+#define RESET_DELTA 2
+
+#define B_LINE 1
+#define B_BORDER 6.5
+#define B_RADIUS 20
+#define B_HUMP 130
+#define B_COLOR         .1,.1,.7,.1
+#define B_LINE_COLOR     0, 0,.7,.3
+#define TEXT_COLOR      .0,.0,.7,.6
+#define HIGH_COLOR      .7,.0,.0,.6
+
+#define SCOREHEIGHT 50
+
+#define ICON_WIDTH  160
+#define ICON_HEIGHT 120
+
 G_BEGIN_DECLS
 
 #define GAMEBOARD_TYPE            (gameboard_get_type ())
@@ -32,11 +46,51 @@
 typedef struct _Gameboard       Gameboard;
 typedef struct _GameboardClass  GameboardClass;
 
+#define NUMBUTTONS 11
+
+typedef struct {
+  int position; // 0 inactive
+                // 1 left
+                // 2 center
+                // 3 right
+  int x_target;
+  int x;
+
+  int y_target;
+  int y_inactive;
+  int y_active;
+  int y;
+  int sweepdeploy;
+
+  int alphad;
+  double alpha;
+
+  cairo_surface_t *idle;
+  cairo_surface_t *lit;
+
+  char *rollovertext;
+  cairo_text_extents_t ex;
+
+  int rollover;
+  int press;
+
+  void (*callback)();
+} buttonstate;
+
+typedef struct {
+  buttonstate states[NUMBUTTONS];
+  int buttons_ready;
+  int allclear; // short-circuit hint
+  int sweeper;
+  int sweeperd;
+  buttonstate *grabbed;
+} buttongroup;
+
 struct _Gameboard{
   GtkWidget w;
-  graph *g;
 
-  int pushed_background;
+  graph g;
+
   int pushed_curtain;
   void (*redraw_callback)(Gameboard *g);
 
@@ -55,7 +109,16 @@
   cairo_surface_t *curtains;
 
   int delayed_background;
+  int first_expose;
+  int hide_lines;
+  int show_intersections;
+  int finish_dialog_active;
+  int about_dialog_active;
+  int pause_dialog_active;
+  int level_dialog_active;
 
+  buttongroup b;
+
   vertex *grabbed_vertex;
   vertex *lit_vertex;
   int group_drag;
@@ -75,11 +138,12 @@
   int selectionw;
   int selectionh;
 
-  int hide_lines;
+  int checkbutton_deployed;
+  int buttonbar_sweeper;
+  
+  gint gtk_timer;
+  void (*button_callback)(Gameboard *);
 
-  int first_expose;
-  int show_intersections;
-
 };
 
 struct _GameboardClass{
@@ -88,23 +152,101 @@
 };
 
 GType          gameboard_get_type        (void);
-Gameboard*     gameboard_new             (graph *g);
+Gameboard*     gameboard_new             (void);
 
 G_END_DECLS
 
-extern void main_board(int argc, char *argv[]);
-extern void run_immediate_expose(Gameboard *g,int x, int y, int w, int h);
-extern void gameboard_reset(Gameboard *g);
-extern void hide_lines(Gameboard *g);
-extern void show_lines(Gameboard *g);
-extern int get_hide_lines(Gameboard *g);
-extern void show_intersections(Gameboard *g);
-extern void hide_intersections(Gameboard *g);
-extern void invalidate_region_vertex(Gameboard *g, vertex *v);
-extern int selected(Gameboard *g);
+extern void init_buttons(Gameboard *g);
+extern buttonstate *find_button(Gameboard *g,int x,int y);
+extern void button_set_state(Gameboard *g, buttonstate *b, int rollover, int press);
+extern void rollover_extents(Gameboard *g, buttonstate *b);
+extern gboolean animate_button_frame(gpointer ptr);
+extern void expose_buttons(Gameboard *g,cairo_t *c, int x,int y,int w,int h);
+extern void resize_buttons(Gameboard *g,int oldw,int oldh,int w,int h);
+extern void button_clear_state(Gameboard *g);
+
+extern void update_push(Gameboard *g, cairo_t *c);
+extern void push_curtain(Gameboard *g,void(*redraw_callback)(Gameboard *g));
+extern void pop_curtain(Gameboard *g);
+
+extern void prepare_reenter_game(Gameboard *g);
+extern void reenter_game(Gameboard *g);
+extern void enter_game(Gameboard *g);
+extern void quit_action(Gameboard *g);
+extern void finish_action(Gameboard *g);
+extern void expand_action(Gameboard *g);
+extern void shrink_action(Gameboard *g);
+extern void pause_action(Gameboard *g);
+extern void about_action(Gameboard *g);
+extern void level_action(Gameboard *g);
+extern void set_hide_lines(Gameboard *g, int state);
+extern void toggle_hide_lines(Gameboard *g);
+extern void set_show_intersections(Gameboard *g, int state);
+extern void toggle_show_intersections(Gameboard *g);
+extern void reset_action(Gameboard *g);
+extern int gameboard_write(char *basename, Gameboard *g);
+extern int gameboard_read(char *basename, Gameboard *g);
+
+extern void topbox (Gameboard *g,cairo_t *c, double w, double h);
+extern void bottombox (Gameboard *g,cairo_t *c, double w, double h);
+extern void centerbox (cairo_t *c, int x, int y, double w, double h);
+extern void borderbox_path (cairo_t *c, double x, double y, double w, double h);
+
+extern void setup_buttonbar(Gameboard *g);
+extern void deploy_buttonbar(Gameboard *g);
+extern void deploy_check(Gameboard *g);
+extern void undeploy_check(Gameboard *g);
+
+extern void update_draw(Gameboard *g);
 extern void update_full(Gameboard *g);
-extern void pop_background(Gameboard *g);
-extern void push_background(Gameboard *g, void (*callback)(Gameboard *g));
-extern void push_curtain(Gameboard *g, void (*callback)(Gameboard *g));
-extern int gameboard_write(FILE *f, Gameboard *g);
-extern int gameboard_read(FILE *f, Gameboard *g);
+extern void expose_full(Gameboard *g);
+extern void update_full_delayed(Gameboard *g);
+extern void update_add_vertex(Gameboard *g, vertex *v);
+extern void gameboard_draw(Gameboard *g, int x, int y, int w, int h);
+extern void draw_foreground(Gameboard *g,cairo_t *c,
+			    int x,int y,int width,int height);
+extern void draw_intersections(Gameboard *b,graph *g,cairo_t *c,
+			       int x,int y,int w,int h);
+
+extern void draw_score(Gameboard *g);
+extern void update_score(Gameboard *g);
+
+extern void draw_vertex(cairo_t *c,vertex *v,cairo_surface_t *s);
+extern cairo_surface_t *cache_vertex(Gameboard *g);
+extern cairo_surface_t *cache_vertex_sel(Gameboard *g);
+extern cairo_surface_t *cache_vertex_grabbed(Gameboard *g);
+extern cairo_surface_t *cache_vertex_lit(Gameboard *g);
+extern cairo_surface_t *cache_vertex_attached(Gameboard *g);
+extern cairo_surface_t *cache_vertex_ghost(Gameboard *g);
+extern void invalidate_vertex_off(GtkWidget *widget, vertex *v, int dx, int dy);
+extern void invalidate_vertex(Gameboard *g, vertex *v);
+extern void invalidate_attached(GtkWidget *widget, vertex *v);
+extern void invalidate_edges(GtkWidget *widget, vertex *v);
+extern void draw_selection_rectangle(Gameboard *g,cairo_t *c);
+extern void invalidate_selection(GtkWidget *widget);
+extern void invalidate_verticies_selection(GtkWidget *widget);
+
+extern void cache_curtain(Gameboard *g);
+extern void draw_curtain(Gameboard *g);
+extern void draw_buttonbar_box (Gameboard *g);
+
+extern gint mouse_motion(GtkWidget *widget,GdkEventMotion *event);
+extern gboolean mouse_press (GtkWidget *widget,GdkEventButton *event);
+extern gboolean mouse_release (GtkWidget *widget,GdkEventButton *event);
+
+extern void setup_background_edge(cairo_t *c);
+extern void setup_foreground_edge(cairo_t *c);
+extern void draw_edge(cairo_t *c,edge *e);
+extern void finish_edge(cairo_t *c);
+
+extern cairo_surface_t *gameboard_read_icon(char *filename, char *ext,Gameboard *b);
+extern int gameboard_write_icon(char *filename, char *ext,Gameboard *b, graph *g,
+				int lines, int intersections);
+extern int gameboard_icon_exists(char *filename, char *ext);
+
+extern void deploy_buttons(Gameboard *g, void (*callback)(Gameboard *));
+extern void undeploy_buttons(Gameboard *g, void (*callback)(Gameboard *));
+
+extern GdkRectangle render_text_centered(cairo_t *c, char *s, int x, int y);
+extern GdkRectangle render_border_centered(cairo_t *c, char *s, int x, int y);
+extern GdkRectangle render_bordertext_centered(cairo_t *c, char *s, int x, int y);

Added: trunk/planarity/gameboard_draw_box.c
===================================================================
--- trunk/planarity/gameboard_draw_box.c	2005-09-24 22:02:43 UTC (rev 10059)
+++ trunk/planarity/gameboard_draw_box.c	2005-09-25 09:24:23 UTC (rev 10060)
@@ -0,0 +1,252 @@
+#include <math.h>
+#include <gtk/gtk.h>
+#include "graph.h"
+#include "gameboard.h"
+#include "gameboard_draw_button.h"
+
+void topbox (Gameboard *g,cairo_t *c, double w, double h){
+  
+  double x0 = B_BORDER+B_RADIUS;
+  double y0 = B_BORDER;
+
+  double x1 = B_BORDER;
+  double y1 = B_BORDER+B_RADIUS;
+
+  double x2 = B_BORDER;
+  double y2 = h-B_BORDER-B_RADIUS;
+
+  double x3 = B_BORDER+B_RADIUS;
+  double y3 = h-B_BORDER;
+
+  double x4 = w/2 - B_HUMP - B_RADIUS;
+  double y4 = h-B_BORDER;
+
+  double x5 = x4+B_RADIUS;
+  double y5 = y4;
+
+
+  double x7 = w/2 + B_HUMP + B_RADIUS;
+  double y7 = h-B_BORDER;
+
+  double x8 = w - B_BORDER -B_RADIUS;
+  double y8 = h-B_BORDER;
+
+  double x9 = w - B_BORDER;
+  double y9 = h-B_BORDER-B_RADIUS;
+
+  double x10 = w - B_BORDER;
+  double y10 = B_BORDER+B_RADIUS;
+
+  double x11 = w - B_BORDER-B_RADIUS;
+  double y11 = B_BORDER;
+
+  cairo_set_line_width(c,B_LINE);
+
+  cairo_move_to  (c, x0, y0);
+  cairo_curve_to (c, x1,y0, x1,y0, x1,y1);
+  cairo_line_to (c, x2,y2);
+  cairo_curve_to (c, x2,y3, x2,y3, x3,y3);
+  cairo_line_to (c, x4,y4);
+
+  {
+    double hh = g->g.orig_height*.5+50;
+
+    double radius = sqrt( (w*.5-x5) * (w*.5-x5) + (hh-y5) * (hh-y5));
+    double siderad = atan( (x5-w*.5)/(hh-y5));
+
+    double xx = sin(siderad+.05)*radius+w*.5;
+    double yy = -cos(siderad+.05)*radius+hh;
+    double x = sin(siderad+.1)*radius+w*.5;
+    double y = -cos(siderad+.1)*radius+hh;
+
+    cairo_curve_to (c, x4+B_RADIUS,y4, xx,yy, x,y);
+    cairo_arc(c,w*.5,hh,radius,-M_PI*.5+siderad+.1,-M_PI*.5-siderad-.1);
+
+    xx = -sin(siderad+.05)*radius+w*.5;
+    yy = -cos(siderad+.05)*radius+hh;
+    xx = -sin(siderad+.1)*radius+w*.5;
+    yy = -cos(siderad+.1)*radius+hh;
+
+    cairo_curve_to (c, xx,yy, x7-B_RADIUS,y7, x7,y7);
+
+  }
+
+  cairo_line_to  (c, x8,y8);
+  cairo_curve_to (c, x9,y8, x9,y8, x9,y9);
+  cairo_line_to  (c, x10,y10);
+  cairo_curve_to (c, x10,y11, x10,y11, x11,y11);
+  cairo_close_path (c);
+
+  cairo_set_source_rgba (c, B_COLOR);
+  cairo_fill_preserve (c);
+  cairo_set_source_rgba (c, B_LINE_COLOR);
+  cairo_stroke (c);
+
+}
+
+void bottombox (Gameboard *g, cairo_t *c, double w, double h){
+  
+  double x0 = B_BORDER+B_RADIUS;
+  double y0 = h-B_BORDER;
+
+  double x1 = B_BORDER;
+  double y1 = h-B_BORDER-B_RADIUS;
+
+  double x2 = B_BORDER;
+  double y2 = B_BORDER+B_RADIUS;
+
+  double x3 = B_BORDER+B_RADIUS;
+  double y3 = B_BORDER;
+
+  double x4 = w/2 - B_HUMP - B_RADIUS;
+  double y4 = B_BORDER;
+
+  double x5 = x4+B_RADIUS;
+  double y5 = y4;
+
+
+  double x7 = w/2 + B_HUMP + B_RADIUS;
+  double y7 = B_BORDER;
+
+  double x8 = w - B_BORDER -B_RADIUS;
+  double y8 = B_BORDER;
+
+  double x9 = w - B_BORDER;
+  double y9 = B_BORDER+B_RADIUS;
+
+  double x10 = w - B_BORDER;
+  double y10 = h- B_BORDER -B_RADIUS;
+
+  double x11 = w - B_BORDER-B_RADIUS;
+  double y11 = h-B_BORDER;
+
+  cairo_set_line_width(c,B_LINE);
+
+  cairo_move_to  (c, x0, y0);
+  cairo_curve_to (c, x1,y0, x1,y0, x1,y1);
+  cairo_line_to (c, x2,y2);
+  cairo_curve_to (c, x2,y3, x2,y3, x3,y3);
+  cairo_line_to (c, x4,y4);
+
+  {
+    double hh = g->g.orig_height*.5+50;
+
+    double radius = sqrt( (w*.5-x5) * (w*.5-x5) + (y5+hh-h) * (y5+hh-h));
+    double siderad = atan( (x5-w*.5)/(y5+hh-h));
+
+    double xx = sin(siderad+.05)*radius+w*.5;
+    double yy = cos(siderad+.05)*radius-hh+h;
+    double x = sin(siderad+.1)*radius+w*.5;
+    double y = cos(siderad+.1)*radius-hh+h;
+
+    cairo_curve_to (c, x4+B_RADIUS,y4, xx,yy, x,y);
+    cairo_arc_negative(c,w*.5,h-hh,radius,M_PI*.5-siderad-.1,M_PI*.5+siderad+.1);
+
+    xx = -sin(siderad+.05)*radius+w*.5;
+    yy = cos(siderad+.05)*radius-hh+h;
+    xx = -sin(siderad+.1)*radius+w*.5;
+    yy = cos(siderad+.1)*radius-hh+h;
+
+    cairo_curve_to (c, xx,yy, x7-B_RADIUS,y7, x7,y7);
+
+  }
+
+  cairo_line_to  (c, x8,y8);
+  cairo_curve_to (c, x9,y8, x9,y8, x9,y9);
+  cairo_line_to  (c, x10,y10);
+  cairo_curve_to (c, x10,y11, x10,y11, x11,y11);
+  cairo_close_path (c);
+
+  cairo_set_source_rgba (c, B_COLOR);
+  cairo_fill_preserve (c);
+  cairo_set_source_rgba (c, B_LINE_COLOR);
+  cairo_stroke (c);
+
+}
+
+void centerbox (cairo_t *c, int x, int y, double w, double h){
+  
+  double x0 = B_BORDER+B_RADIUS;
+  double y0 = h-B_BORDER;
+
+  double x1 = B_BORDER;
+  double y1 = h-B_BORDER-B_RADIUS;
+
+  double x2 = B_BORDER;
+  double y2 = B_BORDER+B_RADIUS;
+
+  double x3 = B_BORDER+B_RADIUS;
+  double y3 = B_BORDER;
+
+  double x8 = w - B_BORDER -B_RADIUS;
+  double y8 = B_BORDER;
+
+  double x9 = w - B_BORDER;
+  double y9 = B_BORDER+B_RADIUS;
+
+  double x10 = w - B_BORDER;
+  double y10 = h- B_BORDER -B_RADIUS;
+
+  double x11 = w - B_BORDER-B_RADIUS;
+  double y11 = h-B_BORDER;
+
+  cairo_save(c);
+  cairo_translate(c,x,y);
+  cairo_set_line_width(c,B_LINE);
+
+  cairo_move_to  (c, x0, y0);
+  cairo_curve_to (c, x1,y0, x1,y0, x1,y1);
+  cairo_line_to (c, x2,y2);
+  cairo_curve_to (c, x2,y3, x2,y3, x3,y3);
+  cairo_line_to (c, x8,y8);
+  cairo_curve_to (c, x9,y8, x9,y8, x9,y9);
+  cairo_line_to  (c, x10,y10);
+  cairo_curve_to (c, x10,y11, x10,y11, x11,y11);
+  cairo_close_path (c);
+
+  cairo_set_source_rgba (c, B_COLOR);
+  cairo_fill_preserve (c);
+  cairo_set_source_rgba (c, B_LINE_COLOR);
+  cairo_stroke (c);
+
+  cairo_restore(c);
+}
+
+void borderbox_path (cairo_t *c, double x, double y, double w, double h){
+  
+  double x0 = x+ B_RADIUS;
+  double y0 = y+ h;
+
+  double x1 = x;
+  double y1 = y+ h-B_RADIUS;
+
+  double x2 = x;
+  double y2 = y+ B_RADIUS;
+
+  double x3 = x+ B_RADIUS;
+  double y3 = y;
+
+  double x8 = x+ w -B_RADIUS;
+  double y8 = y;
+
+  double x9 = x+ w;
+  double y9 = y+ B_RADIUS;
+
+  double x10 = x+ w;
+  double y10 = y+ h -B_RADIUS;
+
+  double x11 = x+ w -B_RADIUS;
+  double y11 = y+h;
+
+  cairo_move_to  (c, x0, y0);
+  cairo_curve_to (c, x1,y0, x1,y0, x1,y1);
+  cairo_line_to (c, x2,y2);
+  cairo_curve_to (c, x2,y3, x2,y3, x3,y3);
+  cairo_line_to (c, x8,y8);
+  cairo_curve_to (c, x9,y8, x9,y8, x9,y9);
+  cairo_line_to  (c, x10,y10);
+  cairo_curve_to (c, x10,y11, x10,y11, x11,y11);
+  cairo_close_path (c);
+
+}
+

Added: trunk/planarity/gameboard_draw_button.c
===================================================================
--- trunk/planarity/gameboard_draw_button.c	2005-09-24 22:02:43 UTC (rev 10059)
+++ trunk/planarity/gameboard_draw_button.c	2005-09-25 09:24:23 UTC (rev 10060)
@@ -0,0 +1,273 @@
+#include <math.h>
+#include <gtk/gtk.h>
+
+#include "graph.h"
+#include "gameboard.h"
+#include "gameboard_draw_button.h"
+
+
+/************************* simple round icon drawing *********************/
+
+void path_button_help(cairo_t *c, double x, double y){
+  
+  cairo_save(c);
+  cairo_translate(c,x,y);
+  cairo_set_fill_rule(c,CAIRO_FILL_RULE_EVEN_ODD);
+  cairo_set_line_width(c,1);
+  cairo_arc(c,0,0,14,0,2*M_PI);
+
+  cairo_move_to(c,-9,-2);
+  cairo_curve_to(c,-8,-7, -4.5,-12, 0,-12);
+  cairo_curve_to(c, 7,-12,  9,-6, 9,-4);
+  cairo_curve_to(c, 9,0 ,6,2, 5,2);
+
+  cairo_curve_to(c, 4,2, 2.5,3, 2.5,6);
+  cairo_line_to(c,-2.5,6);
+
+  cairo_curve_to(c,-2.5,3, -3,1, 0,-1);
+  cairo_curve_to(c, 11,-4, -3,-12, -4,-2);
+  cairo_close_path(c);
+
+  cairo_move_to(c,-2.5,8);
+  cairo_line_to(c,2.5,8);
+  cairo_line_to(c,2.5,12);
+  cairo_line_to(c,-2.5,12);
+  cairo_close_path(c);
+  cairo_fill_preserve(c);
+
+  cairo_restore(c);
+
+}
+
+void path_button_pause(cairo_t *c, double x, double y){
+  
+  cairo_save(c);
+  cairo_translate(c,x,y);
+  cairo_set_fill_rule(c,CAIRO_FILL_RULE_EVEN_ODD);
+  cairo_set_line_width(c,1);
+  cairo_arc(c,0,0,14,0,2*M_PI);
+
+  cairo_rectangle(c,-7,-9,5,18);
+  cairo_rectangle(c,2,-9,5,18);
+  cairo_fill_preserve(c);
+  cairo_restore(c);
+
+}
+
+void path_button_exit(cairo_t *c, double x, double y){
+  
+  cairo_save(c);
+  cairo_translate(c,x,y);
+  cairo_set_fill_rule(c,CAIRO_FILL_RULE_EVEN_ODD);
+  cairo_set_line_width(c,1);
+  cairo_arc(c,0,0,14,0,2*M_PI);
+
+  cairo_move_to(c,-6,-9.5);
+  cairo_line_to(c,0,-3.5);
+  cairo_line_to(c,6,-9.5);
+  cairo_line_to(c,9.5,-6);
+  cairo_line_to(c,3.5,0);
+  cairo_line_to(c,9.5,6);
+  cairo_line_to(c,6,9.5);
+  cairo_line_to(c,0,3.5);
+  cairo_line_to(c,-6,9.5);
+  cairo_line_to(c,-9.5,6);
+  cairo_line_to(c,-3.5,0);
+  cairo_line_to(c,-9.5,-6);
+  cairo_close_path(c);
+
+  cairo_fill_preserve(c);
+  cairo_restore(c);
+
+}
+
+void path_button_back(cairo_t *c, double x, double y){
+  
+  cairo_save(c);
+  cairo_translate(c,x,y);
+  cairo_set_fill_rule(c,CAIRO_FILL_RULE_EVEN_ODD);
+  cairo_set_line_width(c,1);
+
+  cairo_arc(c,0,0,14,0,2*M_PI);
+
+  cairo_move_to(c,0,-11);
+  cairo_line_to(c,-9,0);
+  cairo_line_to(c,-3,0);
+  cairo_line_to(c,-3,10);
+  cairo_line_to(c,3,10);
+  cairo_line_to(c,3,0);
+  cairo_line_to(c,9,0);
+  cairo_close_path(c);
+
+  cairo_fill_preserve(c);
+  cairo_restore(c);
+
+}
+
+void path_button_reset(cairo_t *c, double x, double y){
+  
+  cairo_save(c);
+  cairo_translate(c,x,y);
+  cairo_set_fill_rule(c,CAIRO_FILL_RULE_EVEN_ODD);
+  cairo_set_line_width(c,1);
+
+  cairo_arc(c,0,0,14,0,2*M_PI);
+
+  cairo_move_to(c,-11,-5);
+  cairo_line_to(c,-12,1);
+  cairo_line_to(c,-6,1);
+  cairo_line_to(c,0, 11);
+  cairo_line_to(c,6,1);
+  cairo_line_to(c,12,1);
+  cairo_line_to(c,11,-5);
+  cairo_line_to(c,4,-5);
+  cairo_line_to(c,0,2);
+  cairo_line_to(c,-4,-5);
+  cairo_close_path(c);
+
+  cairo_fill_preserve(c);
+  cairo_restore(c);
+
+}
+
+void path_button_expand(cairo_t *c, double x, double y){
+  
+  cairo_save(c);
+  cairo_translate(c,x,y);
+  cairo_set_fill_rule(c,CAIRO_FILL_RULE_EVEN_ODD);
+  cairo_set_line_width(c,1);
+
+  cairo_arc(c,0,0,14,0,2*M_PI);
+
+  cairo_move_to(c,-8.5,-3);
+  cairo_line_to(c,-2.5,-3);
+  cairo_line_to(c,-3,-8.5);
+  cairo_line_to(c,3, -8.5);
+  cairo_line_to(c,3,-3);
+  cairo_line_to(c,8.5,-3);
+  cairo_line_to(c,8.5,3);
+  cairo_line_to(c,3,3);
+  cairo_line_to(c,3,8.5);
+  cairo_line_to(c,-3,8.5);
+  cairo_line_to(c,-3,3);
+  cairo_line_to(c,-8.5,3);
+  cairo_close_path(c);
+
+  cairo_fill_preserve(c);
+  cairo_restore(c);
+
+}
+
+void path_button_shrink(cairo_t *c, double x, double y){
+  
+  cairo_save(c);
+  cairo_translate(c,x,y);
+  cairo_set_fill_rule(c,CAIRO_FILL_RULE_EVEN_ODD);
+  cairo_set_line_width(c,1);
+
+  cairo_arc(c,0,0,14,0,2*M_PI);
+
+  cairo_move_to(c,-10,-3);
+  cairo_line_to(c,10,-3);
+  cairo_line_to(c,10,3);
+  cairo_line_to(c,-10,3);
+  cairo_close_path(c);
+
+  cairo_fill_preserve(c);
+  cairo_restore(c);
+
+}
+
+void path_button_lines(cairo_t *c, double x, double y){
+  
+  cairo_save(c);
+  cairo_translate(c,x,y);
+  cairo_set_fill_rule(c,CAIRO_FILL_RULE_EVEN_ODD);
+  cairo_set_line_width(c,1);
+
+  cairo_arc(c,0,0,14,0,2*M_PI);
+
+  cairo_move_to(c,6,-4);
+  cairo_arc(c,0,-4,6,0,2*M_PI);
+
+  cairo_move_to(c,0,2);
+  cairo_line_to(c,0,10);
+  cairo_close_path(c);
+
+  cairo_move_to(c,2.68328,5.36656-4);
+  cairo_rel_line_to(c,4,8);
+  cairo_close_path(c);
+
+  cairo_move_to(c,-2.68328,5.36656-4);
+  cairo_rel_line_to(c,-4,8);
+  cairo_close_path(c);
+
+  cairo_fill_preserve(c);
+  cairo_restore(c);
+
+}
+
+void path_button_int(cairo_t *c, double x, double y){
+  
+  cairo_save(c);
+  cairo_translate(c,x,y);
+  cairo_set_fill_rule(c,CAIRO_FILL_RULE_EVEN_ODD);
+  cairo_set_line_width(c,1);
+
+  cairo_arc(c,0,0,14,0,2*M_PI);
+
+  cairo_move_to(c,8,0);
+  cairo_line_to(c,0,8);
+  cairo_line_to(c,-8,0);
+  cairo_line_to(c,0,-8);
+
+  cairo_close_path(c);
+ 
+  cairo_fill_preserve(c);
+  cairo_restore(c);
+
+}
+
+void path_button_check(cairo_t *c, double x, double y){
+  
+  cairo_save(c);
+  cairo_translate(c,x,y);
+  cairo_set_fill_rule(c,CAIRO_FILL_RULE_EVEN_ODD);
+  cairo_set_line_width(c,1);
+
+  cairo_arc(c,0,0,14,0,2*M_PI);
+
+  cairo_move_to(c,8,-8);
+  cairo_curve_to(c, 7,-7, 11,-7.3, 10,-6);
+  cairo_line_to(c,0,9);
+  cairo_curve_to(c, -1,9.1, -2,11, -3,10);
+  cairo_line_to(c,-11,4);
+  cairo_curve_to(c, -12,3, -10,.5, -9,1);
+  cairo_line_to(c,-3,3);
+
+  cairo_close_path(c);
+ 
+  cairo_fill_preserve(c);
+  cairo_restore(c);
+
+}
+
+void path_button_play(cairo_t *c, double x, double y){
+  
+  cairo_save(c);
+  cairo_translate(c,x,y);
+  cairo_set_fill_rule(c,CAIRO_FILL_RULE_EVEN_ODD);
+  cairo_set_line_width(c,1);
+
+  cairo_arc(c,0,0,14,0,2*M_PI);
+
+  cairo_move_to(c,-8,-8);
+  cairo_line_to(c,10,0);
+  cairo_line_to(c,-8,8);
+  cairo_close_path(c);
+ 
+  cairo_fill_preserve(c);
+  cairo_restore(c);
+
+}
+

Added: trunk/planarity/gameboard_draw_button.h
===================================================================
--- trunk/planarity/gameboard_draw_button.h	2005-09-24 22:02:43 UTC (rev 10059)
+++ trunk/planarity/gameboard_draw_button.h	2005-09-25 09:24:23 UTC (rev 10060)
@@ -0,0 +1,46 @@
+extern void path_button_help(cairo_t *c, double x, double y);
+extern void path_button_back(cairo_t *c, double x, double y);
+extern void path_button_reset(cairo_t *c, double x, double y);
+extern void path_button_pause(cairo_t *c, double x, double y);
+extern void path_button_exit(cairo_t *c, double x, double y);
+extern void path_button_expand(cairo_t *c, double x, double y);
+extern void path_button_shrink(cairo_t *c, double x, double y);
+extern void path_button_lines(cairo_t *c, double x, double y);
+extern void path_button_int(cairo_t *c, double x, double y);
+extern void path_button_check(cairo_t *c, double x, double y);
+extern void path_button_play(cairo_t *c, double x, double y);
+ 
+
+#define BUTTON_QUIT_IDLE_FILL   .7,.1,.1,.3
+#define BUTTON_QUIT_IDLE_PATH   .7,.1,.1,.6
+
+#define BUTTON_QUIT_LIT_FILL    .7,.1,.1,.5
+#define BUTTON_QUIT_LIT_PATH    .7,.1,.1,.6
+
+#define BUTTON_IDLE_FILL        .1,.1,.7,.3
+#define BUTTON_IDLE_PATH        .1,.1,.7,.6
+
+#define BUTTON_LIT_FILL         .1,.1,.7,.6
+#define BUTTON_LIT_PATH         .1,.1,.7,.6
+
+#define BUTTON_CHECK_IDLE_FILL  .1,.5,.1,.3
+#define BUTTON_CHECK_IDLE_PATH  .1,.5,.1,.6
+
+#define BUTTON_CHECK_LIT_FILL   .1,.5,.1,.6
+#define BUTTON_CHECK_LIT_PATH   .1,.5,.1,.6
+
+#define BUTTON_RADIUS 14
+#define BUTTON_Y_FROM_BOTTOM 25
+#define BUTTON_LINE_WIDTH 1
+#define BUTTON_TEXT_BORDER 15
+#define BUTTON_TEXT_COLOR       .1,.1,.7,.8
+#define BUTTON_TEXT_SIZE  15.,18.
+
+#define BUTTON_ANIM_INTERVAL 15
+#define BUTTON_LEFT 5
+#define BUTTON_RIGHT 5
+#define BUTTON_BORDER 35
+#define BUTTON_SPACING 35
+#define BUTTON_EXPOSE  50
+#define DEPLOY_DELTA 6
+#define SWEEP_DELTA 3

Added: trunk/planarity/gameboard_draw_buttonbar.c
===================================================================
--- trunk/planarity/gameboard_draw_buttonbar.c	2005-09-24 22:02:43 UTC (rev 10059)
+++ trunk/planarity/gameboard_draw_buttonbar.c	2005-09-25 09:24:23 UTC (rev 10060)
@@ -0,0 +1,21 @@
+#include <math.h>
+#include <string.h>
+#include <gtk/gtk.h>
+#include <gdk/gdk.h>
+
+#include "graph.h"
+#include "gameboard.h"
+
+void draw_buttonbar_box (Gameboard *g){
+  cairo_t *c = cairo_create(g->forebutton);
+  
+  cairo_save(c);
+  cairo_set_operator(c,CAIRO_OPERATOR_CLEAR);
+  cairo_set_source_rgba (c, 1,1,1,1);
+  cairo_paint(c);
+  cairo_restore(c);
+
+  bottombox(g,c,g->g.width,SCOREHEIGHT);
+  cairo_destroy(c);
+}
+

Added: trunk/planarity/gameboard_draw_curtain.c
===================================================================
--- trunk/planarity/gameboard_draw_curtain.c	2005-09-24 22:02:43 UTC (rev 10059)
+++ trunk/planarity/gameboard_draw_curtain.c	2005-09-25 09:24:23 UTC (rev 10060)
@@ -0,0 +1,55 @@
+#define _GNU_SOURCE
+#include <gtk/gtk.h>
+#include <gtk/gtkmain.h>
+#include <gdk/gdk.h>
+#include <gdk/gdkx.h>
+#include <stdlib.h>
+#include <math.h>
+#include <string.h>
+
+#include "graph.h"
+#include "gameboard.h"
+
+/* cache the curtain surface/pattern ******************************/
+
+#define CW 4
+void cache_curtain(Gameboard *g){
+  int x,y;
+  cairo_t *c;
+  g->curtains=
+    cairo_surface_create_similar (cairo_get_target (g->wc),
+				  CAIRO_CONTENT_COLOR_ALPHA,
+				  CW,CW);
+  
+  c = cairo_create(g->curtains);
+  cairo_save(c);
+  cairo_set_operator(c,CAIRO_OPERATOR_CLEAR);
+  cairo_set_source_rgba (c, 1,1,1,1);
+  cairo_paint(c);
+  cairo_restore(c);
+      
+  cairo_set_line_width(c,1);
+  cairo_set_source_rgba (c, 0,0,0,.5);
+  
+  for(y=0;y<CW;y++){
+    for(x=y&1;x<CW;x+=2){
+      cairo_move_to(c,x+.5,y);
+      cairo_rel_line_to(c,0,1);
+    }
+  }
+  cairo_stroke(c);
+  cairo_destroy(c);
+  
+  g->curtainp=cairo_pattern_create_for_surface (g->curtains);
+  cairo_pattern_set_extend (g->curtainp, CAIRO_EXTEND_REPEAT);
+
+}
+
+void draw_curtain(Gameboard *g){
+  
+  cairo_t *c = cairo_create(g->background);
+  
+  cairo_set_source (c, g->curtainp);
+  cairo_paint(c);
+  cairo_destroy(c);
+}  

Added: trunk/planarity/gameboard_draw_edge.c
===================================================================
--- trunk/planarity/gameboard_draw_edge.c	2005-09-24 22:02:43 UTC (rev 10059)
+++ trunk/planarity/gameboard_draw_edge.c	2005-09-25 09:24:23 UTC (rev 10060)
@@ -0,0 +1,53 @@
+#define _GNU_SOURCE
+#include <gtk/gtk.h>
+#include <gtk/gtkmain.h>
+#include <gdk/gdk.h>
+#include <gdk/gdkx.h>
+#include <stdlib.h>
+#include <math.h>
+#include <string.h>
+
+#include "graph.h"
+#include "gameboard.h"
+
+
+/******** draw edges ********************************************/
+
+void setup_background_edge(cairo_t *c){
+  cairo_set_line_width(c,E_LINE);
+  cairo_set_source_rgba(c,E_LINE_B_COLOR);
+}
+
+void setup_foreground_edge(cairo_t *c){
+  cairo_set_line_width(c,E_LINE);
+  cairo_set_source_rgba(c,E_LINE_F_COLOR);
+}
+
+void draw_edge(cairo_t *c,edge *e){
+  cairo_move_to(c,e->A->x,e->A->y);
+  cairo_line_to(c,e->B->x,e->B->y);
+}
+
+void finish_edge(cairo_t *c){
+  cairo_stroke(c);
+}
+
+/* invalidate edge region for efficient expose *******************/
+
+void invalidate_edges(GtkWidget *widget, vertex *v){
+  GdkRectangle r;
+  
+  if(v){
+    edge_list *el=v->edges;
+    while (el){
+      edge *e=el->edge;
+      r.x = min(e->A->x,e->B->x) - E_LINE;
+      r.y = min(e->A->y,e->B->y) - E_LINE;
+      r.width  = labs(e->B->x - e->A->x) + 1 + E_LINE*2;
+      r.height = labs(e->B->y - e->A->y) + 1 + E_LINE*2;
+      gdk_window_invalidate_rect (widget->window, &r, FALSE);
+      el=el->next;
+    }
+  }
+}
+

Added: trunk/planarity/gameboard_draw_intersection.c
===================================================================
--- trunk/planarity/gameboard_draw_intersection.c	2005-09-24 22:02:43 UTC (rev 10059)
+++ trunk/planarity/gameboard_draw_intersection.c	2005-09-25 09:24:23 UTC (rev 10060)
@@ -0,0 +1,82 @@
+#define _GNU_SOURCE
+#include <gtk/gtk.h>
+#include <gtk/gtkmain.h>
+#include <gdk/gdk.h>
+#include <gdk/gdkx.h>
+#include <stdlib.h>
+#include <math.h>
+#include <string.h>
+
+#include "graph.h"
+#include "gameboard.h"
+
+static void draw_intersection(cairo_t *c,double x, double y){
+  cairo_move_to(c,x-INTERSECTION_RADIUS,y);
+  cairo_rel_line_to(c,INTERSECTION_RADIUS,-INTERSECTION_RADIUS);
+  cairo_rel_line_to(c,INTERSECTION_RADIUS,INTERSECTION_RADIUS);
+  cairo_rel_line_to(c,-INTERSECTION_RADIUS,INTERSECTION_RADIUS);
+  cairo_close_path(c);
+}
+
+static void draw_many_intersection(Gameboard *g,cairo_t *c){
+  cairo_matrix_t ma;
+  int x2 = g->g.width/2;
+  int y2 = g->g.height/2;
+  int r = INTERSECTION_RADIUS*10;
+
+  cairo_set_source_rgba (c, INTERSECTION_COLOR);
+  cairo_set_line_width(c, INTERSECTION_LINE_WIDTH*10);
+
+  cairo_move_to(c,x2-r,y2-r/4);
+  cairo_rel_line_to(c,r,-r);
+  cairo_rel_line_to(c,r,r);
+  cairo_rel_line_to(c,-r,r);
+  cairo_close_path(c);
+
+  cairo_stroke(c);
+
+  cairo_select_font_face (c, "Sans",
+			  CAIRO_FONT_SLANT_NORMAL,
+			  CAIRO_FONT_WEIGHT_BOLD);
+  
+  cairo_matrix_init_scale (&ma, 30.,34.);
+  cairo_set_font_matrix (c,&ma);
+  render_bordertext_centered(c,"rather many, really",x2,y2+r/4);
+
+}
+
+void draw_intersections(Gameboard *b, graph *g, cairo_t *c,
+			int x,int y,int w,int h){
+  
+  double xx=x-(INTERSECTION_LINE_WIDTH*.5 + INTERSECTION_RADIUS);
+  double x2=w+x+(INTERSECTION_LINE_WIDTH + INTERSECTION_RADIUS*2);
+  double yy=y-(INTERSECTION_LINE_WIDTH*.5 + INTERSECTION_RADIUS);
+  double y2=h+y+(INTERSECTION_LINE_WIDTH + INTERSECTION_RADIUS*2);
+  
+  if(g->active_intersections > 1000){
+    // don't draw them all; potential for accidental CPU sink.
+    draw_many_intersection(b,c);
+  }else{
+    cairo_set_source_rgba (c, INTERSECTION_COLOR);
+    cairo_set_line_width(c, INTERSECTION_LINE_WIDTH);
+    
+    // walk the intersection list of all edges; draw if paired is a higher pointer
+    edge *e=g->edges;
+    while(e){
+      intersection *i = e->i.next;
+      while(i){
+	if(i->paired > i){
+	  double ix=i->x, iy=i->y;
+	  
+	  if(ix >= xx && ix <= x2 && iy >= yy && iy <= y2)
+	    draw_intersection(c,ix,iy);
+	}
+	cairo_stroke(c); // incremental stroke!  It's easy to run
+	// the path longer than the X server
+	// allows.
+	i=i->next;
+      }
+      e=e->next;
+    }
+  }
+}

Added: trunk/planarity/gameboard_draw_main.c
===================================================================
--- trunk/planarity/gameboard_draw_main.c	2005-09-24 22:02:43 UTC (rev 10059)
+++ trunk/planarity/gameboard_draw_main.c	2005-09-25 09:24:23 UTC (rev 10060)
@@ -0,0 +1,366 @@
+#define _GNU_SOURCE
+#include <gtk/gtk.h>
+#include <gtk/gtkmain.h>
+#include <gdk/gdk.h>
+#include <gdk/gdkx.h>
+#include <stdlib.h>
+#include <math.h>
+#include <string.h>
+#include <stdio.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <unistd.h>
+
+#include "graph.h"
+#include "gameboard.h"
+#include "dialog_level.h"
+#include "main.h"
+
+/* The gPlanarity gameboard consists of a background and a foreground.
+   The background is a predrawn backdrop of anything not currently
+   being animated.  The foreground is 'active' elements of the UI and
+   anything that floats above them.  There are also two translucent
+   bars, top and bottom, that hold the score and buttonbar
+   respectively.  These are prerendered surfaces that are blitted onto
+   the foreground during exposes */
+
+/* the midground is a group of elements that may currently be animated
+   (thus rendered to the foreground) or not (thus rendered to the
+   background) */
+static void draw_midground(Gameboard *g,cairo_t *c,int x,int y,int w,int h){
+
+  /* Edges attached to the grabbed vertex are drawn here */
+
+  if(g->grabbed_vertex && !g->hide_lines){
+    edge_list *el=g->grabbed_vertex->edges;
+    setup_foreground_edge(c);
+    while(el){
+      edge *e=el->edge;
+      /* no need to check rectangle; if they're to be drawn, they'll
+	 always be in the rect */
+      draw_edge(c,e);
+      el=el->next;
+    }
+    finish_edge(c);
+  }
+  
+  /* verticies drawn over the edges */
+  {
+    vertex *v = g->g.verticies;
+    int clipx = x-V_RADIUS;
+    int clipw = x+w+V_RADIUS;
+    int clipy = y-V_RADIUS;
+    int cliph = y+h+V_RADIUS;
+    
+    while(v){
+
+      /* is the vertex in the expose rectangle? */
+      if(v->x>=clipx && v->x<=clipw &&
+	 v->y>=clipy && v->y<=cliph){
+	
+	if(v == g->grabbed_vertex && !g->group_drag) {
+	  draw_vertex(c,v,g->vertex_grabbed);      
+	} else if( v->selected ){
+	  draw_vertex(c,v,g->vertex_sel);
+	} else if ( v == g->lit_vertex){
+	  draw_vertex(c,v,g->vertex_lit);
+	} else if (v->attached_to_grabbed && !g->group_drag){
+	  draw_vertex(c,v,g->vertex_attached);
+	}else
+	  draw_vertex(c,v,g->vertex);
+      }
+      
+      v=v->next;
+    }
+  }
+}
+
+static void draw_background(Gameboard *g,cairo_t *c){
+  int width=g->g.width;
+  int height=g->g.height;
+  edge *e=g->g.edges;
+
+  cairo_set_source_rgb(c,1,1,1);
+  cairo_paint(c);
+    
+  if(!g->hide_lines){
+    setup_background_edge(c);
+    while(e){
+      if(e->active){
+	draw_edge(c,e);
+      }
+      e=e->next;
+    }
+    finish_edge(c);
+  }
+
+  // if there's a a group drag in progress, midground is drawn here
+  if(g->group_drag)
+    draw_midground(g,c,0,0,width,height);
+
+}
+
+/* This draws the nominal foreground, that is, the elements that are
+   always foreground except when a 'background push' (dialog and
+   curtain over the board) is in place. */
+void draw_foreground(Gameboard *g,cairo_t *c,
+		     int x,int y,int width,int height){
+  
+  /* if a group drag is in progress, draw the group 
+     ghosted in the foreground */
+
+  if(g->group_drag){ 
+    vertex *v = g->g.verticies;
+    while(v){
+      
+      if( v->selected ){
+	vertex tv;
+	tv.x=v->x+g->dragx;
+	tv.y=v->y+g->dragy;
+	draw_vertex(c,&tv,g->vertex_ghost);
+      }
+
+      v=v->next;
+    }
+  }else
+    draw_midground(g,c,x,y,width,height);    
+
+  if(g->selection_grab)
+    draw_selection_rectangle(g,c);
+}
+
+/* Several very-high-level drawing sequences triggered by various
+   actions in the game *********************************************/
+
+/* request that the background plane be redrawn from scratch to match
+   current game state and the full window invalidated to request an
+   expose.  As other layers are always drawn on the fly, this is
+   effectively a full refresh of the screen.
+   This is done as sparingly as possible and only within large state
+   changes as it's not fast enough for animation. */
+
+void update_full(Gameboard *g){
+  cairo_t *c = cairo_create(g->background);
+
+  g->delayed_background=0;
+
+  // render the far background plane
+  draw_background(g,c);
+  update_push(g,c);
+  cairo_destroy(c);
+  expose_full(g);
+}
+
+void expose_full(Gameboard *g){
+  GtkWidget *widget = GTK_WIDGET(g);
+  GdkRectangle r;
+  
+  r.x=0;
+  r.y=0;
+  r.width=g->g.width;
+  r.height=g->g.height;
+
+  gdk_window_invalidate_rect (widget->window, &r, FALSE);
+    
+}
+
+void update_draw(Gameboard *g){
+  cairo_t *c = cairo_create(g->background);
+
+  g->delayed_background=0;
+
+  // render the far background plane
+  draw_background(g,c);
+  update_push(g,c);
+  cairo_destroy(c);
+  
+  gameboard_draw(g,0,0,g->g.width,g->g.height);
+}
+
+/* request a full redraw update to be processed immediately *after*
+   the next expose.  This is used to increase apparent interactivity
+   of a vertex 'grab' where the background must be redrawn, but the
+   vertex itself should react immediately */
+void update_full_delayed(Gameboard *g){
+  g->delayed_background=1;
+}
+
+/* specialized update request for when re-adding a grabbed vertex's
+   edges to the background.  Doesn't require a full redraw. */
+void update_add_vertex(Gameboard *g, vertex *v){
+  GtkWidget *widget = GTK_WIDGET(g);
+  edge_list *el=v->edges;
+  cairo_t *c = cairo_create(g->background);
+
+  if(!g->hide_lines){
+    setup_background_edge(c);
+    while(el){
+      edge *e=el->edge;
+      
+      if(e->active)
+	draw_edge(c,e);
+      
+      el=el->next;
+    }
+    finish_edge(c);
+  }
+  cairo_destroy(c);
+
+  invalidate_attached(widget,v);    
+}
+
+/* top level draw function called by expose.  May also be called
+   directly to immediately render a region of the board without going
+   through the expose mechanism (necessary for some button draw
+   operations where expose combining causes huge, slow in-server alpha
+   blends that are undesirable) */
+void gameboard_draw(Gameboard *g, int x, int y, int w, int h){
+  
+  if (w==0 || h==0) return;
+  
+  cairo_t *c = cairo_create(g->foreground);
+  
+  // copy background to foreground draw buffer
+  cairo_set_source_surface(c,g->background,0,0);
+  cairo_rectangle(c,x,y,w,h);
+  cairo_fill(c);
+
+  if(!g->pushed_curtain){
+    draw_foreground(g,c,x,y,w,h);
+    
+    // copy in any of the score or button surfaces?
+    if(y<SCOREHEIGHT){
+      cairo_set_source_surface(c,g->forescore,0,0);
+      cairo_rectangle(c, x,y,w,
+		      min(SCOREHEIGHT-y,h));
+      cairo_fill(c);
+    }
+    if(y+h>g->w.allocation.height-SCOREHEIGHT){
+      cairo_set_source_surface(c,g->forebutton,0,
+			       g->w.allocation.height-SCOREHEIGHT);
+      cairo_rectangle(c, x,y,w,h);
+      cairo_fill(c);
+    }
+    
+    if(g->show_intersections)
+      draw_intersections(g,&g->g,c,x,y,w,h);
+  }
+  
+  render_level_icons(g,c,x,y,w,h);
+  expose_buttons(g,c,x,y,w,h);
+
+  cairo_destroy(c);
+  
+  // blit to window
+  cairo_set_source_surface(g->wc,g->foreground,0,0);
+  cairo_rectangle(g->wc,x,y,w,h);
+  cairo_fill(g->wc);
+
+  if(g->delayed_background)update_full(g);
+  g->first_expose=1;
+
+}
+
+cairo_surface_t *gameboard_read_icon(char *filename, char *ext, Gameboard *b){
+  char *name=alloca(strlen(boarddir)+strlen(filename)+strlen(ext)+2);
+  name[0]=0;
+  strcat(name,boarddir);
+  strcat(name,filename);
+  strcat(name,".");
+  strcat(name,ext);
+  
+  cairo_surface_t *s = cairo_image_surface_create_from_png(name);
+
+  if(s==NULL)
+    fprintf(stderr,"ERROR:  Could not load board icon \"%s\"\n",
+	    name);
+
+  cairo_t *c = cairo_create(s);
+  borderbox_path(c,1.5,1.5,ICON_WIDTH-3,ICON_HEIGHT-3);
+  cairo_set_operator (c, CAIRO_OPERATOR_DEST_OVER);
+  cairo_set_line_width(c,B_LINE);
+  //cairo_set_source_rgba (c, B_COLOR);
+  //cairo_fill_preserve (c);
+  cairo_set_source_rgba (c, B_LINE_COLOR);
+  cairo_stroke (c);
+
+  cairo_destroy(c);
+  return s;
+}
+
+int gameboard_write_icon(char *filename, char *ext, Gameboard *b, graph *g,
+			 int lines, int intersections){
+  char *name=alloca(strlen(boarddir)+strlen(filename)+strlen(ext)+2);
+  name[0]=0;
+  strcat(name,boarddir);
+  strcat(name,filename);
+  strcat(name,".");
+  strcat(name,ext);
+  
+  {
+    cairo_surface_t *s = 
+      cairo_image_surface_create (CAIRO_FORMAT_ARGB32,
+				  ICON_WIDTH,
+				  ICON_HEIGHT);
+    cairo_t *c = cairo_create(s);
+    edge *e=g->edges;
+    vertex *v = g->verticies;
+      
+    cairo_save(c);
+    cairo_set_operator(c,CAIRO_OPERATOR_CLEAR);
+    cairo_set_source_rgba (c, 1,1,1,1);
+    cairo_paint(c);
+    cairo_restore(c);
+    cairo_scale(c,(float)ICON_WIDTH/g->width,(float)ICON_HEIGHT/g->height);
+
+    if(lines){
+      setup_background_edge(c);
+      while(e){
+	draw_edge(c,e);
+	e=e->next;
+      }
+      finish_edge(c);
+    }
+
+    cairo_set_line_width(c,V_LINE);
+    while(v){
+      // dirty, but we may not have the cached vertex surface yet.
+      cairo_arc(c,v->x,v->y,V_RADIUS,0,2*M_PI);
+      cairo_set_source_rgb(c,V_FILL_IDLE_COLOR);
+      cairo_fill_preserve(c);
+      cairo_set_source_rgb(c,V_LINE_COLOR);
+      cairo_stroke(c);
+
+      v=v->next;
+    }
+
+    if(intersections)
+      draw_intersections(b,g,c,0,0,g->width,g->height);
+
+    if(cairo_surface_write_to_png(s,name) != CAIRO_STATUS_SUCCESS){
+      fprintf(stderr,"ERROR:  Could not save board icon \"%s\"\n",
+	      name);
+      return -1;
+    }
+
+    cairo_destroy(c);
+    cairo_surface_destroy(s);
+  }
+
+  return 0;
+
+}
+
+int gameboard_icon_exists(char *filename, char *ext){
+  struct stat s;
+  char *name=alloca(strlen(boarddir)+strlen(filename)+strlen(ext)+2);
+  name[0]=0;
+  strcat(name,boarddir);
+  strcat(name,filename);
+  strcat(name,".");
+  strcat(name,ext);
+
+  if(stat(name,&s))return 0;
+  return 1;
+
+}

Added: trunk/planarity/gameboard_draw_score.c
===================================================================
--- trunk/planarity/gameboard_draw_score.c	2005-09-24 22:02:43 UTC (rev 10059)
+++ trunk/planarity/gameboard_draw_score.c	2005-09-25 09:24:23 UTC (rev 10060)
@@ -0,0 +1,88 @@
+#define _GNU_SOURCE
+#include <gtk/gtk.h>
+#include <gtk/gtkmain.h>
+#include <gdk/gdk.h>
+#include <gdk/gdkx.h>
+#include <stdlib.h>
+#include <math.h>
+#include <string.h>
+
+#include "graph.h"
+#include "gameboard.h"
+#include "levelstate.h"
+
+void draw_score(Gameboard *g){
+  char level_string[160];
+  char score_string[160];
+  char int_string[160];
+  char obj_string[160];
+  cairo_text_extents_t extentsL;
+  cairo_text_extents_t extentsS;
+  cairo_text_extents_t extentsO;
+  cairo_text_extents_t extentsI;
+  cairo_matrix_t m;
+
+  cairo_t *c = cairo_create(g->forescore);
+
+  // clear the pane
+  cairo_save(c);
+  cairo_set_operator(c,CAIRO_OPERATOR_CLEAR);
+  cairo_set_source_rgba (c, 1,1,1,1);
+  cairo_paint(c);
+  cairo_restore(c);
+
+  topbox(g,c,g->g.width,SCOREHEIGHT);
+
+  cairo_select_font_face (c, "Arial",
+			  CAIRO_FONT_SLANT_NORMAL,
+			  CAIRO_FONT_WEIGHT_BOLD);
+
+  cairo_matrix_init_scale (&m, 12.,15.);
+  cairo_set_font_matrix (c,&m);
+  cairo_set_source_rgba (c, TEXT_COLOR);
+
+  snprintf(level_string,160,"Level %d: %s",get_level_num()+1,get_level_desc());
+  snprintf(score_string,160,"Score: %d",graphscore_get_score(&g->g));
+  snprintf(int_string,160,"Intersections: %ld",g->g.active_intersections);
+  snprintf(obj_string,160,"Objective: %s",graphscore_objective_string(&g->g));
+
+  cairo_text_extents (c, level_string, &extentsL);
+  cairo_text_extents (c, obj_string, &extentsO);
+  cairo_text_extents (c, int_string, &extentsI);
+  cairo_text_extents (c, score_string, &extentsS);
+
+  /*
+  text_h = extentsL.height;
+  text_h = max(text_h,extentsO.height);
+  text_h = max(text_h,extentsI.height);
+  text_h = max(text_h,extentsS.height);
+  */
+
+  int ty1 = 23;
+  int ty2 = 38;
+
+  cairo_move_to (c, 15, ty1);
+  cairo_show_text (c, int_string);  
+  cairo_move_to (c, 15, ty2);
+  cairo_show_text (c, score_string);  
+
+  cairo_move_to (c, g->g.width-extentsL.width-15, ty1);
+  cairo_show_text (c, level_string);  
+  cairo_move_to (c, g->g.width-extentsO.width-15, ty2);
+  cairo_show_text (c, obj_string);  
+
+  cairo_destroy(c);
+}
+
+void update_score(Gameboard *g){
+  GdkRectangle r;
+  draw_score(g);
+  
+  r.x=0;
+  r.y=0;
+  r.width=g->g.width;
+  r.height = SCOREHEIGHT;
+  gdk_window_invalidate_rect (g->w.window, &r, FALSE);
+
+}
+

Added: trunk/planarity/gameboard_draw_selection.c
===================================================================
--- trunk/planarity/gameboard_draw_selection.c	2005-09-24 22:02:43 UTC (rev 10059)
+++ trunk/planarity/gameboard_draw_selection.c	2005-09-25 09:24:23 UTC (rev 10060)
@@ -0,0 +1,45 @@
+#define _GNU_SOURCE
+#include <gtk/gtk.h>
+#include <gtk/gtkmain.h>
+#include <gdk/gdk.h>
+#include <gdk/gdkx.h>
+#include <stdlib.h>
+#include <math.h>
+#include <string.h>
+
+#include "graph.h"
+#include "gameboard.h"
+
+
+// draw selection box 
+void draw_selection_rectangle(Gameboard *g,cairo_t *c){
+  cairo_set_source_rgba(c,SELECTBOX_COLOR);
+  cairo_rectangle(c,g->selectionx,
+		  g->selectiony,
+		  g->selectionw,
+		  g->selectionh);
+  cairo_fill(c);
+}
+
+// invalidate the selection box plus enough area to catch any verticies
+void invalidate_selection(GtkWidget *widget){
+  Gameboard *g = GAMEBOARD (widget);
+  GdkRectangle r;
+  r.x = g->selectionx - (V_RADIUS + V_LINE)*2;
+  r.y = g->selectiony - (V_RADIUS + V_LINE)*2;
+  r.width =  g->selectionw + (V_RADIUS + V_LINE)*4;
+  r.height = g->selectionh + (V_RADIUS + V_LINE)*4;
+  
+  gdk_window_invalidate_rect (widget->window, &r, FALSE);
+}
+
+// invalidate the selection box plus enough area to catch any verticies
+void invalidate_verticies_selection(GtkWidget *widget){
+  Gameboard *g = GAMEBOARD (widget);
+  vertex *v=g->g.verticies;
+  while(v){
+    if(v->selected)
+      invalidate_vertex_off(widget,v,g->dragx,g->dragy);
+    v=v->next;
+  }
+}

Added: trunk/planarity/gameboard_draw_text.c
===================================================================
--- trunk/planarity/gameboard_draw_text.c	2005-09-24 22:02:43 UTC (rev 10059)
+++ trunk/planarity/gameboard_draw_text.c	2005-09-25 09:24:23 UTC (rev 10060)
@@ -0,0 +1,52 @@
+#include <math.h>
+#include <string.h>
+#include <gtk/gtk.h>
+#include <gdk/gdk.h>
+
+#include "graph.h"
+#include "gameboard.h"
+
+GdkRectangle render_text_centered(cairo_t *c, char *s, int x, int y){
+  cairo_text_extents_t ex;
+  GdkRectangle r;
+
+  cairo_text_extents (c, s, &ex);
+
+  r.x=x-(ex.width/2)-ex.x_bearing;
+  r.y=y-(ex.height/2)-ex.y_bearing;
+  r.width=ex.width;
+  r.height=ex.height;
+
+  cairo_move_to (c, r.x, r.y);
+  cairo_show_text (c, s);  
+
+  return r;
+}
+
+GdkRectangle render_border_centered(cairo_t *c, char *s, int x, int y){
+  cairo_text_extents_t ex;
+  GdkRectangle r;
+
+  cairo_text_extents (c, s, &ex);
+
+  r.x=x-(ex.width/2)-ex.x_bearing-2;
+  r.y=y-(ex.height/2)-ex.y_bearing-2;
+  r.width=ex.width+5;
+  r.height=ex.height+5;
+
+  cairo_save(c);
+  cairo_move_to (c, r.x+2, r.y+2 );  
+  cairo_set_line_width(c,3);
+  cairo_set_source_rgba(c,1,1,1,.9);
+  cairo_text_path (c, s);  
+  cairo_stroke(c);
+  cairo_restore(c);
+
+  return r;
+}
+
+GdkRectangle render_bordertext_centered(cairo_t *c, char *s, int x, int y){
+  GdkRectangle r = render_border_centered(c,s,x,y);
+  render_text_centered(c,s,x,y);
+  return r;
+}

Added: trunk/planarity/gameboard_draw_vertex.c
===================================================================
--- trunk/planarity/gameboard_draw_vertex.c	2005-09-24 22:02:43 UTC (rev 10059)
+++ trunk/planarity/gameboard_draw_vertex.c	2005-09-25 09:24:23 UTC (rev 10060)
@@ -0,0 +1,184 @@
+#define _GNU_SOURCE
+#include <gtk/gtk.h>
+#include <gtk/gtkmain.h>
+#include <gdk/gdk.h>
+#include <gdk/gdkx.h>
+#include <stdlib.h>
+#include <math.h>
+#include <string.h>
+
+#include "graph.h"
+#include "gameboard.h"
+
+/* draw/cache vertex surfaces; direct surface copies are faster than
+   always redrawing lots of circles */
+
+void draw_vertex(cairo_t *c,vertex *v,cairo_surface_t *s){
+  cairo_set_source_surface(c,
+			   s,
+			   v->x-V_LINE-V_RADIUS,
+			   v->y-V_LINE-V_RADIUS);
+  cairo_paint(c);
+}      
+
+// normal unlit vertex
+cairo_surface_t *cache_vertex(Gameboard *g){
+  cairo_surface_t *ret=
+    cairo_surface_create_similar (cairo_get_target (g->wc),
+				  CAIRO_CONTENT_COLOR_ALPHA,
+				  (V_RADIUS+V_LINE)*2,
+				  (V_RADIUS+V_LINE)*2);
+  cairo_t *c = cairo_create(ret);
+  
+  cairo_set_line_width(c,V_LINE);
+  cairo_arc(c,V_RADIUS+V_LINE,V_RADIUS+V_LINE,V_RADIUS,0,2*M_PI);
+  cairo_set_source_rgb(c,V_FILL_IDLE_COLOR);
+  cairo_fill_preserve(c);
+  cairo_set_source_rgb(c,V_LINE_COLOR);
+  cairo_stroke(c);
+
+  cairo_destroy(c);
+  return ret;
+}
+
+// selected vertex
+cairo_surface_t *cache_vertex_sel(Gameboard *g){
+  cairo_surface_t *ret=
+    cairo_surface_create_similar (cairo_get_target (g->wc),
+				  CAIRO_CONTENT_COLOR_ALPHA,
+				  (V_RADIUS+V_LINE)*2,
+				  (V_RADIUS+V_LINE)*2);
+  cairo_t *c = cairo_create(ret);
+  
+  cairo_set_line_width(c,V_LINE);
+  cairo_arc(c,V_RADIUS+V_LINE,V_RADIUS+V_LINE,V_RADIUS,0,2*M_PI);
+  cairo_set_source_rgb(c,V_FILL_LIT_COLOR);
+  cairo_fill_preserve(c);
+  cairo_set_source_rgb(c,V_LINE_COLOR);
+  cairo_stroke(c);
+  cairo_arc(c,V_RADIUS+V_LINE,V_RADIUS+V_LINE,V_RADIUS*.5,0,2*M_PI);
+  cairo_set_source_rgb(c,V_FILL_IDLE_COLOR);
+  cairo_fill(c);
+
+  cairo_destroy(c);
+  return ret;
+}
+
+// grabbed vertex
+cairo_surface_t *cache_vertex_grabbed(Gameboard *g){
+  cairo_surface_t *ret=
+    cairo_surface_create_similar (cairo_get_target (g->wc),
+				  CAIRO_CONTENT_COLOR_ALPHA,
+				  (V_RADIUS+V_LINE)*2,
+				  (V_RADIUS+V_LINE)*2);
+  cairo_t *c = cairo_create(ret);
+  
+  cairo_set_line_width(c,V_LINE);
+  cairo_arc(c,V_RADIUS+V_LINE,V_RADIUS+V_LINE,V_RADIUS,0,2*M_PI);
+  cairo_set_source_rgb(c,V_FILL_LIT_COLOR);
+  cairo_fill_preserve(c);
+  cairo_set_source_rgb(c,V_LINE_COLOR);
+  cairo_stroke(c);
+  cairo_arc(c,V_RADIUS+V_LINE,V_RADIUS+V_LINE,V_RADIUS*.5,0,2*M_PI);
+  cairo_set_source_rgb(c,V_FILL_ADJ_COLOR);
+  cairo_fill(c);
+
+  cairo_destroy(c);
+  return ret;
+}
+
+// vertex under mouse rollover
+cairo_surface_t *cache_vertex_lit(Gameboard *g){
+  cairo_surface_t *ret=
+    cairo_surface_create_similar (cairo_get_target (g->wc),
+				  CAIRO_CONTENT_COLOR_ALPHA,
+				  (V_RADIUS+V_LINE)*2,
+				  (V_RADIUS+V_LINE)*2);
+  cairo_t *c = cairo_create(ret);
+  
+  cairo_set_line_width(c,V_LINE);
+  cairo_arc(c,V_RADIUS+V_LINE,V_RADIUS+V_LINE,V_RADIUS,0,2*M_PI);
+  cairo_set_source_rgb(c,V_FILL_LIT_COLOR);
+  cairo_fill_preserve(c);
+  cairo_set_source_rgb(c,V_LINE_COLOR);
+  cairo_stroke(c);
+
+  cairo_destroy(c);
+  return ret;
+}
+
+// verticies attached to grabbed vertex
+cairo_surface_t *cache_vertex_attached(Gameboard *g){
+  cairo_surface_t *ret=
+    cairo_surface_create_similar (cairo_get_target (g->wc),
+				  CAIRO_CONTENT_COLOR_ALPHA,
+				  (V_RADIUS+V_LINE)*2,
+				  (V_RADIUS+V_LINE)*2);
+  cairo_t *c = cairo_create(ret);
+  
+  cairo_set_line_width(c,V_LINE);
+  cairo_arc(c,V_RADIUS+V_LINE,V_RADIUS+V_LINE,V_RADIUS,0,2*M_PI);
+  cairo_set_source_rgb(c,V_FILL_ADJ_COLOR);
+  cairo_fill_preserve(c);
+  cairo_set_source_rgb(c,V_LINE_COLOR);
+  cairo_stroke(c);
+
+  cairo_destroy(c);
+  return ret;
+}
+
+// vertex being dragged in a group
+cairo_surface_t *cache_vertex_ghost(Gameboard *g){
+  cairo_surface_t *ret=
+    cairo_surface_create_similar (cairo_get_target (g->wc),
+				  CAIRO_CONTENT_COLOR_ALPHA,
+				  (V_RADIUS+V_LINE)*2,
+				  (V_RADIUS+V_LINE)*2);
+  cairo_t *c = cairo_create(ret);
+  
+  cairo_set_line_width(c,V_LINE);
+  cairo_arc(c,V_RADIUS+V_LINE,V_RADIUS+V_LINE,V_RADIUS,0,2*M_PI);
+  cairo_set_source_rgba(c,V_LINE_COLOR,.2);
+  cairo_fill_preserve(c);
+  cairo_set_source_rgba(c,V_LINE_COLOR,.4);
+  cairo_stroke(c);
+
+  cairo_destroy(c);
+  return ret;
+}
+
+/* region invalidation operations; do exposes efficiently! **********/
+
+// invalidate the box around a single offset vertex
+void invalidate_vertex_off(GtkWidget *widget, 
+					 vertex *v, int dx, int dy){
+  if(v){
+    GdkRectangle r;
+    r.x = v->x - V_RADIUS - V_LINE + dx;
+    r.y = v->y - V_RADIUS - V_LINE + dy;
+    r.width =  (V_RADIUS + V_LINE)*2;
+    r.height = (V_RADIUS + V_LINE)*2;
+    
+    gdk_window_invalidate_rect (widget->window, &r, FALSE);
+  }
+}
+
+// invalidate the box around a single vertex
+void invalidate_vertex(Gameboard *g, vertex *v){
+  invalidate_vertex_off(&g->w,v,0,0);
+}
+
+// invalidate a vertex and any other attached verticies
+void invalidate_attached(GtkWidget *widget, vertex *v){
+  if(v){
+    edge_list *el=v->edges;
+    while (el){
+      edge *e=el->edge;
+      if(e->A != v)invalidate_vertex(GAMEBOARD(widget),e->A);
+      if(e->B != v)invalidate_vertex(GAMEBOARD(widget),e->B);
+      el=el->next;
+    }
+    invalidate_vertex(GAMEBOARD(widget),v);
+  }
+}
+

Added: trunk/planarity/gameboard_logic.c
===================================================================
--- trunk/planarity/gameboard_logic.c	2005-09-24 22:02:43 UTC (rev 10059)
+++ trunk/planarity/gameboard_logic.c	2005-09-25 09:24:23 UTC (rev 10060)
@@ -0,0 +1,316 @@
+#define _GNU_SOURCE
+#include <gtk/gtk.h>
+#include <gtk/gtkmain.h>
+#include <gdk/gdk.h>
+#include <gdk/gdkx.h>
+#include <stdlib.h>
+#include <math.h>
+#include <stdio.h>
+#include <string.h>
+#include <errno.h>
+
+#include "graph.h"
+#include "timer.h"
+#include "gameboard.h"
+#include "levelstate.h"
+#include "dialog_finish.h"
+#include "dialog_pause.h"
+#include "dialog_level.h"
+#include "main.h"
+
+/* game state helpers ************************************************************************************/
+
+void prepare_reenter_game(Gameboard *g){
+  g->lit_vertex=0;
+  g->grabbed_vertex=0;
+  g->delayed_background=0;
+  
+  g->group_drag=0;
+  g->button_grabbed=0;
+  g->selection_grab=0;
+  g->selection_active=num_selected_verticies(&g->g);
+  
+  update_full(g);
+  update_score(g);
+}
+
+void reenter_game(Gameboard *g){
+  deploy_buttonbar(g);
+  unpause_timer();
+}
+
+void enter_game(Gameboard *g){
+  set_timer(0);
+  prepare_reenter_game(g);
+  reenter_game(g);
+}
+
+static void scale(Gameboard *g,double scale){
+  int x=g->g.width/2;
+  int y=g->g.height/2;
+  int sel = num_selected_verticies(&g->g);
+  vertex *v;
+
+  // if selected, expand from center of selected mass
+  if(sel){
+    double xac=0;
+    double yac=0;
+
+    v=g->g.verticies;
+    while(v){
+      if(v->selected){
+	xac+=v->x;
+	yac+=v->y;
+      }
+      v=v->next;
+    }
+    x = xac / sel;
+    y = yac / sel;
+  }
+
+  v=g->g.verticies;
+  while(v){
+  
+    if(!sel || v->selected){
+      int nx = rint((v->x - x)*scale+x);
+      int ny = rint((v->y - y)*scale+y);
+      
+      if(nx<0)nx=0;
+      if(nx>=g->g.width)nx=g->g.width-1;
+      if(ny<0)ny=0;
+      if(ny>=g->g.height)ny=g->g.height-1;
+
+      deactivate_vertex(&g->g,v);
+      v->x = nx;
+      v->y = ny;
+    }
+    v=v->next;
+  }
+  
+  v=g->g.verticies;
+  while(v){
+    activate_vertex(&g->g,v);
+    v=v->next;
+  }
+
+  update_full(g);
+}
+
+static void gtk_main_quit_wrapper(Gameboard *g){
+  gtk_main_quit();
+}
+
+static void level_wrapper(Gameboard *g){
+  level_dialog(g,0);
+}
+
+/* toplevel main gameboard action entry points; when a button is
+   clicked and decoded to a specific action or a game state change
+   triggers an action, one of the below functions is the entry
+   point **************************************************************************************************************/
+
+void quit_action(Gameboard *g){
+  pause_timer();
+  undeploy_buttons(g,gtk_main_quit_wrapper);
+}
+
+void level_action(Gameboard *g){
+  pause_timer();
+  undeploy_buttons(g,level_wrapper);
+}
+
+void finish_action(Gameboard *g){
+  if(g->g.active_intersections<=g->g.original_intersections){
+    pause_timer();
+    levelstate_finish();
+    undeploy_buttons(g,finish_level_dialog);
+  }
+}
+
+void pause_action(Gameboard *g){
+  pause_timer();
+  undeploy_buttons(g,pause_dialog);
+}
+
+void about_action(Gameboard *g){
+  pause_timer();
+  undeploy_buttons(g,about_dialog);
+}
+
+void expand_action(Gameboard *g){
+  scale(g,1.2);
+}
+
+void shrink_action(Gameboard *g){
+  scale(g,.8);
+}
+
+void set_hide_lines(Gameboard *g, int state){
+  g->hide_lines=state;
+  update_full(g);
+}
+
+void toggle_hide_lines(Gameboard *g){
+  g->hide_lines= !g->hide_lines;
+  update_full(g);
+}
+
+void set_show_intersections(Gameboard *g, int state){
+  if(g->show_intersections != state){
+    g->show_intersections=state;
+    expose_full(g);
+  }
+}
+
+void toggle_show_intersections(Gameboard *g){
+  g->show_intersections=!g->show_intersections;
+  expose_full(g);
+}
+
+void reset_action(Gameboard *g){
+  int flag=1;
+  vertex *v=g->g.verticies;
+
+  // hide intersections now; deactivating the verticies below will hide them anyway
+  g->show_intersections = 0;
+  expose_full(g);
+  gdk_window_process_all_updates();
+  gdk_flush();
+
+  // deactivate all the verticies so they can be moved en-masse
+  // without graph metadata updates at each move
+  while(v){
+    deactivate_vertex(&g->g,v);
+    v=v->next;
+  }
+
+  // animate
+  while(flag){
+    flag=0;
+
+    v=g->g.verticies;
+    while(v){
+      int bxd = (g->g.width - g->g.orig_width) >>1;
+      int byd = (g->g.height - g->g.orig_height) >>1;
+      int vox = v->orig_x + bxd;
+      int voy = v->orig_y + byd;
+
+      if(v->x != vox || v->y != voy){
+	flag=1;
+      
+	invalidate_vertex(g,v);
+	if(v->x<vox){
+	  v->x+=RESET_DELTA;
+	  if(v->x>vox)v->x=vox;
+	}else{
+	  v->x-=RESET_DELTA;
+	  if(v->x<vox)v->x=vox;
+	}
+	if(v->y<voy){
+	  v->y+=RESET_DELTA;
+	  if(v->y>voy)v->y=voy;
+	}else{
+	  v->y-=RESET_DELTA;
+	  if(v->y<voy)v->y=voy;
+	}
+	invalidate_vertex(g,v);
+      }
+      v=v->next;
+    }
+    gdk_window_process_all_updates();
+    gdk_flush();
+    
+  }
+
+  // reactivate all the verticies 
+  activate_verticies(&g->g);
+
+  // it's a reset; show lines is default. This also has the side
+  // effect of forcing a full board redraw and expose
+  set_hide_lines(g,0);
+
+  // hey, the board could ahve come pre-solved
+  if(g->g.active_intersections <= g->g.objective){
+    deploy_check(g);
+  }else{
+    undeploy_check(g);
+  }
+  
+  // reset timer
+  set_timer(0);
+  unpause_timer();
+}
+
+
+/***************** save/load gameboard the widget state we want to be persistent **************/
+
+// there are only a few things; lines, intersections
+int gameboard_write(char *basename, Gameboard *g){
+  char *name;
+  FILE *f;
+
+  name=alloca(strlen(boarddir)+strlen(basename)+1);
+  name[0]=0;
+  strcat(name,boarddir);
+  strcat(name,basename);
+  
+  f = fopen(name,"wb");
+  if(f==NULL){
+    fprintf(stderr,"ERROR:  Could not save board state for \"%s\":\n\t%s\n",
+	    get_level_desc(),strerror(errno));
+    return errno;
+  }
+
+  graph_write(&g->g,f);
+
+  if(g->hide_lines)
+    fprintf(f,"hide_lines 1\n");
+  if(g->show_intersections)
+    fprintf(f,"show_intersections 1\n");
+  
+  fclose(f);
+
+  return 0;
+}
+
+int gameboard_read(char *basename, Gameboard *g){
+  FILE *f;
+  char *name;
+  char *line=NULL;
+  size_t n=0;
+
+  name=alloca(strlen(boarddir)+strlen(basename)+3);
+  name[0]=0;
+  strcat(name,boarddir);
+  strcat(name,basename);
+  
+  f = fopen(name,"rb");
+  if(f==NULL){
+    fprintf(stderr,"ERROR:  Could not read saved board state for \"%s\":\n\t%s\n",
+	    get_level_desc(),strerror(errno));
+    return errno;
+  }
+  
+  graph_read(&g->g,f);
+
+  // get local game state
+  while(getline(&line,&n,f)>0){
+    int i;
+    
+    if (sscanf(line,"hide_lines %d",&i)==1)
+      if(i)
+	g->hide_lines = 1;
+    
+    if (sscanf(line,"show_intersections %d",&i)==1)
+      if(i)
+	g->show_intersections = 1;
+    
+  }
+  
+  fclose (f);
+  free(line);
+  request_resize(g->g.width,g->g.height);
+
+  return 0;
+}
+

Added: trunk/planarity/gameboard_logic_button.c
===================================================================
--- trunk/planarity/gameboard_logic_button.c	2005-09-24 22:02:43 UTC (rev 10059)
+++ trunk/planarity/gameboard_logic_button.c	2005-09-25 09:24:23 UTC (rev 10060)
@@ -0,0 +1,619 @@
+#include <math.h>
+#include <string.h>
+#include <gtk/gtk.h>
+#include <gdk/gdk.h>
+
+#include "graph.h"
+#include "gameboard.h"
+#include "gameboard_draw_button.h"
+
+
+/************************ manage the buttons for buttonbar and dialogs *********************/
+
+/* determine the x/y/w/h box around the rollover text */
+static GdkRectangle rollover_box(Gameboard *g, buttonstate *b){
+  GdkRectangle r;
+  int width = g->g.width;
+  int height = g->g.height;
+
+  int x = b->x - b->ex.width/2 + b->ex.x_bearing -2;
+  int y = b->y - BUTTON_RADIUS - BUTTON_TEXT_BORDER + b->ex.y_bearing -2;
+
+  if(x<BUTTON_TEXT_BORDER)x=BUTTON_TEXT_BORDER;
+  if(y<BUTTON_TEXT_BORDER)y=BUTTON_TEXT_BORDER;
+  if(x+b->ex.width >= width - BUTTON_TEXT_BORDER)
+    x = width - BUTTON_TEXT_BORDER - b->ex.width;
+  if(y+b->ex.height >= height - BUTTON_TEXT_BORDER)
+    y = height - BUTTON_TEXT_BORDER - b->ex.height;
+
+  r.x=x;
+  r.y=y;
+  r.width=b->ex.width+5;
+  r.height=b->ex.height+5;
+
+  return r;
+}
+
+/* draw the actual rollover */
+static void stroke_rollover(Gameboard *g, buttonstate *b, cairo_t *c){
+  cairo_matrix_t m;
+
+  cairo_select_font_face (c, "Arial",
+			  CAIRO_FONT_SLANT_NORMAL,
+			  CAIRO_FONT_WEIGHT_BOLD);
+
+  cairo_matrix_init_scale (&m, BUTTON_TEXT_SIZE);
+  cairo_set_font_matrix (c,&m);
+  
+  {
+    GdkRectangle r=rollover_box(g,b);
+
+    cairo_move_to (c, r.x - b->ex.x_bearing +2, r.y -b->ex.y_bearing+2 );
+
+    cairo_set_line_width(c,3);
+    cairo_set_source_rgba(c,1,1,1,.9);
+    cairo_text_path (c, b->rollovertext);  
+    cairo_stroke(c);
+
+    cairo_set_source_rgba(c,BUTTON_TEXT_COLOR);
+    cairo_move_to (c, r.x - b->ex.x_bearing +2, r.y -b->ex.y_bearing+2 );
+    
+
+    cairo_show_text (c, b->rollovertext);  
+
+  }
+}
+
+/* request an expose for a button region*/
+static void invalidate_button(Gameboard *g,buttonstate *b){
+  GdkRectangle r;
+
+  r.x=b->x-BUTTON_RADIUS-1;
+  r.y=b->y-BUTTON_RADIUS-1;
+  r.width=BUTTON_RADIUS*2+2;
+  r.height=BUTTON_RADIUS*2+2;
+
+  gdk_window_invalidate_rect(g->w.window,&r,FALSE);
+}
+
+/* request an expose for a rollover region*/
+static void invalidate_rollover(Gameboard *g,buttonstate *b){
+  GdkRectangle r = rollover_box(g,b);
+  invalidate_button(g,b);
+  gdk_window_invalidate_rect(g->w.window,&r,FALSE);
+}
+
+/* like invalidation, but just expands a rectangular region */
+static void expand_rectangle_button(buttonstate *b,GdkRectangle *r){
+  int x=b->x-BUTTON_RADIUS-1;
+  int y=b->y-BUTTON_RADIUS-1;
+  int x2=x+BUTTON_RADIUS*2+2;
+  int y2=y+BUTTON_RADIUS*2+2;
+
+  int rx2=r->x+r->width;
+  int ry2=r->y+r->height;
+
+  if(r->width==0 || r->height==0){
+    r->x=x;
+    r->y=y;
+    r->width=x2-x;
+    r->height=y2-y;
+  }
+
+  if(x<r->x)
+    r->x=x;
+  if(y<r->y)
+    r->y=y;
+  if(rx2<x2)
+    rx2=x2;
+  r->width=rx2-r->x;
+  if(ry2<y2)
+    ry2=y2;
+  r->height=ry2-r->y;
+}
+
+/* like invalidation, but just expands a rectangular region */
+static void expand_rectangle_rollover(Gameboard *g,buttonstate *b,GdkRectangle *r){
+  GdkRectangle rr = rollover_box(g,b);
+  int x=rr.x;
+  int y=rr.y;
+  int x2=x+rr.width;
+  int y2=y+rr.height;
+
+  int rx2=r->x+r->width;
+  int ry2=r->y+r->height;
+
+  if(r->width==0 || r->height==0){
+    r->x=x;
+    r->y=y;
+    r->width=x2-x;
+    r->height=y2-y;
+  }
+
+  if(x<r->x)
+    r->x=x;
+  if(y<r->y)
+    r->y=y;
+  if(rx2<x2)
+    rx2=x2;
+  r->width=rx2-r->x;
+  if(ry2<y2)
+    ry2=y2;
+  r->height=ry2-r->y;
+}
+
+/* cache buttons as small surfaces */
+static cairo_surface_t *cache_button(Gameboard *g,
+				     void (*draw)(cairo_t *c, 
+						  double x, 
+						  double y),
+				     double pR,double pG,double pB,double pA,
+				     double fR,double fG,double fB,double fA){
+  cairo_surface_t *ret = 
+    cairo_surface_create_similar (cairo_get_target (g->wc),
+				  CAIRO_CONTENT_COLOR_ALPHA,
+				  BUTTON_RADIUS*2+1,
+				  BUTTON_RADIUS*2+1);
+  
+  cairo_t *c = cairo_create(ret);
+
+  cairo_save(c);
+  cairo_set_operator(c,CAIRO_OPERATOR_CLEAR);
+  cairo_set_source_rgba (c, 1,1,1,1);
+  cairo_paint(c);
+  cairo_restore(c);
+     
+  cairo_set_source_rgba(c,fR,fG,fB,fA);
+  cairo_set_line_width(c,BUTTON_LINE_WIDTH);
+  draw(c,BUTTON_RADIUS+.5,BUTTON_RADIUS+.5);
+
+  cairo_set_source_rgba(c,pR,pG,pB,pA);
+  cairo_stroke(c);
+  
+  cairo_destroy(c);
+  return ret;
+}
+
+/* determine the x/y/w/h box around a button */
+static GdkRectangle button_box(buttonstate *b){
+  GdkRectangle r;
+
+  int x = b->x - BUTTON_RADIUS-1;
+  int y = b->y - BUTTON_RADIUS-1;
+  r.x=x;
+  r.y=y;
+  r.width = BUTTON_RADIUS*2+2;
+  r.height= BUTTON_RADIUS*2+2;
+
+  return r;
+}
+
+static int animate_x_axis(Gameboard *g, buttonstate *b, GdkRectangle *r){
+  int ret=0;
+
+  if(b->x_target != b->x){
+    ret=1;
+    expand_rectangle_button(b,r);
+    if(b->rollover)
+      expand_rectangle_rollover(g,b,r);
+
+    if(b->x_target > b->x){
+      b->x+=DEPLOY_DELTA;
+      if(b->x>b->x_target)b->x=b->x_target;
+    }else{
+      b->x-=DEPLOY_DELTA;
+      if(b->x<b->x_target)b->x=b->x_target;
+    }
+    expand_rectangle_button(b,r);
+    if(b->rollover)
+      expand_rectangle_rollover(g,b,r);
+  }
+
+  return ret;
+}
+static int animate_y_axis(Gameboard *g, buttonstate *b, GdkRectangle *r){
+  int ret=0;
+
+  if(b->y_target != b->y){
+    ret=1;
+    expand_rectangle_button(b,r);
+    if(b->rollover)
+      expand_rectangle_rollover(g,b,r);
+
+    if(b->y_target > b->y){
+      b->y+=DEPLOY_DELTA;
+      if(b->y>b->y_target)b->y=b->y_target;
+    }else{
+      b->y-=DEPLOY_DELTA;
+      if(b->y<b->y_target)b->y=b->y_target;
+    }
+    expand_rectangle_button(b,r);
+    if(b->rollover)
+      expand_rectangle_rollover(g,b,r);
+  }
+
+  if(b->alphad != b->y_active - b->y){
+    double alpha=0;
+    if(b->y_inactive>b->y_active){
+      if(b->y<=b->y_active)
+	alpha=1.;
+      else if (b->y>=b->y_inactive)
+	alpha=0.;
+      else
+	alpha = (double)(b->y_inactive-b->y)/(b->y_inactive-b->y_active);
+    }else if (b->y_inactive<b->y_active){
+      if(b->y>=b->y_active)
+	alpha=1.;
+      else if (b->y<=b->y_inactive)
+	alpha=0.;
+      else
+	alpha = (double)(b->y_inactive-b->y)/(b->y_inactive-b->y_active);
+    }
+    if(alpha != b->alpha){
+      ret=1;
+      expand_rectangle_button(b,r);
+      b->alpha=alpha;
+    }
+    b->alphad = b->y_active - b->y;
+  }
+
+  return ret;
+}
+
+/* do the animation math for one button for one frame */
+static int animate_one(Gameboard *g,buttonstate *b, GdkRectangle *r){
+  int ret=0;
+
+  /* does this button need to be deployed? */
+  if(g->b.sweeperd>0){
+    if(b->y_target != b->y_active){
+      if(g->b.sweeper >= b->sweepdeploy){
+	b->y_target=b->y_active;
+	ret=1;
+      }
+    }
+  }
+  /* does this button need to be undeployed? */
+  if(g->b.sweeperd<0){
+    if(b->y_target != b->y_inactive){
+      if(-g->b.sweeper >= b->sweepdeploy){
+	b->y_target=b->y_inactive;
+	ret=1;
+      }
+    }
+  }
+
+  ret |= animate_x_axis(g,b,r);
+  ret |= animate_y_axis(g,b,r);
+  
+  return ret;
+}
+
+/******************** toplevel abstraction calls *********************/
+
+/* initialize the persistent caches; called once when gameboard is
+   first created */
+void init_buttons(Gameboard *g){
+  buttonstate *states=g->b.states;
+  memset(g->b.states,0,sizeof(g->b.states));
+
+  states[0].idle = cache_button(g, path_button_exit, 
+				BUTTON_QUIT_IDLE_PATH, 
+				BUTTON_QUIT_IDLE_FILL);
+  states[0].lit  = cache_button(g, path_button_exit, 
+				BUTTON_QUIT_LIT_PATH, 
+				BUTTON_QUIT_LIT_FILL);
+
+  states[1].idle = cache_button(g, path_button_back, 
+				BUTTON_IDLE_PATH,
+				BUTTON_IDLE_FILL);
+  states[1].lit  = cache_button(g, path_button_back, 
+				BUTTON_LIT_PATH,
+				BUTTON_LIT_FILL);
+  states[2].idle = cache_button(g, path_button_reset, 
+				BUTTON_IDLE_PATH,
+				BUTTON_IDLE_FILL);
+  states[2].lit  = cache_button(g, path_button_reset, 
+				BUTTON_LIT_PATH,
+				BUTTON_LIT_FILL);
+  states[3].idle = cache_button(g, path_button_pause, 
+				BUTTON_IDLE_PATH,
+				BUTTON_IDLE_FILL);
+  states[3].lit  = cache_button(g, path_button_pause, 
+				BUTTON_LIT_PATH,
+				BUTTON_LIT_FILL);
+  states[4].idle = cache_button(g, path_button_help, 
+				BUTTON_IDLE_PATH,
+				BUTTON_IDLE_FILL);
+  states[4].lit  = cache_button(g, path_button_help, 
+				BUTTON_LIT_PATH,
+				BUTTON_LIT_FILL);
+  states[5].idle = cache_button(g, path_button_expand, 
+				BUTTON_IDLE_PATH,
+				BUTTON_IDLE_FILL);
+  states[5].lit  = cache_button(g, path_button_expand, 
+				BUTTON_LIT_PATH,
+				BUTTON_LIT_FILL);
+  states[6].idle = cache_button(g, path_button_shrink, 
+				BUTTON_IDLE_PATH,
+				BUTTON_IDLE_FILL);
+  states[6].lit  = cache_button(g, path_button_shrink, 
+				BUTTON_LIT_PATH,
+				BUTTON_LIT_FILL);
+  states[7].idle = cache_button(g, path_button_lines, 
+				BUTTON_IDLE_PATH,
+				BUTTON_IDLE_FILL);
+  states[7].lit  = cache_button(g, path_button_lines, 
+				BUTTON_LIT_PATH,
+				BUTTON_LIT_FILL);
+  states[8].idle = cache_button(g, path_button_int, 
+				BUTTON_IDLE_PATH,
+				BUTTON_IDLE_FILL);
+  states[8].lit  = cache_button(g, path_button_int, 
+				BUTTON_LIT_PATH,
+				BUTTON_LIT_FILL);
+  states[9].idle = cache_button(g, path_button_check, 
+				BUTTON_CHECK_IDLE_PATH,
+				BUTTON_CHECK_IDLE_FILL);
+  states[9].lit  = cache_button(g, path_button_check, 
+				BUTTON_CHECK_LIT_PATH,
+				BUTTON_CHECK_LIT_FILL);
+  states[10].idle= cache_button(g, path_button_play, 
+				BUTTON_CHECK_IDLE_PATH,
+				BUTTON_CHECK_IDLE_FILL);
+  states[10].lit = cache_button(g, path_button_play, 
+				BUTTON_CHECK_LIT_PATH,
+				BUTTON_CHECK_LIT_FILL);
+}
+
+/* match x/y to a button if any */
+buttonstate *find_button(Gameboard *g, int x,int y){
+  int i;
+  buttonstate *states=g->b.states;
+
+  for(i=0;i<NUMBUTTONS;i++){
+    buttonstate *b=states+i;
+    if(b->position)
+      if( (b->x-x)*(b->x-x) + (b->y-y)*(b->y-y) <= BUTTON_RADIUS*BUTTON_RADIUS+4)
+	if(b->y != b->y_inactive)
+	  return b;
+  }
+  return 0;
+}
+
+/* set a button to pressed or lit */
+void button_set_state(Gameboard *g, buttonstate *b, int rollover, int press){
+  int i;
+  buttonstate *states=g->b.states;
+  
+  if(b->rollover == rollover && b->press == press)return;
+
+  for(i=0;i<NUMBUTTONS;i++){
+    buttonstate *bb=states+i;
+    if(bb->position){
+      if(bb!=b){
+	if(bb->rollover)
+	  invalidate_rollover(g,bb);
+	if(bb->press)
+	  invalidate_button(g,bb);
+	
+	bb->rollover=0;
+	bb->press=0;
+      }else{
+	if(bb->rollover != rollover)
+	  invalidate_rollover(g,bb);
+	if(bb->press != press)
+	  invalidate_button(g,bb);
+	
+	bb->rollover=rollover;
+	bb->press=press;
+      }
+    }
+  }
+  g->b.allclear=0;
+}
+
+/* cache the text extents of a rollover */
+void rollover_extents(Gameboard *g, buttonstate *b){
+  
+  cairo_matrix_t m;
+
+  cairo_t *c = cairo_create(g->foreground);
+  cairo_select_font_face (c, "Arial",
+			  CAIRO_FONT_SLANT_NORMAL,
+			  CAIRO_FONT_WEIGHT_BOLD);
+  
+  cairo_matrix_init_scale (&m, BUTTON_TEXT_SIZE);
+  cairo_set_font_matrix (c,&m);
+  
+  cairo_text_extents (c, b->rollovertext, &b->ex);
+
+  cairo_destroy(c);
+}
+
+/* perform a single frame of animation for all buttons/rollovers */
+gboolean animate_button_frame(gpointer ptr){
+  Gameboard *g=(Gameboard *)ptr;
+  GdkRectangle expose;
+  buttonstate *states=g->b.states;
+  int ret=0,i,pos;
+  
+  if(!g->first_expose)return 1;
+
+  g->b.sweeper += g->b.sweeperd;
+  
+  /* avoid the normal expose event mechanism
+     during the button animations.  This direct method is much faster as
+     it won't accidentally combine exposes over long stretches of
+     alpha-blended surfaces. */
+  
+  for(pos=1;pos<=3;pos++){
+    memset(&expose,0,sizeof(expose));
+    for(i=0;i<NUMBUTTONS;i++)
+      if(states[i].position == pos)
+	ret|=animate_one(g,states+i,&expose);
+    if(expose.width && expose.height)
+      gameboard_draw(g,
+		     expose.x,
+		     expose.y,
+		     expose.width,
+		     expose.height);
+  }
+
+  if(!ret)g->b.sweeperd = 0;
+
+  if(!ret && g->gtk_timer!=0){
+    g_source_remove(g->gtk_timer);
+    g->gtk_timer=0;
+  }
+
+  if(!ret && g->button_callback)
+    // undeploy finished... call the undeploy callback
+    g->button_callback(g);
+
+  return ret;
+}
+
+/* the normal expose method for all buttons; called from the master
+   widget's expose */
+void expose_buttons(Gameboard *g,cairo_t *c, int x,int y,int w,int h){
+  int i;
+  buttonstate *states=g->b.states;
+  
+  for(i=0;i<NUMBUTTONS;i++){
+
+    buttonstate *b=states+i;
+
+    if(b->position){
+      GdkRectangle r = rollover_box(g,b);
+      GdkRectangle br = button_box(b);
+      
+      if(x < br.x+br.width &&
+	 y < br.y+br.height &&
+	 x+w > br.x &&
+       y+h > br.y) {
+	
+	cairo_set_source_surface(c,
+				 (b->rollover || b->press ? b->lit : b->idle),
+				 br.x,
+				 br.y);
+	
+	if(b->press)
+	  cairo_paint_with_alpha(c,b->alpha);
+	cairo_paint_with_alpha(c,b->alpha);
+	
+      }
+      
+      if((b->rollover || b->press) && b->y_target!=b->y_inactive)
+	if(x < r.x+r.width &&
+	   y < r.y+r.height &&
+	   x+w > r.x &&
+	   y+h > r.y)
+	  
+	  stroke_rollover(g,b,c);
+    }
+  }
+}
+
+/* resize the button bar; called from master resize in gameboard */
+void resize_buttons(Gameboard *g,int oldw,int oldh,int w,int h){
+  int i;
+  int dx=w/2-oldw/2;
+  int dy=h/2-oldh/2;
+  buttonstate *states=g->b.states;
+
+  for(i=0;i<NUMBUTTONS;i++){
+    if(states[i].position == 2){
+
+      states[i].x+=dx;
+      states[i].x_target+=dx;
+
+      states[i].y+=dy;
+      states[i].y_target+=dy;
+      states[i].y_active+=dy;
+      states[i].y_inactive+=dy;
+    }
+  }
+
+  dx=w-oldw;
+  dy=h-oldh;
+
+  for(i=0;i<NUMBUTTONS;i++){
+    if(states[i].position==1 || 
+       states[i].position==3){
+      states[i].y+=dy;
+      states[i].y_target+=dy;
+      states[i].y_active+=dy;
+      states[i].y_inactive+=dy;
+    }
+  }
+  
+  for(i=0;i<NUMBUTTONS;i++){
+    if(states[i].position == 3){
+      states[i].x+=dx;
+      states[i].x_target+=dx;
+    }
+  }
+}
+
+/* clear all buttons to unpressed/unlit */
+void button_clear_state(Gameboard *g){
+  int i;
+  buttonstate *states=g->b.states;
+
+  if(!g->b.allclear){
+    for(i=0;i<NUMBUTTONS;i++){
+      buttonstate *bb=states+i;
+      if(bb->position){
+	if(bb->rollover)
+	  invalidate_rollover(g,bb);
+	if(bb->press)
+	  invalidate_button(g,bb);
+	
+	bb->rollover=0;
+	bb->press=0;
+      }
+    }
+  }
+  g->b.allclear=1;
+  g->b.grabbed=0;
+}
+
+void deploy_buttons(Gameboard *g, void (*callback)(Gameboard *g)){
+
+  if(!g->b.buttons_ready){
+    
+    // sweep across the buttons inward from the horizontal edges; when
+    // the sweep passes a button it is set to deploy by making the
+    // target y equal to the active y target.
+    
+    g->b.sweeper  = 0;
+    g->b.sweeperd = 1;
+
+    g->button_callback = callback;
+    g->gtk_timer = g_timeout_add(BUTTON_ANIM_INTERVAL, animate_button_frame, (gpointer)g);
+    g->b.buttons_ready=1;
+  }
+
+}
+
+void undeploy_buttons(Gameboard *g, void (*callback)(Gameboard *ptr)){
+
+  if(g->b.buttons_ready){
+    
+    button_clear_state(g);
+    g->b.buttons_ready=0;
+
+    // sweep across the buttons outward from the center; when
+    // the sweep passes a button it is set to undeploy by making the
+    // target y equal to the inactive y target.
+
+    g->b.sweeper  = 0;
+    g->b.sweeperd = -1;
+
+    g->button_callback = callback;
+    g->gtk_timer = g_timeout_add(BUTTON_ANIM_INTERVAL, animate_button_frame, (gpointer)g);
+  }else
+    callback(g);
+
+}

Added: trunk/planarity/gameboard_logic_buttonbar.c
===================================================================
--- trunk/planarity/gameboard_logic_buttonbar.c	2005-09-24 22:02:43 UTC (rev 10059)
+++ trunk/planarity/gameboard_logic_buttonbar.c	2005-09-25 09:24:23 UTC (rev 10060)
@@ -0,0 +1,149 @@
+#include <math.h>
+#include <string.h>
+#include <gtk/gtk.h>
+#include <gdk/gdk.h>
+
+#include "graph.h"
+#include "timer.h"
+#include "gameboard.h"
+#include "gameboard_draw_button.h"
+#include "gameboard_logic_buttonbar.h"
+#include "dialog_finish.h"
+#include "dialog_pause.h"
+#include "dialog_level.h"
+
+/************************ the lower button bar *********************/
+
+/* initialize the rather weird little animation engine */
+void setup_buttonbar(Gameboard *g){
+  
+  buttonstate *states=g->b.states;
+  int xcount=BUTTONBAR_BORDER;
+  int dcount=0;
+  int i;
+  int w=g->g.width;
+  int h=g->g.height;
+
+  states[0].rollovertext="exit gPlanarity";
+  states[1].rollovertext="level selection menu";
+  states[2].rollovertext="reset board";
+  states[3].rollovertext="pause";
+  states[4].rollovertext="help / about";
+  states[5].rollovertext="expand";
+  states[6].rollovertext="shrink";
+  states[7].rollovertext="hide/show lines";
+  states[8].rollovertext="mark intersections";
+  states[9].rollovertext="click when finished!";
+
+  states[0].callback = quit_action;
+  states[1].callback = level_action;
+  states[2].callback = reset_action;
+  states[3].callback = pause_action;
+  states[4].callback = about_action;
+  states[5].callback = expand_action;
+  states[6].callback = shrink_action;
+  states[7].callback = toggle_hide_lines;
+  states[8].callback = toggle_show_intersections;
+  states[9].callback = finish_action;
+
+  for(i=0;i<NUMBUTTONS;i++)
+    states[i].position=0;
+
+  states[0].position = 1; //left;
+  states[1].position = 1; //left;
+  states[2].position = 1; //left;
+  states[3].position = 1; //left;
+  states[4].position = 1; //left;
+  states[5].position = 3; //right
+  states[6].position = 3; //right
+  states[7].position = 3; //right
+  states[8].position = 3; //right
+  states[9].position = 3; //right
+
+  for(i=0;i<NUMBUTTONS;i++){
+    buttonstate *b=states+i;
+    if(b->position == 1){
+
+      b->x = b->x_target = xcount;
+      b->y_active = h - BUTTONBAR_Y_FROM_BOTTOM;
+      b->y = b->y_target = b->y_inactive = h - BUTTONBAR_Y_FROM_BOTTOM + BUTTON_EXPOSE;
+      b->sweepdeploy = dcount;
+      xcount += BUTTONBAR_SPACING;
+      dcount += SWEEP_DELTA;
+      rollover_extents(g,states+i);
+    }
+  }
+  
+  xcount = w-BUTTONBAR_BORDER;
+  dcount = 0;
+  for(i=NUMBUTTONS-1;i>=0;i--){
+    buttonstate *b=states+i;
+    if(b->position == 3){
+
+      b->x = b->x_target = xcount;
+      b->y_active = h - BUTTONBAR_Y_FROM_BOTTOM;
+      b->y = b->y_target = b->y_inactive = h - BUTTONBAR_Y_FROM_BOTTOM + BUTTON_EXPOSE;
+      b->sweepdeploy = dcount;
+      
+      if(i!=9 || g->checkbutton_deployed){ // special-case the checkbutton
+	xcount -= BUTTONBAR_SPACING;
+	dcount += SWEEP_DELTA;
+      }else{
+	states[9].position = 0; //deactivate it for the deploy
+      }
+      rollover_extents(g,states+i);
+    }
+  }
+}
+
+/* effects animated 'rollout' of buttons when level begins */
+void deploy_buttonbar(Gameboard *g){
+  if(!g->b.buttons_ready ){
+    if(g->g.active_intersections <= g->g.objective)
+      g->checkbutton_deployed=1;
+    else
+      g->checkbutton_deployed=0;
+    setup_buttonbar(g);
+    deploy_buttons(g,0);
+  }
+  
+}
+
+/* effects animated rollout of 'check' button */
+void deploy_check(Gameboard *g){
+  buttonstate *states=g->b.states;
+  if(g->b.buttons_ready && !g->checkbutton_deployed){
+    int i;
+    for(i=5;i<9;i++){
+      states[i].x_target-=BUTTONBAR_SPACING;
+      states[i].sweepdeploy += SWEEP_DELTA;
+    }
+
+    states[9].position = 3; //activate it
+    states[9].y_target = states[9].y_active;
+    states[i].sweepdeploy = 0;
+
+    if(g->gtk_timer!=0)
+      g_source_remove(g->gtk_timer);
+    g->gtk_timer = g_timeout_add(BUTTON_ANIM_INTERVAL, animate_button_frame, (gpointer)g);
+    g->checkbutton_deployed=1;
+  }
+}
+
+/* effects animated rollback of 'check' button */
+void undeploy_check(Gameboard *g){
+  buttonstate *states=g->b.states;
+  if(g->checkbutton_deployed){
+    int i;
+    for(i=5;i<9;i++){
+      states[i].x_target+=BUTTONBAR_SPACING;
+      states[i].sweepdeploy -= SWEEP_DELTA;
+    }
+    states[9].y_target=states[9].y_inactive;
+
+    if(g->gtk_timer!=0)
+      g_source_remove(g->gtk_timer);
+    g->gtk_timer = g_timeout_add(BUTTON_ANIM_INTERVAL, animate_button_frame, (gpointer)g);
+    g->checkbutton_deployed=0;
+  }
+}

Added: trunk/planarity/gameboard_logic_buttonbar.h
===================================================================
--- trunk/planarity/gameboard_logic_buttonbar.h	2005-09-24 22:02:43 UTC (rev 10059)
+++ trunk/planarity/gameboard_logic_buttonbar.h	2005-09-25 09:24:23 UTC (rev 10060)
@@ -0,0 +1,21 @@
+extern void setup_buttonbar(Gameboard *g);
+extern void deploy_buttonbar(Gameboard *g);
+extern void undeploy_buttonbar(Gameboard *g, void (*callback)());
+extern void deploy_check(Gameboard *g);
+extern void undeploy_check(Gameboard *g);
+
+#define BUTTONBAR_Y_FROM_BOTTOM 25
+#define BUTTONBAR_LINE_WIDTH 1
+#define BUTTONBAR_TEXT_BORDER 15
+#define BUTTONBAR_TEXT_COLOR       .1,.1,.7,.8
+#define BUTTONBAR_TEXT_SIZE  15.,18.
+
+#define BUTTONBAR_ANIM_INTERVAL 15
+#define BUTTONBAR_EXPOSE  50
+#define BUTTONBAR_DELTA 3
+
+#define BUTTONBAR_LEFT 5
+#define BUTTONBAR_RIGHT 5
+#define BUTTONBAR_BUTTONS 5
+#define BUTTONBAR_BORDER 35
+#define BUTTONBAR_SPACING 35

Added: trunk/planarity/gameboard_logic_mouse.c
===================================================================
--- trunk/planarity/gameboard_logic_mouse.c	2005-09-24 22:02:43 UTC (rev 10059)
+++ trunk/planarity/gameboard_logic_mouse.c	2005-09-25 09:24:23 UTC (rev 10060)
@@ -0,0 +1,317 @@
+#define _GNU_SOURCE
+#include <gtk/gtk.h>
+#include <gtk/gtkmain.h>
+#include <gdk/gdk.h>
+#include <gdk/gdkx.h>
+#include <stdlib.h>
+#include <math.h>
+#include <stdio.h>
+#include <string.h>
+
+#include "graph.h"
+#include "gameboard.h"
+#include "dialog_level.h"
+
+/* does the given x/y fall inside an on-screen vertex? */
+static void check_lit(Gameboard *g,int x, int y){
+  vertex *v = find_vertex(&g->g,x,y);
+  if(v!=g->lit_vertex){
+    invalidate_vertex(g,v);
+    invalidate_vertex(g,g->lit_vertex);
+    g->lit_vertex = v;
+  }
+}
+
+/* toplevel mouse motion handler */
+gint mouse_motion(GtkWidget        *widget,
+		  GdkEventMotion   *event){
+  Gameboard *g = GAMEBOARD (widget);
+
+  /* the level selection dialog implements its own icon mouse management */
+  if(g->level_dialog_active)
+    level_mouse_motion(g,(int)event->x,(int)event->y);
+
+  /* similarly, if a vertex is grabbed, the only thing that can
+     happen is that we're dragging that vertex */
+  
+  if(g->grabbed_vertex){
+    int x = (int)event->x;
+    int y = (int)event->y;
+    g->dragx = x-g->grabx;
+    g->dragy = y-g->graby;
+    
+    invalidate_vertex(g,g->grabbed_vertex);
+    invalidate_edges(widget,g->grabbed_vertex);
+    move_vertex(&g->g,g->grabbed_vertex,x+g->graboffx,y+g->graboffy);
+    invalidate_vertex(g,g->grabbed_vertex);
+    invalidate_edges(widget,g->grabbed_vertex);
+    return TRUE;
+
+  }
+
+  /* if the selection is grabbed, we can only be dragging a selection box */
+  if (g->selection_grab){
+    invalidate_selection(widget);
+    
+    g->selectionx = min(g->grabx, event->x);
+    g->selectionw = labs(g->grabx - event->x);
+    g->selectiony = min(g->graby, event->y);
+    g->selectionh = labs(g->graby - event->y);
+    select_verticies(&g->g,
+		     g->selectionx,g->selectiony,
+		     g->selectionx+g->selectionw-1,
+		     g->selectiony+g->selectionh-1);
+    
+    invalidate_selection(widget);
+    return TRUE;
+  }
+
+  /* if a selected vertex is grabbed (group drag) we're dragging
+     the ghosted selection */
+  
+  if(g->group_drag){
+    invalidate_verticies_selection(widget);
+    g->dragx = event->x - g->grabx;
+    g->dragy = event->y - g->graby;
+    
+    /* don't allow the drag to put a vertex offscreen; bound the drag at the edge */
+    {
+      vertex *v=g->g.verticies;
+      int w=g->g.width;
+      int h=g->g.height;
+      
+      while(v){
+	if(v->selected){
+	  if(v->x + g->dragx >= w)
+	    g->dragx=w - v->x -1;
+	  if(v->x + g->dragx < 0 )
+	    g->dragx= -v->x;
+	  if(v->y + g->dragy >= h)
+	    g->dragy=h - v->y -1;
+	  if(v->y + g->dragy < 0 )
+	    g->dragy= -v->y;
+	}
+	v=v->next;
+      }
+    }
+    
+    invalidate_verticies_selection(widget);
+    return TRUE;
+  }
+
+  /* A vertex rollover has priority over a button rollover.  However,
+     a button grab disallows a rollover, as does the curtain. */
+  if(!g->pushed_curtain && !g->button_grabbed)
+    check_lit(g, (int)event->x,(int)event->y);
+  
+  /* handle button rollovers and ongoing grabs; other grabs are
+     already disallowed, but also verify a vertex rollover isn't
+     already active (and that buttons are currently hot) */
+  if(!g->lit_vertex && g->b.buttons_ready){
+    buttonstate *b = find_button(g,(int)event->x,(int)event->y);
+
+    if(g->b.grabbed){
+      /* a button is grabbed; a rollover sees only the grabbed
+	 button */
+      if(g->b.grabbed==b)
+	button_set_state(g,b,1,1);
+      else
+	button_set_state(g,b,0,0);
+    }else{
+      /* no button is grabbed; any button may see a rollover */
+      if(b)
+	button_set_state(g,b,1,0);
+      else
+	button_clear_state(g);
+    }
+  }
+  
+  return TRUE;
+}
+
+/* toplevel mouse button press handler */
+gboolean mouse_press (GtkWidget        *widget,
+		      GdkEventButton   *event){
+
+  if(event->type == GDK_BUTTON_PRESS){ // filter out doubleclicks
+
+    Gameboard *g = GAMEBOARD (widget);
+    
+    vertex *v = find_vertex(&g->g,(int)event->x,(int)event->y);
+    buttonstate *b = find_button(g,(int)event->x,(int)event->y);
+    int old_intersections = g->show_intersections;
+  
+    /* the level selection dialog implements its own icon mouse management */
+    if(g->level_dialog_active)
+      level_mouse_press(g,(int)event->x,(int)event->y);
+    
+    button_clear_state(g);
+    g->button_grabbed=0;
+    set_show_intersections(g,0);
+    
+    /* presses that affect board elements other than buttons can only
+       happen when the curtain isn't pushed.  */
+    if(!g->pushed_curtain){
+      
+      /* case: shift-click addition of a single new vertex to selection */
+      if(v && event->state&GDK_SHIFT_MASK){
+	if(v->selected){
+	  v->selected=0;
+	  g->selection_active--;
+	}else{
+	  v->selected=1;
+	  g->selection_active++;
+	}
+	invalidate_vertex(g,g->lit_vertex);
+	return TRUE;
+      }
+      
+      /* case: shift-click drag of an additional region to add to selection */
+      if(event->state&GDK_SHIFT_MASK){
+	g->selection_grab=1;
+	g->grabx = (int)event->x;
+	g->graby = (int)event->y;
+	g->selectionx = g->grabx;
+	g->selectionw = 0;
+	g->selectiony = g->graby;
+	g->selectionh = 0;
+	return TRUE;
+      }
+      
+      /* case: drag the selected group of verticies to new location */
+      if(g->selection_active && v && v->selected){
+	g->group_drag=1;
+	g->grabx = (int)event->x;
+	g->graby = (int)event->y;
+	g->dragx = 0;
+	g->dragy = 0;
+	// put the verticies into the background for faster update
+	update_full(g); 
+	return TRUE;
+      }
+      
+      /* if selection is active, we got this far, and we're not about to
+	 take action based on pointing at a button, selection should be
+	 inactivated */
+      if(g->selection_active && (g->lit_vertex || !b)){
+	deselect_verticies(&g->g);
+	g->selection_active=0;
+	g->group_drag=0;
+	// potentially pull verticies out of background (can happen if
+	// mouse went offscreen during a grab)
+	update_full(g); 
+      }
+      
+      /* case: grab/drag a single unselected vertex */
+      if(g->lit_vertex){
+	g->grabbed_vertex = g->lit_vertex;
+	g->grabx = (int)event->x;
+	g->graby = (int)event->y;
+	g->graboffx = g->grabbed_vertex->x-g->grabx;
+	g->graboffy = g->grabbed_vertex->y-g->graby;
+	
+	grab_vertex(&g->g,g->grabbed_vertex);
+	invalidate_attached(widget,g->grabbed_vertex);
+	invalidate_edges(widget,g->grabbed_vertex);
+	
+	// highlight vertex immediately; update the background after the
+	// vertex change
+	update_full_delayed(g);
+	return TRUE;
+      }
+    }
+    
+    /* case: button click */
+    if(b){
+      // if intersections were visible, a button press is the only
+      // click that wouldn't auto-hide them, so restore the flag to
+      // its state before entry.
+      g->show_intersections=old_intersections;
+      
+      button_set_state(g,b,1,1);
+      g->b.grabbed = b;
+      return TRUE;
+    }
+
+    /* clicking anywhere else wtith no modifiers initiates a new
+       selection drag (assuming curtain isn't pushed) */
+    if(!g->pushed_curtain){
+      g->selection_grab=1;
+      g->grabx = (int)event->x;
+      g->graby = (int)event->y;
+      g->selectionx = g->grabx;
+      g->selectionw = 0;
+      g->selectiony = g->graby;
+      g->selectionh = 0;
+      return TRUE;
+    }
+  }
+  
+  return TRUE;
+}
+
+/* toplevel mouse button release handler */
+gboolean mouse_release (GtkWidget        *widget,
+			GdkEventButton   *event){
+  Gameboard *g = GAMEBOARD (widget);
+  
+  /* case: button grabbed */
+  if(g->b.grabbed){
+    buttonstate *b = find_button(g,(int)event->x,(int)event->y);
+    if(b && g->b.grabbed==b){
+      button_set_state(g,b,1,0);
+      if(b->callback)
+	b->callback(g);
+    }
+    g->b.grabbed=0;
+  }
+  
+  /* if the curtain is pushed, no sense checking for other grabs */
+  if(!g->pushed_curtain){
+  
+    /* case: release a grabbed vertex */
+    if(g->grabbed_vertex){
+      ungrab_vertex(&g->g,g->grabbed_vertex);
+      update_add_vertex(g,g->grabbed_vertex);
+      update_score(g);
+      g->grabbed_vertex = 0;
+
+      if(g->g.active_intersections<=g->g.objective)
+	deploy_check(g);
+      else
+	undeploy_check(g);
+
+    }
+    
+    /* case: release a selection grab */
+    if(g->selection_grab){
+      invalidate_selection(widget);
+      g->selection_grab=0;
+      
+      /* are there selected verticies? If only one was selected, that's
+	 usually due to an accidental drag while clicking.  Treat a
+	 single-vertex drag select as a nil selection */
+      if(num_selected_verticies(&g->g)<=1){
+	g->selection_active=0;
+	deselect_verticies(&g->g); // could have grabbed just one
+      }else{
+	commit_volatile_selection();
+	g->selection_active=num_selected_verticies(&g->g);
+      }
+    }
+    
+    /* case: release a group drag */
+    if(g->group_drag){
+      move_selected_verticies(&g->g,g->dragx,g->dragy);
+      update_full(g); // cheating
+      update_score(g);
+      g->group_drag=0;
+    }
+  }
+
+  /* a release may result in a new mouse-over; look for it */
+  mouse_motion(widget,(GdkEventMotion *)event); // the cast is safe in this case
+
+  return TRUE;
+}
+

Added: trunk/planarity/gameboard_logic_push.c
===================================================================
--- trunk/planarity/gameboard_logic_push.c	2005-09-24 22:02:43 UTC (rev 10059)
+++ trunk/planarity/gameboard_logic_push.c	2005-09-25 09:24:23 UTC (rev 10060)
@@ -0,0 +1,61 @@
+#define _GNU_SOURCE
+#include <gtk/gtk.h>
+#include <gtk/gtkmain.h>
+#include <gdk/gdk.h>
+#include <gdk/gdkx.h>
+#include <stdlib.h>
+#include <math.h>
+#include <string.h>
+
+#include "graph.h"
+#include "gameboard.h"
+
+void update_push(Gameboard *g, cairo_t *c){
+
+  if(g->pushed_curtain){
+    int w = g->g.width;
+    int h = g->g.height;
+
+    draw_foreground(g,c,0,0,w,h);
+    
+    // copy in the score and button surfaces
+    cairo_set_source_surface(c,g->forescore,0,0);
+    cairo_rectangle(c, 0,0,w,
+		    min(SCOREHEIGHT,h));
+    cairo_fill(c);
+    
+    cairo_set_source_surface(c,g->forebutton,0,g->w.allocation.height-SCOREHEIGHT);
+    cairo_rectangle(c, 0,0,w,h);
+    cairo_fill(c);
+    
+    if(g->show_intersections)
+      draw_intersections(g,&g->g,c,0,0,w,h);
+
+    cairo_set_source (c, g->curtainp);
+    cairo_paint(c);
+
+    if(g->redraw_callback)g->redraw_callback(g);
+  }
+}
+
+void push_curtain(Gameboard *g,void(*redraw_callback)(Gameboard *g)){
+  if(!g->pushed_curtain){ 
+    cairo_t *c = cairo_create(g->background);
+    g->pushed_curtain=1;    
+    g->redraw_callback=redraw_callback;
+
+    update_push(g,c);
+
+    cairo_destroy(c);
+    expose_full(g);
+
+    //gameboard_draw(g,0,0,w,h);
+  }
+}
+
+void pop_curtain(Gameboard *g){
+  if(g->pushed_curtain){
+    g->pushed_curtain=0;
+    update_full(g);
+  }
+}

Deleted: trunk/planarity/gamestate.c
===================================================================
--- trunk/planarity/gamestate.c	2005-09-24 22:02:43 UTC (rev 10059)
+++ trunk/planarity/gamestate.c	2005-09-25 09:24:23 UTC (rev 10060)
@@ -1,400 +0,0 @@
-#define _GNU_SOURCE
-#include <math.h>
-#include <string.h>
-#include <stdio.h>
-#include <stdlib.h>
-#include <errno.h>
-#include <gtk/gtk.h>
-#include "graph.h"
-#include "gameboard.h"
-#include "generate.h"
-#include "gamestate.h"
-#include "buttons.h"
-#include "buttonbar.h"
-#include "finish.h"
-#include "pause.h"
-
-extern GtkWidget *toplevel_window;
-extern Gameboard *gameboard;
-extern graph maingraph;
-
-static int width=800;
-static int height=640;
-static int orig_width=800;
-static int orig_height=640;
-
-static int initial_intersections;
-static float intersection_mult=1.;
-static int objective=0;
-static int objective_lessthan=0;
-static char objective_string[80];
-static float objective_mult=1.;
-static int paused=0;
-static time_t begin_time_add=0;
-static time_t begin_time;
-
-
-time_t get_timer(){
-  if(paused)
-    return begin_time_add;
-  else{
-    time_t ret = time(NULL);
-    return ret - begin_time + begin_time_add;
-  }
-}
-
-void set_timer(time_t off){
-  begin_time_add = off;
-  begin_time = time(NULL);
-}
-
-void resize_board(int x, int y){
-  width=x;
-  height=y;
-  check_verticies();
-}
-
-int get_board_width(){
-  return width;
-}
-
-int get_board_height(){
-  return height;
-}
-
-// graphs are originally generated to a 'nominal' minimum sized board.
-int get_orig_width(){
-  return orig_width;
-}
-
-int get_orig_height(){
-  return orig_height;
-}
-
-void gamestate_generate(int level){
-    generate_mesh_1(&maingraph,level);
-    impress_location(&maingraph);
-    initial_intersections = maingraph.original_intersections;
-}
-
-void gamestate_go(){
-    gameboard_reset(gameboard);
-    set_timer(0);
-    deploy_buttonbar(gameboard);
-    unpause();
-}
-
-#define RESET_DELTA 2;
-
-void reset_board(){
-  int flag=1;
-  int hide_state = get_hide_lines(gameboard);
-  hide_lines(gameboard);
-
-  vertex *v=maingraph.verticies;
-  while(v){
-    deactivate_vertex(&maingraph,v);
-    v=v->next;
-  }
-
-  while(flag){
-    flag=0;
-
-    v=maingraph.verticies;
-    while(v){
-      int bxd = (width - orig_width) >>1;
-      int byd = (height - orig_height) >>1;
-      int vox = v->orig_x + bxd;
-      int voy = v->orig_y + byd;
-
-      if(v->x != vox || v->y != voy){
-	flag=1;
-      
-	invalidate_region_vertex(gameboard,v);
-	if(v->x<vox){
-	  v->x+=RESET_DELTA;
-	  if(v->x>vox)v->x=vox;
-	}else{
-	  v->x-=RESET_DELTA;
-	  if(v->x<vox)v->x=vox;
-	}
-	if(v->y<voy){
-	  v->y+=RESET_DELTA;
-	  if(v->y>voy)v->y=voy;
-	}else{
-	  v->y-=RESET_DELTA;
-	  if(v->y<voy)v->y=voy;
-	}
-	invalidate_region_vertex(gameboard,v);
-      }
-      v=v->next;
-    }
-    gdk_window_process_all_updates();
-    gdk_flush();
-    
-  }
-
-  v=maingraph.verticies;
-  while(v){
-    activate_vertex(&maingraph,v);
-    v=v->next;
-  }
-
-  if(!hide_state)show_lines(gameboard);
-  if(maingraph.active_intersections <= get_objective()){
-    deploy_check(gameboard);
-  }else{
-    undeploy_check(gameboard);
-  }
-  update_full(gameboard);
-  
-  // reset timer
-  set_timer(0);
-  unpause();
-}
-
-void pause(){
-  begin_time_add = get_elapsed();
-  paused=1;
-}
-
-void unpause(){
-  paused=0;
-  set_timer(begin_time_add);
-}
-
-int paused_p(){
-  return paused;
-}
-
-void scale(double scale){
-  int x=get_board_width()/2;
-  int y=get_board_height()/2;
-  int sel = selected(gameboard);
-  // if selected, expand from center of selected mass
-  if(sel){
-    vertex *v=maingraph.verticies;
-    double xac=0;
-    double yac=0;
-    while(v){
-      if(v->selected){
-	xac+=v->x;
-	yac+=v->y;
-      }
-      v=v->next;
-    }
-    x = xac / selected(gameboard);
-    y = yac / selected(gameboard);
-  }
-
-  vertex *v=maingraph.verticies;
-  while(v){
-  
-    if(!sel || v->selected){
-      int nx = rint((v->x - x)*scale+x);
-      int ny = rint((v->y - y)*scale+y);
-      
-      if(nx<0)nx=0;
-      if(nx>=width)nx=width-1;
-      if(ny<0)ny=0;
-      if(ny>=height)ny=height-1;
-
-      deactivate_vertex(&maingraph,v);
-      v->x = nx;
-      v->y = ny;
-    }
-    v=v->next;
-  }
-  
-  v=maingraph.verticies;
-  while(v){
-    activate_vertex(&maingraph,v);
-    v=v->next;
-  }
-
-  update_full(gameboard);
-}
-
-void expand(){
-  scale(1.2);
-}
-
-void shrink(){
-  scale(.8);
-}
-
-void hide_show_lines(){
-  if(get_hide_lines(gameboard))
-    show_lines(gameboard);
-  else
-    hide_lines(gameboard);
-}
-
-void mark_intersections(){
-  show_intersections(gameboard);
-}
-
-void finish_board(){
-  if(maingraph.active_intersections<=initial_intersections){
-    pause();
-    levelstate_finish();
-    undeploy_buttonbar(gameboard,finish_level_dialog);
-  }
-}
-
-void quit(){
-  pause();
-  undeploy_buttonbar(gameboard,gtk_main_quit);
-}
-
-int get_score(){
-  float intersection_score = (initial_intersections- maingraph.active_intersections)*
-    intersection_mult;
-  
-  float obj_multiplier = 1;
-
-  if(objective_lessthan)
-    if(objective > maingraph.active_intersections)
-      obj_multiplier += (objective-maingraph.active_intersections)*objective_mult;
-
-  return rint( intersection_score * obj_multiplier );
-}
-
-int get_bonus(){
-  float obj_multiplier = 1;
-
-  if(objective_lessthan)
-    if(objective > maingraph.active_intersections)
-      obj_multiplier += (objective-maingraph.active_intersections)*objective_mult;
-  
-  if(get_elapsed()<initial_intersections)
-    return rint ((initial_intersections-get_elapsed()) * obj_multiplier);
-  
-  return 0;
-}
-
-int get_initial_intersections(){
-  return initial_intersections;
-}
-
-int get_objective(){
-  return objective;
-}
-
-char *get_objective_string(){
-  if(objective == 0)
-    return "zero intersections";
-  if(objective == 1){
-    if(objective_lessthan){
-      return "1 intersection or fewer";
-    }else{
-      return "1 intersection";
-    }
-  }else{
-    snprintf(objective_string,80,"%d intersections%s",
-	     objective,(objective_lessthan?
-			" or fewer":""));
-    return objective_string;
-  }
-}
-
-int write_board(char *boarddir, char *basename){
-  char *name;
-  FILE *f;
-
-  name=alloca(strlen(boarddir)+strlen(basename)+3);
-  name[0]=0;
-  strcat(name,boarddir);
-  strcat(name,basename);
-  
-  f = fopen(name,"wb");
-  if(f==NULL){
-    fprintf(stderr,"ERROR:  Could not save board state for \"%s\":\n\t%s\n",
-	    get_level_string(),strerror(errno));
-    return errno;
-  }
-
-  graph_write(&maingraph,f);
-
-  fprintf(f,"objective %c %d\n",
-	  (objective_lessthan?'*':'='),objective);
-  fprintf(f,"scoring %d %f %f %ld\n",
-	  initial_intersections,intersection_mult,objective_mult,(long)get_elapsed());
-  fprintf(f,"board %d %d %d %d\n",
-	  width,height,orig_width,orig_height);
-
-  gameboard_write(f, gameboard);
-
-  fclose(f);
-
-  strcat(name,".1");
-  f = fopen(name,"wb");
-  if(f==NULL){
-    fprintf(stderr,"ERROR:  Could not save board state for \"%s\":\n\t%s\n",
-	    get_level_string(),strerror(errno));
-    return errno;
-  }
-  //gameboard_write_icon(f);
-  
-  fclose(f);
-  return 0;
-}
-
-int read_board(char *boarddir,char *basename){
-  FILE *f;
-  char *name;
-  char *line=NULL;
-  size_t n=0;
-
-  name=alloca(strlen(boarddir)+strlen(basename)+3);
-  name[0]=0;
-  strcat(name,boarddir);
-  strcat(name,basename);
-  
-  f = fopen(name,"rb");
-  if(f==NULL){
-    if(errno != ENOENT){
-      fprintf(stderr,"ERROR:  Could not read saved board state for \"%s\":\n\t%s\n",
-	      get_level_string(),strerror(errno));
-    }
-    return errno;
-  }
-  
-  graph_read(&maingraph,f);
-
-  // get local game state
-  while(getline(&line,&n,f)>0){
-    int o;
-    char c;
-    long l;
-    
-    if (sscanf(line,"objective %c %d",&c,&o)==2){
-      
-      objective_lessthan = (c=='*');
-      objective = o;
-
-    }else if (sscanf(line,"scoring %d %f %f %ld",
-		     &initial_intersections,&intersection_mult,
-		     &objective_mult,&l)==4){
-      paused=1;
-      begin_time_add = l;
-      
-    }else{
-      sscanf(line,"board %d %d %d %d",&width,&height,&orig_width,&orig_height);	
-
-    }
-  }
-  
-
-  rewind(f);
-    
-  gameboard_read(f,gameboard);
-  fclose (f);
-  free(line);
-
-  gtk_window_resize(GTK_WINDOW(toplevel_window),width,height);
-  gameboard_reset(gameboard);
-
-  return 0;
-}
-

Deleted: trunk/planarity/gamestate.h
===================================================================
--- trunk/planarity/gamestate.h	2005-09-24 22:02:43 UTC (rev 10059)
+++ trunk/planarity/gamestate.h	2005-09-25 09:24:23 UTC (rev 10060)
@@ -1,30 +0,0 @@
-#include <time.h>
-
-extern void resize_board(int x, int y);
-extern int get_board_width();
-extern int get_board_height();
-extern int get_orig_width();
-extern int get_orig_height();
-extern void gamestate_generate(int level);
-extern void gamestate_go();
-extern void finish_board();
-extern void hide_show_lines();
-extern void mark_intersections();
-extern void reset_board();
-extern void expand();
-extern void shrink();
-extern int get_score();
-extern int get_bonus();
-extern int get_objective();
-extern char *get_objective_string();
-extern void quit();
-
-extern void pause();
-extern void unpause();
-extern int paused_p();
-extern time_t get_timer();
-extern void set_timer(time_t off);
-extern int get_initial_intersections();
-
-extern int read_board(char *boarddir,char *basename);
-extern int write_board(char *boarddir,char *basename);

Deleted: trunk/planarity/generate.c
===================================================================
--- trunk/planarity/generate.c	2005-09-24 22:02:43 UTC (rev 10059)
+++ trunk/planarity/generate.c	2005-09-25 09:24:23 UTC (rev 10060)
@@ -1,228 +0,0 @@
-#include <stdlib.h>
-#include <string.h>
-
-#include "graph.h"
-#include "generate.h"
-#include "arrange.h"
-
-typedef struct {
-  vertex **v;
-  int width;
-  int height;
-} mesh;
-
-typedef struct {
-  int vnum[8];
-  vertex *center;
-  mesh   *m;
-} neighbors_grid;
-
-typedef struct {
-  int vnum[8];
-  int num;
-} neighbors_list;
-
-static void populate_neighbors(int vnum, mesh *m, 
-			       neighbors_grid *ng){
-  int width = m->width;
-  int y = vnum/width;
-  int x = vnum - (y*width);
-  int i;
-
-  for(i=0;i<8;i++)ng->vnum[i]=-1;
-
-
-  ng->center = m->v[vnum];
-  ng->m = m;
-
-  if(y-1 >= 0){
-    if(x-1 >= 0)        ng->vnum[0]= (y-1)*width+(x-1);
-                        ng->vnum[1]= (y-1)*width+x;
-    if(x+1 <  m->width) ng->vnum[2]= (y-1)*width+(x+1);
-  }
-
-  if(x-1   >= 0)        ng->vnum[3]= y*width+(x-1);
-  if(x+1   <  m->width) ng->vnum[4]= y*width+(x+1);
-
-  if(y+1   < m->height){
-    if(x-1 >= 0)        ng->vnum[5]= (y+1)*width+(x-1);
-                        ng->vnum[6]= (y+1)*width+x;
-    if(x+1 <  m->width) ng->vnum[7]= (y+1)*width+(x+1);
-  }
-
-}
-
-// eliminate from neighbor structs the verticies that already have at
-// least one edge
-static void filter_spanned_neighbors(neighbors_grid *ng,
-				     neighbors_list *nl){
-  int i;
-  int count=0;
-  for(i=0;i<8;i++)
-    if(ng->vnum[i]==-1 || ng->m->v[ng->vnum[i]]->edges){
-      ng->vnum[i]=-1;
-    }else{
-      nl->vnum[count++]=ng->vnum[i];
-    }
-  nl->num=count;
-
-}
-
-// eliminate from neighbor struct any verticies to which we can't make
-// an edge without crossing another edge.  Only 0,2,5,7 possible.
-static void filter_intersections(neighbors_grid *ng){
-  int i;
-  for(i=0;i<8;i++){
-    switch(i){
-    case 0: 
-      if(ng->vnum[1] != -1 && 
-	 ng->vnum[3] != -1 &&
-	 exists_edge(ng->m->v[ng->vnum[1]],
-		     ng->m->v[ng->vnum[3]]))
-	ng->vnum[i]=-1;
-      break;
-      
-    case 2: 
-      if(ng->vnum[1] != -1 && 
-	 ng->vnum[4] != -1 &&
-	 exists_edge(ng->m->v[ng->vnum[1]],
-		     ng->m->v[ng->vnum[4]]))
-	ng->vnum[i]=-1;
-      break;
-      
-    case 5: 
-      if(ng->vnum[3] != -1 && 
-	 ng->vnum[6] != -1 &&
-	 exists_edge(ng->m->v[ng->vnum[3]],
-		     ng->m->v[ng->vnum[6]]))
-	ng->vnum[i]=-1;
-      break;
-      
-    case 7: 
-      if(ng->vnum[4] != -1 && 
-	 ng->vnum[6] != -1 &&
-	 exists_edge(ng->m->v[ng->vnum[4]],
-		     ng->m->v[ng->vnum[6]]))
-	ng->vnum[i]=-1;
-      break;
-    } 
-  }
-}
-
-// eliminate verticies we've already connected to
-static void filter_edges(neighbors_grid *ng,
-			 neighbors_list *nl){
-
-  vertex *v=ng->center;
-  int count=0,i;
-  for(i=0;i<8;i++){
-    if(ng->vnum[i]!=-1){
-      if(!exists_edge(v,ng->m->v[ng->vnum[i]]))
-	nl->vnum[count++]=ng->vnum[i];
-      else
-	ng->vnum[i]=-1;
-    }
-  }
-  nl->num=count;
-}
-
-static void random_populate(graph *g, int current, mesh *m, int min_connect, float prob){
-  int num_edges=0,i;
-  neighbors_grid ng;
-  neighbors_list nl;
-  populate_neighbors(current, m, &ng);
-  filter_intersections(&ng);
-  filter_edges(&ng,&nl);
-
-  {
-    edge_list *el=m->v[current]->edges;
-    while(el){
-      num_edges++;
-      el=el->next;
-    }
-  }
-
-  while(num_edges<min_connect && nl.num){
-    int choice = random() % nl.num;
-    add_edge(g,m->v[current], m->v[nl.vnum[choice]]);
-    num_edges++;
-    filter_intersections(&ng);
-    filter_edges(&ng,&nl);
-  }
-  
-  for(i=0;i<nl.num;i++)
-    if(random()<RAND_MAX*prob){
-      num_edges++;
-      add_edge(g,m->v[current], m->v[nl.vnum[i]]);
-    }
-}
-
-static void span_depth_first(graph *g,int current, mesh *m){
-  neighbors_grid ng;
-  neighbors_list nl;
-
-  while(1){
-    populate_neighbors(current, m, &ng);
-    // don't reverse the order of the next two
-    filter_intersections(&ng);
-    filter_spanned_neighbors(&ng,&nl);
-    if(nl.num == 0) break;
-    
-    {
-      int choice = random() % nl.num;
-      add_edge(g,m->v[current], m->v[nl.vnum[choice]]);
-      
-      span_depth_first(g,nl.vnum[choice], m);
-    }
-  }
-}
-
-void generate_mesh_1(graph *g, int order){
-  int flag=0;
-  mesh m;
-  m.width=3;
-  m.height=2;
-  vertex *vlist;
-
-  srandom(order);
-  {
-    while(order--){
-      if(flag){
-	flag=0;
-	m.height+=1;
-      }else{
-	flag=1;
-	m.width+=2;
-      }
-    }
-  }
-
-  vlist=new_board(g, m.width * m.height);
-
-  /* a flat vector is easier to address while building the mesh */
-  {
-    int i;
-    vertex *v=vlist;
-    m.v=alloca(m.width*m.height * sizeof(*m.v));
-    for(i=0;i<m.width*m.height;i++){
-      m.v[i]=v;
-      v=v->next;
-    }
-  }
-
-  /* first walk a random spanning tree */
-  span_depth_first(g, 0, &m);
-  
-  /* now iterate the whole mesh adding random edges */
-  {
-    int i;
-    for(i=0;i<m.width*m.height;i++)
-      random_populate(g, i, &m, 2, .25);
-  }
-
-  randomize_verticies(g);
-  arrange_verticies_circle(g);
-
-  //arrange_verticies_mesh(m.width,m.height);
-}
-

Deleted: trunk/planarity/generate.h
===================================================================
--- trunk/planarity/generate.h	2005-09-24 22:02:43 UTC (rev 10059)
+++ trunk/planarity/generate.h	2005-09-25 09:24:23 UTC (rev 10060)
@@ -1 +0,0 @@
-extern void generate_mesh_1(graph *g, int order);

Modified: trunk/planarity/graph.c
===================================================================
--- trunk/planarity/graph.c	2005-09-24 22:02:43 UTC (rev 10059)
+++ trunk/planarity/graph.c	2005-09-25 09:24:23 UTC (rev 10060)
@@ -5,7 +5,7 @@
 #include <math.h>
 
 #include "graph.h"
-#include "gamestate.h"
+#include "timer.h"
 #include "gameboard.h"
 #define CHUNK 64
 
@@ -393,8 +393,15 @@
   while(num--)
     get_vertex(g);
   g->original_intersections = 0;
+  g->active_intersections = 0;
+  g->num_edges=0;        // hopefully redundant
+  g->num_edges_active=0; // hopefully redundant
 }
 
+void graph_release(graph *g){
+  set_num_verticies(g,0);
+}
+
 void activate_vertex(graph *g,vertex *v){
   edge_list *el=v->edges;
   v->active=1;
@@ -463,61 +470,6 @@
   return match;
 }
 
-static void check_vertex_helper(graph *g, vertex *v, int reactivate){
-  int flag=0;
-
-  if(v->x>=get_board_width()){
-    v->x=get_board_width()-1;
-    flag=1;
-  }
-  if(v->x<0){
-    v->x=0;
-    flag=1;
-  }
-  if(v->y>=get_board_height()){
-    v->y=get_board_height()-1;
-    flag=0;
-  }
-  if(v->y<0){
-    v->y=0;
-    flag=1;
-  }
-  if(flag){
-    if(v->edges){
-      deactivate_vertex(g,v);
-      if(reactivate)activate_vertex(g,v);
-    }
-  }
-}
-
-static void check_vertex(graph *g, vertex *v){
-  check_vertex_helper(g,v,1);
-}
-
-void check_verticies(graph *g){
-  vertex *v=g->verticies;
-  while(v){
-    vertex *next=v->next;
-    check_vertex_helper(g,v,0);
-    v=next;
-  }
-
-  v=g->verticies;
-  while(v){
-    vertex *next=v->next;
-    activate_vertex(g,v);
-    v=next;
-  }
-}
-
-void move_vertex(graph *g, vertex *v, int x, int y){
-  if(!v->grabbed) deactivate_vertex(g,v);
-  v->x=x;
-  v->y=y;
-  check_vertex_helper(g,v,0);
-  if(!v->grabbed) activate_vertex(g,v);
-}
-
 // tenative selection; must be confirmed if next call should not clear
 void select_verticies(graph *g,int x1, int y1, int x2, int y2){
   vertex *v = g->verticies;
@@ -583,53 +535,6 @@
   }
 }
 
-void move_selected_verticies(graph *g,int dx, int dy){
-  vertex *v = g->verticies;
-  /* deactivate selected verticies */
-  while(v){
-    vertex *next=v->next;
-    if(v->selected)
-      deactivate_vertex(g,v);
-    v=next;
-  }
-
-  /* move selected verticies and reactivate */
-  v=g->verticies;
-  while(v){
-    vertex *next=v->next;
-    if(v->selected){
-      v->x+=dx;
-      v->y+=dy;
-      check_vertex(g,v);
-      activate_vertex(g,v);
-    }
-    v=next;
-  }
-
-}
-
-void scale_verticies(graph *g,float amount){
-  vertex *v=g->verticies;
-  int x=get_board_width()/2;
-  int y=get_board_height()/2;
-
-  while(v){
-    vertex *next=v->next;
-    deactivate_vertex(g,v);
-    v->x=rint((v->x-x)*amount)+x;
-    v->y=rint((v->y-y)*amount)+y;
-    v=next;
-  }
-
-  v=g->verticies;
-  while(v){
-    vertex *next=v->next;
-    check_vertex(g,v);
-    activate_vertex(g,v);
-    v=next;
-  }
-}
-
 static vertex *split_vertex_list(vertex *v){
   vertex *half=v;
   vertex *prevhalf=v;
@@ -682,6 +587,108 @@
   g->verticies=randomize_helper(g->verticies);
 }
 
+static void check_vertex_helper(graph *g, vertex *v, int reactivate){
+  int flag=0;
+
+  if(v->x>=g->width){
+    v->x=g->width-1;
+    flag=1;
+  }
+  if(v->x<0){
+    v->x=0;
+    flag=1;
+  }
+  if(v->y>=g->height){
+    v->y=g->height-1;
+    flag=0;
+  }
+  if(v->y<0){
+    v->y=0;
+    flag=1;
+  }
+  if(flag){
+    if(v->edges){
+      deactivate_vertex(g,v);
+      if(reactivate)activate_vertex(g,v);
+    }
+  }
+}
+
+static void check_vertex(graph *g, vertex *v){
+  check_vertex_helper(g,v,1);
+}
+
+void check_verticies(graph *g){
+  vertex *v=g->verticies;
+  while(v){
+    vertex *next=v->next;
+    check_vertex_helper(g,v,0);
+    v=next;
+  }
+
+  v=g->verticies;
+  while(v){
+    vertex *next=v->next;
+    activate_vertex(g,v);
+    v=next;
+  }
+}
+
+void move_vertex(graph *g, vertex *v, int x, int y){
+  if(!v->grabbed) deactivate_vertex(g,v);
+  v->x=x;
+  v->y=y;
+  check_vertex_helper(g,v,0);
+  if(!v->grabbed) activate_vertex(g,v);
+}
+
+void move_selected_verticies(graph *g,int dx, int dy){
+  vertex *v = g->verticies;
+  /* deactivate selected verticies */
+  while(v){
+    vertex *next=v->next;
+    if(v->selected)
+      deactivate_vertex(g,v);
+    v=next;
+  }
+
+  /* move selected verticies and reactivate */
+  v=g->verticies;
+  while(v){
+    vertex *next=v->next;
+    if(v->selected){
+      v->x+=dx;
+      v->y+=dy;
+      check_vertex(g,v);
+      activate_vertex(g,v);
+    }
+    v=next;
+  }
+
+}
+
+void scale_verticies(graph *g,float amount){
+  vertex *v=g->verticies;
+  int x=g->width/2;
+  int y=g->height/2;
+
+  while(v){
+    vertex *next=v->next;
+    deactivate_vertex(g,v);
+    v->x=rint((v->x-x)*amount)+x;
+    v->y=rint((v->y-y)*amount)+y;
+    v=next;
+  }
+
+  v=g->verticies;
+  while(v){
+    vertex *next=v->next;
+    check_vertex(g,v);
+    activate_vertex(g,v);
+    v=next;
+  }
+}
+
 vertex *new_board(graph *g, int num_v){
   set_num_verticies(g,num_v);
   return g->verticies;
@@ -693,8 +700,8 @@
 // centered on what the current board size is.
 
 void impress_location(graph *g){
-  int xd = (get_board_width()-get_orig_width())>>1;
-  int yd = (get_board_height()-get_orig_height())>>1;
+  int xd = (g->width-g->orig_width)>>1;
+  int yd = (g->height-g->orig_height)>>1;
   vertex *v=g->verticies;
   while(v){
     v->orig_x=v->x;
@@ -712,20 +719,32 @@
   vertex *v=g->verticies;
   edge *e=g->edges;
   vertex **flat = alloca(g->vertex_num*sizeof(*flat));
+  int *iflat = alloca(g->vertex_num*sizeof(*iflat));
 
+  i=0;
   while(v){
     flat[v->num]=v;
+    iflat[v->num]=i++;
     v=v->next;
   }
 
-  for(i=0;i<g->vertex_num;i++){
-    v = flat[i];
+  fprintf(f,"scoring %ld %f %f %ld %c %d\n",
+	  g->original_intersections,
+	  g->intersection_mult, g->objective_mult, (long)get_timer(),
+	  (g->objective_lessthan?'*':'='),g->objective);
+
+  fprintf(f,"board %d %d %d %d\n",
+	  g->width,g->height,g->orig_width,g->orig_height);
+
+  v=g->verticies;
+  while(v){
     fprintf(f,"vertex %d %d %d %d %d\n",
 	    v->orig_x,v->orig_y,v->x,v->y,v->selected);
+    v=v->next;
   }
 
   while(e){
-    fprintf(f,"edge %d %d\n",e->A->num,e->B->num); 
+    fprintf(f,"edge %d %d\n",iflat[e->A->num],iflat[e->B->num]); 
     e=e->next;
   }
 
@@ -733,13 +752,15 @@
 }
 
 int graph_read(graph *g,FILE *f){
-  char *line=NULL;
-  int i,x,y,ox,oy,sel,A,B,n=0;
+  char *line=NULL,c;
+  int i,x,y,ox,oy,sel,A,B;
+  unsigned int n=0;
   vertex **flat,*v;
+  long l;
   
   new_board(g,0);
 
-  // get all verticies first
+  // get all verticies / scoring first
   while(getline(&line,&n,f)>0){
     
     if(sscanf(line,"vertex %d %d %d %d %d",
@@ -752,7 +773,23 @@
       v->y=y;
       v->selected=sel;
     }
-  }
+
+    if(sscanf(line,"scoring %ld %f %f %ld %c %d\n",
+	      &g->original_intersections,
+	      &g->intersection_mult, &g->objective_mult, &l,
+	      &c,&g->objective)==6){
+
+      pause_timer();
+      set_timer(l);
+      if(c == '*')
+	g->objective_lessthan = 1;
+      else
+	g->objective_lessthan = 0;
+    }
+ 
+    sscanf(line,"board %d %d %d %d",&g->width,&g->height,&g->orig_width,&g->orig_height);	
+
+ }
     
   rewind(f);
   flat=alloca(g->vertex_num*sizeof(*flat));
@@ -772,6 +809,7 @@
       else
 	fprintf(stderr,"WARNING: edge references out of range vertex in save file\n");
     }
+    sscanf(line,"int %ld",&g->original_intersections);
   }
   
   rewind(f);

Modified: trunk/planarity/graph.h
===================================================================
--- trunk/planarity/graph.h	2005-09-24 22:02:43 UTC (rev 10059)
+++ trunk/planarity/graph.h	2005-09-25 09:24:23 UTC (rev 10060)
@@ -47,13 +47,33 @@
   vertex *verticies;
   int     vertex_num;
   edge *edges;
-  int active_intersections;
-  int original_intersections;
+  long active_intersections;
 
   int num_edges;
   int num_edges_active;
+
+  int width;
+  int height;
+  int orig_width;
+  int orig_height;
+
+  // scoring related metadata
+  long  original_intersections;
+  float intersection_mult;
+  int   objective;
+  int   objective_lessthan;
+  float objective_mult;
+
 } graph;
 
+typedef struct graphmeta{
+  int num;
+  char *id;
+  char *desc;
+  void (*gen)(graph *,int arg);
+  int gen_arg;
+} graphmeta;
+
 #include <stdio.h>
 
 extern vertex *new_board(graph *g, int num_v);
@@ -70,8 +90,6 @@
 extern void randomize_verticies(graph *g);
 extern edge *add_edge(graph *g,vertex *A, vertex *B);
 extern int exists_edge(vertex *a, vertex *b);
-extern int get_board_width();
-extern int get_board_height();
 extern vertex *get_verticies();
 extern edge *get_edges();
 extern int num_selected_verticies(graph *g);
@@ -82,5 +100,10 @@
 extern void commit_volatile_selection();
 extern vertex *get_vertex();
 extern void activate_verticies();
-extern int graph_write(graph *g,FILE *f);
-extern int graph_read(graph *g,FILE *f);
+extern int graph_write(graph *g, FILE *f);
+extern int graph_read(graph *g, FILE *f);
+extern void graph_release(graph *g);
+
+extern int graphscore_get_score(graph *g);
+extern int graphscore_get_bonus(graph *g);
+extern char *graphscore_objective_string(graph *g);

Added: trunk/planarity/graph_arrange.c
===================================================================
--- trunk/planarity/graph_arrange.c	2005-09-24 22:02:43 UTC (rev 10059)
+++ trunk/planarity/graph_arrange.c	2005-09-25 09:24:23 UTC (rev 10060)
@@ -0,0 +1,38 @@
+#include <math.h>
+
+#include "graph.h"
+#include "gameboard.h"
+#include "graph_arrange.h"
+
+void arrange_verticies_circle(graph *g){
+  vertex *v = g->verticies;
+  int n = g->vertex_num;
+  int bw=g->orig_width;
+  int bh=g->orig_height;
+  int radius=min(bw,bh)*.45;
+  int i;
+  for(i=0;i<n;i++){
+    v->x = rint( radius * cos( i*M_PI*2./n) + (bw>>1));
+    v->y = rint( radius * sin( i*M_PI*2./n) + (bh>>1));
+    v=v->next;
+  }
+}
+
+void arrange_verticies_mesh(graph *g, int width, int height){
+  vertex *v = g->verticies;
+  int bw=g->orig_width;
+  int bh=g->orig_height;
+  int spacing=min((float)bw/width,(float)bh/height)*.9;
+  int x,y;
+
+  int xpad=(bw - (width-1)*spacing)/2;
+  int ypad=(bh - (height-1)*spacing)/2;
+
+  for(y=0;y<height;y++){
+    for(x=0;x<width;x++){
+      v->x=x*spacing+xpad;
+      v->y=y*spacing+ypad;
+      v=v->next;
+    }
+  }
+}

Added: trunk/planarity/graph_arrange.h
===================================================================
--- trunk/planarity/graph_arrange.h	2005-09-24 22:02:43 UTC (rev 10059)
+++ trunk/planarity/graph_arrange.h	2005-09-25 09:24:23 UTC (rev 10060)
@@ -0,0 +1,2 @@
+extern void arrange_verticies_circle(graph *g);
+extern void arrange_verticies_mesh(graph *g, int width, int height);

Added: trunk/planarity/graph_generate.c
===================================================================
--- trunk/planarity/graph_generate.c	2005-09-24 22:02:43 UTC (rev 10059)
+++ trunk/planarity/graph_generate.c	2005-09-25 09:24:23 UTC (rev 10060)
@@ -0,0 +1,114 @@
+#define _GNU_SOURCE
+#include <string.h>
+#include <stdlib.h>
+#include "graph.h"
+#include "graph_generate.h"
+
+typedef struct {
+  char *class;
+  int instancenum;
+  char *desc;
+  void (*gen)(graph *g, int num);
+  float intersection_mult;
+  float objective_mult;
+} gen_instance;
+
+#define FINITE_LEVELS 1
+static gen_instance i_list[FINITE_LEVELS]={ 
+  {"mesh1", 1, "\"original\" board number one",   generate_mesh_1, 1.,1. },
+  //{"mesh1", 2, "\"original\" board number two",   generate_mesh_1, 1.,1. },
+  //{"mesh1", 3, "\"original\" board number three", generate_mesh_1, 1.,1. },
+  //{"mesh1", 4, "\"original\" board number four",  generate_mesh_1, 1.,1. },
+  //{"mesh1", 5, "\"original\" board number five",  generate_mesh_1, 1.,1. },
+};
+
+#define LOOP_LEVELS 1
+static gen_instance i_list_loop[LOOP_LEVELS]={ 
+  {"mesh1", 2, "\"original\" board number %d",    generate_mesh_1, 1.,1. },
+};
+
+int generate_find_number(char *id){
+  int i;
+  char buffer[160];
+  
+  for(i=0;i<FINITE_LEVELS;i++){
+    snprintf(buffer,160,"%s %d",i_list[i].class,i_list[i].instancenum);
+    if(!strcmp(id,buffer))
+      return i;
+  }
+  
+  {  
+    char *arg = strchr(id,' ');
+    if(arg){
+      unsigned int len = arg-id;
+
+      for(i=0;i<LOOP_LEVELS;i++){
+	if(strlen(i_list_loop[i].class)==len &&
+	   !strncmp(i_list_loop[i].class,id,len)){
+
+	  // class match, determine the level number
+	  int order = atoi(arg+1);
+	  return FINITE_LEVELS + (order - i_list_loop[i].instancenum)*FINITE_LEVELS + i;
+	
+	
+	}
+      }
+    }
+  }
+
+  return -1;
+}
+
+int generate_get_meta(int num, graphmeta *gm){
+  if(num<FINITE_LEVELS){
+
+    /* part of the finite list */
+
+    gm->num = num;
+    gm->desc = i_list[num].desc;
+    if(asprintf(&gm->id,"%s %d",i_list[num].class,i_list[num].instancenum)==-1){
+      fprintf(stderr,"Couldn't allocate memory for level name.\n");
+      return -1;
+    }
+    return 0;
+  }else{
+
+    /* past the finite list; one of the loop levels */
+    int classnum = (num - FINITE_LEVELS) % LOOP_LEVELS;
+    int ordernum = (num - FINITE_LEVELS) / LOOP_LEVELS + 
+      i_list_loop[classnum].instancenum;
+    
+    gm->num = num;
+    if(asprintf(&gm->desc,i_list_loop[classnum].desc,ordernum)==-1){
+      fprintf(stderr,"Couldn't allocate memory for level desciption.\n");
+      return -1;
+    }
+
+    if(asprintf(&gm->id,"%s %d",i_list[classnum].class,ordernum)==-1){
+      fprintf(stderr,"Couldn't allocate memory for level name.\n");
+      return -1;
+    }
+    
+    return 0;
+
+  }
+}
+
+void generate_board(graph *g,int num){
+  gen_instance *gi;
+  if(num<FINITE_LEVELS){
+    gi = i_list+num;
+
+    gi->gen(g,gi->instancenum);
+  }else{
+    int classnum = (num - FINITE_LEVELS) % LOOP_LEVELS;
+    int ordernum = (num - FINITE_LEVELS) / LOOP_LEVELS + 
+      i_list_loop[classnum].instancenum;
+
+    gi = i_list_loop+classnum;
+    gi->gen(g,ordernum);
+  }
+
+  g->objective_mult = gi->objective_mult;
+  g->intersection_mult = gi->intersection_mult;
+}

Added: trunk/planarity/graph_generate.h
===================================================================
--- trunk/planarity/graph_generate.h	2005-09-24 22:02:43 UTC (rev 10059)
+++ trunk/planarity/graph_generate.h	2005-09-25 09:24:23 UTC (rev 10060)
@@ -0,0 +1,5 @@
+extern int generate_find_number(char *id);
+extern int generate_get_meta(int num, graphmeta *gm);
+extern void generate_board(graph *g,int num);
+
+extern void generate_mesh_1(graph *g, int order);

Added: trunk/planarity/graph_generate_mesh1.c
===================================================================
--- trunk/planarity/graph_generate_mesh1.c	2005-09-24 22:02:43 UTC (rev 10059)
+++ trunk/planarity/graph_generate_mesh1.c	2005-09-25 09:24:23 UTC (rev 10060)
@@ -0,0 +1,229 @@
+#include <stdlib.h>
+#include <string.h>
+
+#include "graph.h"
+#include "gameboard.h"
+#include "graph_generate.h"
+#include "graph_arrange.h"
+
+typedef struct {
+  vertex **v;
+  int width;
+  int height;
+} mesh;
+
+typedef struct {
+  int vnum[8];
+  vertex *center;
+  mesh   *m;
+} neighbors_grid;
+
+typedef struct {
+  int vnum[8];
+  int num;
+} neighbors_list;
+
+static void populate_neighbors(int vnum, mesh *m, 
+			       neighbors_grid *ng){
+  int width = m->width;
+  int y = vnum/width;
+  int x = vnum - (y*width);
+  int i;
+
+  for(i=0;i<8;i++)ng->vnum[i]=-1;
+
+
+  ng->center = m->v[vnum];
+  ng->m = m;
+
+  if(y-1 >= 0){
+    if(x-1 >= 0)        ng->vnum[0]= (y-1)*width+(x-1);
+                        ng->vnum[1]= (y-1)*width+x;
+    if(x+1 <  m->width) ng->vnum[2]= (y-1)*width+(x+1);
+  }
+
+  if(x-1   >= 0)        ng->vnum[3]= y*width+(x-1);
+  if(x+1   <  m->width) ng->vnum[4]= y*width+(x+1);
+
+  if(y+1   < m->height){
+    if(x-1 >= 0)        ng->vnum[5]= (y+1)*width+(x-1);
+                        ng->vnum[6]= (y+1)*width+x;
+    if(x+1 <  m->width) ng->vnum[7]= (y+1)*width+(x+1);
+  }
+
+}
+
+// eliminate from neighbor structs the verticies that already have at
+// least one edge
+static void filter_spanned_neighbors(neighbors_grid *ng,
+				     neighbors_list *nl){
+  int i;
+  int count=0;
+  for(i=0;i<8;i++)
+    if(ng->vnum[i]==-1 || ng->m->v[ng->vnum[i]]->edges){
+      ng->vnum[i]=-1;
+    }else{
+      nl->vnum[count++]=ng->vnum[i];
+    }
+  nl->num=count;
+
+}
+
+// eliminate from neighbor struct any verticies to which we can't make
+// an edge without crossing another edge.  Only 0,2,5,7 possible.
+static void filter_intersections(neighbors_grid *ng){
+  int i;
+  for(i=0;i<8;i++){
+    switch(i){
+    case 0: 
+      if(ng->vnum[1] != -1 && 
+	 ng->vnum[3] != -1 &&
+	 exists_edge(ng->m->v[ng->vnum[1]],
+		     ng->m->v[ng->vnum[3]]))
+	ng->vnum[i]=-1;
+      break;
+      
+    case 2: 
+      if(ng->vnum[1] != -1 && 
+	 ng->vnum[4] != -1 &&
+	 exists_edge(ng->m->v[ng->vnum[1]],
+		     ng->m->v[ng->vnum[4]]))
+	ng->vnum[i]=-1;
+      break;
+      
+    case 5: 
+      if(ng->vnum[3] != -1 && 
+	 ng->vnum[6] != -1 &&
+	 exists_edge(ng->m->v[ng->vnum[3]],
+		     ng->m->v[ng->vnum[6]]))
+	ng->vnum[i]=-1;
+      break;
+      
+    case 7: 
+      if(ng->vnum[4] != -1 && 
+	 ng->vnum[6] != -1 &&
+	 exists_edge(ng->m->v[ng->vnum[4]],
+		     ng->m->v[ng->vnum[6]]))
+	ng->vnum[i]=-1;
+      break;
+    } 
+  }
+}
+
+// eliminate verticies we've already connected to
+static void filter_edges(neighbors_grid *ng,
+			 neighbors_list *nl){
+
+  vertex *v=ng->center;
+  int count=0,i;
+  for(i=0;i<8;i++){
+    if(ng->vnum[i]!=-1){
+      if(!exists_edge(v,ng->m->v[ng->vnum[i]]))
+	nl->vnum[count++]=ng->vnum[i];
+      else
+	ng->vnum[i]=-1;
+    }
+  }
+  nl->num=count;
+}
+
+static void random_populate(graph *g, int current, mesh *m, int min_connect, float prob){
+  int num_edges=0,i;
+  neighbors_grid ng;
+  neighbors_list nl;
+  populate_neighbors(current, m, &ng);
+  filter_intersections(&ng);
+  filter_edges(&ng,&nl);
+
+  {
+    edge_list *el=m->v[current]->edges;
+    while(el){
+      num_edges++;
+      el=el->next;
+    }
+  }
+
+  while(num_edges<min_connect && nl.num){
+    int choice = random() % nl.num;
+    add_edge(g,m->v[current], m->v[nl.vnum[choice]]);
+    num_edges++;
+    filter_intersections(&ng);
+    filter_edges(&ng,&nl);
+  }
+  
+  for(i=0;i<nl.num;i++)
+    if(random()<RAND_MAX*prob){
+      num_edges++;
+      add_edge(g,m->v[current], m->v[nl.vnum[i]]);
+    }
+}
+
+static void span_depth_first(graph *g,int current, mesh *m){
+  neighbors_grid ng;
+  neighbors_list nl;
+
+  while(1){
+    populate_neighbors(current, m, &ng);
+    // don't reverse the order of the next two
+    filter_intersections(&ng);
+    filter_spanned_neighbors(&ng,&nl);
+    if(nl.num == 0) break;
+    
+    {
+      int choice = random() % nl.num;
+      add_edge(g,m->v[current], m->v[nl.vnum[choice]]);
+      
+      span_depth_first(g,nl.vnum[choice], m);
+    }
+  }
+}
+
+void generate_mesh_1(graph *g, int order){
+  int flag=0;
+  mesh m;
+  m.width=3;
+  m.height=2;
+  vertex *vlist;
+
+  srandom(order);
+  {
+    while(--order){
+      if(flag){
+	flag=0;
+	m.height+=1;
+      }else{
+	flag=1;
+	m.width+=2;
+      }
+    }
+  }
+
+  vlist=new_board(g, m.width * m.height);
+
+  /* a flat vector is easier to address while building the mesh */
+  {
+    int i;
+    vertex *v=vlist;
+    m.v=alloca(m.width*m.height * sizeof(*m.v));
+    for(i=0;i<m.width*m.height;i++){
+      m.v[i]=v;
+      v=v->next;
+    }
+  }
+
+  /* first walk a random spanning tree */
+  span_depth_first(g, 0, &m);
+  
+  /* now iterate the whole mesh adding random edges */
+  {
+    int i;
+    for(i=0;i<m.width*m.height;i++)
+      random_populate(g, i, &m, 2, .25);
+  }
+
+  randomize_verticies(g);
+  arrange_verticies_circle(g);
+
+  //arrange_verticies_mesh(m.width,m.height);
+}
+

Added: trunk/planarity/graph_score.c
===================================================================
--- trunk/planarity/graph_score.c	2005-09-24 22:02:43 UTC (rev 10059)
+++ trunk/planarity/graph_score.c	2005-09-25 09:24:23 UTC (rev 10060)
@@ -0,0 +1,51 @@
+#include <string.h>
+#include <stdio.h>
+#include <math.h>
+#include "graph.h"
+#include "timer.h"
+
+static char objective_string[160];
+
+int graphscore_get_score(graph *g){
+  int intersection_score = (int)ceil((g->original_intersections- g->active_intersections)*
+				     g->intersection_mult);
+  float obj_multiplier = 1;
+  
+  if(g->objective_lessthan)
+    if(g->objective > g->active_intersections)
+      obj_multiplier += (g->objective-g->active_intersections)*g->objective_mult;
+
+  return ceil( intersection_score * obj_multiplier );
+}
+
+int graphscore_get_bonus(graph *g){
+  float obj_multiplier = 1;
+
+  if(g->objective_lessthan)
+    if(g->objective > g->active_intersections)
+      obj_multiplier += (g->objective-g->active_intersections)*g->objective_mult;
+  
+  if(get_timer()< g->original_intersections*g->intersection_mult)
+    return ceil ((g->original_intersections*g->intersection_mult-get_timer()) * obj_multiplier);
+  
+  return 0;
+}
+
+char *graphscore_objective_string(graph *g){
+  if(g->objective == 0)
+    return "zero intersections";
+  if(g->objective == 1){
+    if(g->objective_lessthan){
+      return "1 intersection or fewer";
+    }else{
+      return "1 intersection";
+    }
+  }else{
+    snprintf(objective_string,160,"%d intersections%s",
+	     g->objective,(g->objective_lessthan?
+			" or fewer":""));
+    return objective_string;
+  }
+}
+
+

Modified: trunk/planarity/levelstate.c
===================================================================
--- trunk/planarity/levelstate.c	2005-09-24 22:02:43 UTC (rev 10059)
+++ trunk/planarity/levelstate.c	2005-09-25 09:24:23 UTC (rev 10060)
@@ -1,5 +1,17 @@
+#define _GNU_SOURCE
+#include <gtk/gtk.h>
+#include <gtk/gtkmain.h>
 #include <stdlib.h>
+#include <string.h>
+#include <errno.h>
+#include "graph.h"
 #include "levelstate.h"
+#include "gameboard.h"
+#include "dialog_pause.h"
+#include "dialog_finish.h"
+#include "dialog_level.h"
+#include "main.h"
+#include "graph_generate.h"
 
 #define CHUNK 64
 #define SAVENAME "levelstate"
@@ -8,28 +20,42 @@
   struct levelstate *prev;
   struct levelstate *next;
 
-  int num;
-  char *name;
+  graphmeta gm;
+
+  int in_progress;
   long highscore;
-  int in_progress;
+  
 } levelstate;
 
-levelstate *head;
-levelstate *tail;
-levelstate *curr;
-levelstate *pool;
+static levelstate *head=0;
+static levelstate *tail=0;
+static levelstate *curr=0;
+static levelstate *pool=0;
+static int graph_dirty = 1;
 
+static int completed_boards = 0;
+static int aboutflag = 0;
+static int pauseflag = 0;
+static int finishflag = 0;
+static int selectflag = 0;
+ 
 static levelstate *new_level(){
   levelstate *ret;
-  
+  int num=0;
+
   if(pool==0){
     int i;
     pool = calloc(CHUNK,sizeof(*pool));
-    for(i=0;i<CHUNK-1;i++) /* last addition's next points to nothing */
+    for(i=0;i<CHUNK-1;i++) /* last addition's ->next points to nothing */
       pool[i].next=pool+i+1;
   }
 
   ret=pool;
+  if(tail)
+    num=tail->gm.num+1;
+
+  if(generate_get_meta(num, &ret->gm)) return 0;
+
   pool=ret->next;
 
   ret->next=0;
@@ -37,112 +63,153 @@
     
   if(tail){
     ret->prev->next=ret;
-    ret->num=ret->prev->num+1;
   }else{
     head=ret;
-    ret->num=0;
   }
-  
-  ret->name=board_level_to_name(ret->num);
+  tail=ret;
+
   ret->highscore=0;
   ret->in_progress=0;
+
+  /* write out a 'fresh' board icon if it doesn't already exist */
+  if(!gameboard_icon_exists(ret->gm.id,"1")){
+    // generate a graph we can use to make the icon
+    graph g;
+    memset(&g,0,sizeof(g));
+    g.width=gameboard->g.width;
+    g.height=gameboard->g.height;
+    g.orig_width=gameboard->g.orig_width;
+    g.orig_height=gameboard->g.orig_height;
+
+    generate_board(&g,num);
+    impress_location(&g);
+
+    gameboard_write_icon(ret->gm.id,"1",gameboard,&g,1,1);
+   
+    // releases the temporary graph memory
+    graph_release(&g);
+  }
+
   
   return ret;
 }
 
+static levelstate *ensure_level_num(int level){
 
-static levelstate *find_level(char *name){
-  int level=board_name_to_level(name);
-
   if(level<0)return 0;
 
   if(!tail)new_level();
 
-  if(level=<tail->num){
+  if(level <= tail->gm.num){
     // find it in the existing levels
     levelstate *l=tail;
     while(l){
-      if(level == l->num) return l;
+      if(level == l->gm.num) return l;
       l=l->prev;
     }
     return 0;
   }else{
     // make new levels to fill 
       
-    while(tail->num<level)
+    while(tail->gm.num<level)
       new_level();
 
     return tail;
   }
 }
 
-int levelstate_write(char *statedir){
+static levelstate *ensure_level(char *name){
+  int level=generate_find_number(name);
+  return ensure_level_num(level);
+}
+
+int levelstate_write(){
   FILE *f;
-  char *name=alloca(strlen(statedir)+strlen(levelstate)+1);
+  char *name=alloca(strlen(statedir)+strlen(SAVENAME)+1);
   name[0]=0;
-  strcat(name,boarddir);
-  strcat(name,levelstate);
+  strcat(name,statedir);
+  strcat(name,SAVENAME);
 
-  if(curr->in_progress)
-    write_board(curr->name);
+  if(!graph_dirty && (curr->in_progress || gameboard->finish_dialog_active)){
+    gameboard_write(curr->gm.id,gameboard);
+    gameboard_write_icon(curr->gm.id,"2",gameboard,&gameboard->g,
+			 !gameboard->hide_lines,gameboard->show_intersections);
 
+  }
+
   f = fopen(name,"wb");
   if(f==NULL){
     fprintf(stderr,"ERROR:  Could not save game state file \"%s\":\n\t%s\n",
-	    name,strerror(errno));
+	    curr->gm.id,strerror(errno));
     return errno;
   }
   
-  fprintf(f,"current %d : %s\n",strlen(curr->name),curr->name);
+  fprintf(f,"current %d : %s\n",strlen(curr->gm.id),curr->gm.id);
 
   {
     levelstate *l=head;
     while(l){
-      fprintf(f,"level %ld %d %d %s\n",
+      fprintf(f,"level %ld %d %d : %s\n",
 	      l->highscore,l->in_progress,
-	      strlen(l->name),l->name);
+	      strlen(l->gm.id),l->gm.id);
       l=l->next;
     }
   }
 
-  if(about_dialog_active())
+  if(gameboard->about_dialog_active)
     fprintf(f,"about 1\n");
-  if(pause_dialog_active())
+  if(gameboard->pause_dialog_active)
     fprintf(f,"pause 1\n");
-  if(finish_dialog_active())
+  if(gameboard->finish_dialog_active)
     fprintf(f,"finish 1\n");
-  //if(level_dialog_active())
-  //fprintf(f,"select 1\n");
+  if(gameboard->level_dialog_active)
+    fprintf(f,"select 1\n");
 	  
+  fclose(f);
   return 0;
 }
 
-int levelstate_read(char *statedir){
-  char *cur_name;
-  int count;
+// also functions as the levelstate init; always called once upon startup
+int levelstate_read(){
   char *line=NULL;
   size_t n=0;
 
-  int aboutflag=0;
-  int pauseflag=0;
-  int finishflag=0;
+  FILE *f;
+  char *name=alloca(strlen(statedir)+strlen(SAVENAME)+1);
+  name[0]=0;
+  strcat(name,statedir);
+  strcat(name,SAVENAME);
+
+  if(!head)new_level();
+  if(!curr)curr=head;
+
+  f = fopen(name,"rb");
+  if(f==NULL){
+    if(errno!=ENOENT){ 
+      fprintf(stderr,"ERROR:  Could not read game state file \"%s\":\n\t%s\n",
+	      curr->gm.id,strerror(errno));
+    }
+    return errno;
+  }
   
-  // first get all levels we've seen.
+  // get all levels we've seen.
   while(getline(&line,&n,f)>0){
     long l;
-    int i,j;
+    int i;
+    unsigned int j;
     if (sscanf(line,"level %ld %d %d : ",&l,&i,&j)==3){
       char *name=strchr(line,':');
       // guard against bad edits
       if(name &&
 	 (strlen(line) - (name - line + 2) >= j)){
-	levelstate *l;
+	levelstate *le;
 	name += 2;
 	name[j]=0;
-	l = find_level(name);
-	if(l){
-	  l->highscore=l;
-	  l->in_progress=i;
+	le = ensure_level(name);
+	if(le){
+	  if(l>0)completed_boards++;
+	  le->highscore=l;
+	  le->in_progress=i;
 	}
       }
     }
@@ -157,15 +224,13 @@
       char *name=strchr(line,':');
       // guard against bad edits
       if(name &&
-	 (strlen(line) - (name - line + 2) >= j)){
-	levelstate *l;
+	 (strlen(line) - (name - line + 2) >= (unsigned)i)){
+	levelstate *le;
 	name += 2;
-	name[j]=0;
-	l = find_level(name);
-	if(l){
-	  curr=l;
-	  break;
-	}
+	name[i]=0;
+	le = ensure_level(name);
+	if(le)
+	  curr=le;
       }
     }
 
@@ -180,24 +245,41 @@
     if(sscanf(line,"finish %d",&i)==1)
       if(i==1)
 	finishflag=1;
+
+    if(sscanf(line,"select %d",&i)==1)
+      if(i==1)
+	selectflag=1;
 	  
   }
 
-  if(!head)new_level();
-  if(!curr)curr=head;
+  return 0;
+}
+
+void levelstate_resume(){
+
   levelstate_go();
 
   if(pauseflag){
-    pause_game();
+    prepare_reenter_game(gameboard);
+    pause_dialog(gameboard);
   }else if (aboutflag){
-    about_game();
+    prepare_reenter_game(gameboard);
+    about_dialog(gameboard);
   }else if (finishflag){
-    finish_level_dialog();
+    prepare_reenter_game(gameboard);
+    finish_level_dialog(gameboard);
+  }else if (selectflag){
+    prepare_reenter_game(gameboard);
+    level_dialog(gameboard,0);
   }else{
-    gamestate_go();
+    prepare_reenter_game(gameboard);
+    reenter_game(gameboard);
   }
+  aboutflag=0;
+  pauseflag=0;
+  finishflag=0;
+  selectflag=0;
 
-  return 0;
 }
 
 long levelstate_total_hiscore(){
@@ -216,39 +298,80 @@
   return curr->highscore;
 }
 
-void levelstate_next(){
-  if(curr->next)
+int levelstate_next(){
+  if(!curr->next)
+    new_level();
+
+  if(curr->next){
     curr=curr->next;
+    graph_dirty=1;
+    return 1;
+  }
+  return 0;
 }
 
-void levelstate_prev(){
-  if(curr->prev)
+int levelstate_prev(){
+  if(curr->prev){
     curr=curr->prev;
+    graph_dirty=1;
+    return 1;
+  }
+  return 0;
 }
 
 int get_level_num(){
-  return curr->num;
+  return curr->gm.num;
 }
 
-char *get_level_name(){
-  return curr->name;
+char *get_level_desc(){
+  return curr->gm.desc;
 }
 
 void levelstate_finish(){
+  int score = graphscore_get_score(&gameboard->g) + 
+    graphscore_get_bonus(&gameboard->g);
   curr->in_progress=0;
-  if(get_score() > curr->highscore)
-    curr->highscore = get_score();
+  if(score > curr->highscore)
+    curr->highscore = score;
 }
 
+void levelstate_reset(){
+  curr->in_progress=0;
+  graph_dirty=1;
+}
+
+int levelstate_in_progress(){
+  return curr->in_progress;
+}
+
 /* commit to the currently selected level and set the game state to
    readiness using it */
 void levelstate_go(){
 
-  if(!curr->in_progress || read_board(curr->name)){
-    /* not on disk or couldn't load it.  Get a fresh version */
-    gamestate_generate(curr->level)
+  // we need to load the board if we're currently playing the board or sitting in the finish dialog right after
+  if(curr->in_progress || finishflag){
+    if(gameboard_read(curr->gm.id,gameboard)){
+      /* not on disk or couldn't load it.  clear level state flags and get a fresh version */
+      aboutflag=0;
+      pauseflag=0;
+      finishflag=0;
+      selectflag=0;
+      generate_board(&gameboard->g,curr->gm.num);
+      activate_verticies(&gameboard->g);
+      impress_location(&gameboard->g);
+    }
+  }else{
+    /* no board in progress; fetch a new board */
+    generate_board(&gameboard->g,curr->gm.num);
+    activate_verticies(&gameboard->g);
+    impress_location(&gameboard->g);
   }
-
   curr->in_progress=1;
+  graph_dirty=0;
+}
 
+cairo_surface_t *levelstate_get_icon(int num){
+  levelstate *l=ensure_level_num(num);
+  if(l==0)return 0;
+  return gameboard_read_icon(l->gm.id,(l->in_progress?"2":"1"),gameboard);
 }

Modified: trunk/planarity/levelstate.h
===================================================================
--- trunk/planarity/levelstate.h	2005-09-24 22:02:43 UTC (rev 10059)
+++ trunk/planarity/levelstate.h	2005-09-25 09:24:23 UTC (rev 10060)
@@ -1,10 +1,14 @@
-extern int levelstate_write(char *statedir);
-extern int levelstate_read(char *statedir);
+extern int levelstate_write();
+extern int levelstate_read();
 extern long levelstate_total_hiscore();
 extern long levelstate_get_hiscore();
-extern void levelstate_next();
-extern void levelstate_prev();
+extern int levelstate_in_progress();
+extern int levelstate_next();
+extern int levelstate_prev();
 extern int get_level_num();
-extern char *get_level_name();
+extern char *get_level_desc();
 extern void levelstate_finish();
 extern void levelstate_go();
+extern void levelstate_resume();
+extern void levelstate_reset();
+extern cairo_surface_t* levelstate_get_icon(int num);

Modified: trunk/planarity/main.c
===================================================================
--- trunk/planarity/main.c	2005-09-24 22:02:43 UTC (rev 10059)
+++ trunk/planarity/main.c	2005-09-25 09:24:23 UTC (rev 10060)
@@ -3,6 +3,7 @@
 #include <stdio.h>
 #include <stdlib.h>
 #include <errno.h>
+#include <signal.h>
 #include <sys/stat.h>
 #include <sys/types.h>
 #include <gtk/gtk.h>
@@ -10,13 +11,13 @@
 #include "graph.h"
 #include "gameboard.h"
 #include "levelstate.h"
+#include "main.h"
 
 #define boardstate "/.gPlanarity/boards/"
 #define mainstate "/.gPlanarity/"
 
-static char *boarddir;
-static char *statedir;
-
+char *boarddir;
+char *statedir;
 Gameboard *gameboard;
 GtkWidget *toplevel_window;
 graph maingraph;
@@ -38,6 +39,21 @@
   return 0;
 }     
 
+void request_resize(int width, int height){
+  gtk_window_resize(GTK_WINDOW(toplevel_window),width,height);
+}
+
+static void clean_exit(int sig){
+  signal(sig,SIG_IGN);
+  if(sig!=SIGINT)
+    fprintf(stderr,
+            "\nTrapped signal %d; saving state and exiting!\n",sig);
+
+  levelstate_write(statedir);
+  gtk_main_quit();
+  exit(0);
+}
+
 int main(int argc, char *argv[]){
   char *homedir = getenv("home");
   if(!homedir)
@@ -85,16 +101,24 @@
   g_signal_connect (G_OBJECT (toplevel_window), "delete-event",
                     G_CALLBACK (gtk_main_quit), NULL);
   
-  gameboard = gameboard_new (&maingraph);
+  gameboard = gameboard_new();
+  levelstate_read();
 
   gtk_container_add (GTK_CONTAINER (toplevel_window), GTK_WIDGET(gameboard));
   gtk_widget_show_all (toplevel_window);
   memset(&maingraph,0,sizeof(maingraph));
 
-  levelstate_read(statedir);
+  /* get the setup processed before we fire up animations */
+  while (gtk_events_pending ())
+    gtk_main_iteration ();
+  gdk_flush();
 
+  levelstate_resume();
+  //  signal(SIGINT,clean_exit);
+  //signal(SIGSEGV,clean_exit);
+
   gtk_main ();
 
-  levelstate_write(statedir);
+  levelstate_write();
   return 0;
 }

Added: trunk/planarity/main.h
===================================================================
--- trunk/planarity/main.h	2005-09-24 22:02:43 UTC (rev 10059)
+++ trunk/planarity/main.h	2005-09-25 09:24:23 UTC (rev 10060)
@@ -0,0 +1,5 @@
+extern char *boarddir;
+extern char *statedir;
+extern Gameboard *gameboard;
+
+extern void request_resize(int width, int height);

Deleted: trunk/planarity/pause.c
===================================================================
--- trunk/planarity/pause.c	2005-09-24 22:02:43 UTC (rev 10059)
+++ trunk/planarity/pause.c	2005-09-25 09:24:23 UTC (rev 10060)
@@ -1,330 +0,0 @@
-#include <math.h>
-#include <string.h>
-#include <gtk/gtk.h>
-#include <gdk/gdk.h>
-
-#include "graph.h"
-#include "gameboard.h"
-#include "gamestate.h"
-#include "button_base.h"
-#include "buttonbar.h"
-#include "pause.h"
-#include "box.h"
-
-static int ui_paused=0;
-static int ui_about=0;
-static gint timer;
-static void (*callback)(Gameboard *);
-
-extern char *version;
-
-/* perform a single frame of animation for all pause dialog buttons/rollovers */
-static gboolean pause_animate_buttons(gpointer ptr){
-  Gameboard *g=(Gameboard *)ptr;
-  int ret=0;
-
-  ret=animate_button_frame(g);
-
-  if(!ret && timer!=0){
-    g_source_remove(timer);
-    timer=0;
-  }
-
-  if(!ret && callback)
-    // undeploy finished... call the undeploy callback
-    callback(g);
-
-  return ret;
-}
-
-static void unpause_post (Gameboard *g){
-  // back to buttonbar activity!
-  pop_background(g);
-  deploy_buttonbar(g);
-  unpause();
-  ui_paused=0;
-  ui_about=0;
-} 
-
-static void unpause_quit (Gameboard *g){
-  gtk_main_quit();
-} 
-
-static void undeploy_buttons(Gameboard *g){
-  // undeploy pause buttons
-  button_clear_state(g);
-  buttons_ready=0;
-
-  {
-    buttonstate *b=states; 
-    b->target_x-=BUTTON_EXPOSE;
-  }
-  {
-    buttonstate *b=states+10; 
-    b->target_x+=BUTTON_EXPOSE;
-  }
-}
-
-static void local_unpause (Gameboard *g){
-  undeploy_buttons(g);
-  callback = unpause_post;
-  timer = g_timeout_add(BUTTON_ANIM_INTERVAL, pause_animate_buttons, (gpointer)g);
-}
-
-static void local_quit (Gameboard *g){
-  undeploy_buttons(g);
-  callback = unpause_quit;
-  timer = g_timeout_add(BUTTON_ANIM_INTERVAL, pause_animate_buttons, (gpointer)g);
-}
-
-/* initialize the rather weird little animation engine */
-static void setup_pause_buttons(Gameboard *g,int bw, int bh){
-  int i;
-  int w=get_board_width();
-  int h=get_board_height();
-
-  states[0].rollovertext="exit gPlanarity";
-  states[10].rollovertext="resume game!";
-
-  states[0].callback = local_quit;
-  states[10].callback = local_unpause;
-
-  for(i=0;i<NUMBUTTONS;i++)
-    states[i].position=0;
-
-  states[0].position = 2; //center;
-  states[10].position = 2; //center;
-
-  {
-    buttonstate *b=states;
-    b->target_x_active=
-      b->x_active=
-      b->target_x_active=
-      b->target_x= 
-      w/2 - bw/2 + PAUSE_BUTTON_BORDER;
-    b->x=b->target_x_inactive=b->x_inactive=b->target_x - BUTTON_EXPOSE;
-    b->y = h/2 + bh/2 - PAUSE_BUTTON_Y;
-  }
-
-  {
-    buttonstate *b=states+10;
-    b->target_x_active=
-      b->x_active=
-      b->target_x_active=
-      b->target_x= 
-      w/2 + bw/2 - PAUSE_BUTTON_BORDER;
-    b->x=b->target_x_inactive=b->x_inactive=b->target_x + BUTTON_EXPOSE;
-    b->y = h/2 + bh/2 - PAUSE_BUTTON_Y;
-  }
-
-  for(i=0;i<NUMBUTTONS;i++)
-    if(states[i].position)
-      rollover_extents(g,states+i);  
-}
-
-static void render_text_centered(cairo_t *c, char *s, int x, int y){
-  cairo_text_extents_t ex;
-
-  cairo_text_extents (c, s, &ex);
-  cairo_move_to (c, x-(ex.width/2)-ex.x_bearing, y-(ex.height/2)-ex.y_bearing);
-  cairo_show_text (c, s);  
-}
-
-static void draw_pausebox(Gameboard *g){
-  int w= get_board_width();
-  int h= get_board_height();
-
-  cairo_t *c = cairo_create(g->background);
-  borderbox_path(c,
-		 w/2 - PAUSEBOX_WIDTH/2,
-		 h/2 - PAUSEBOX_HEIGHT/2,
-		 PAUSEBOX_WIDTH,
-		 PAUSEBOX_HEIGHT);
-  cairo_set_source_rgb(c,1,1,1);
-  cairo_fill(c);
-
-  centerbox(c,
-	    w/2 - PAUSEBOX_WIDTH/2,
-	    h/2 - PAUSEBOX_HEIGHT/2,
-	    PAUSEBOX_WIDTH,
-	    SCOREHEIGHT);
-
-  centerbox(c,
-	    w/2 - PAUSEBOX_WIDTH/2 ,
-	    h/2 + PAUSEBOX_HEIGHT/2 - SCOREHEIGHT,
-	    PAUSEBOX_WIDTH,
-	    SCOREHEIGHT);
-
-  {
-    cairo_matrix_t ma;
-    char time[160];
-    int  ho = get_elapsed() / 3600;
-    int  mi = get_elapsed() / 60 - ho*60;
-    int  se = get_elapsed() - ho*3600 - mi*60;
-    
-    if(ho){
-      snprintf(time,160,"%d:%02d:%02d",ho,mi,se);
-    }else if (mi){
-      snprintf(time,160,"%d:%02d",mi,se);
-    }else{
-      snprintf(time,160,"%d seconds",se);
-    }
-
-    cairo_select_font_face (c, "Arial",
-			    CAIRO_FONT_SLANT_NORMAL,
-			    CAIRO_FONT_WEIGHT_BOLD);
-
-    cairo_matrix_init_scale (&ma, 18.,18.);
-    cairo_set_font_matrix (c,&ma);
-    cairo_set_source_rgba (c, TEXT_COLOR);
-
-    render_text_centered(c,"Game Paused", w/2,h/2-PAUSEBOX_HEIGHT/2+SCOREHEIGHT/2);
-    cairo_select_font_face (c, "Arial",
-			    CAIRO_FONT_SLANT_NORMAL,
-			    CAIRO_FONT_WEIGHT_NORMAL);
-    render_text_centered(c,"Time Elapsed:", w/2,h/2-30);
-    render_text_centered(c,time, w/2,h/2);
-  }
-
-  cairo_destroy(c);
-}
-
-static void pause_game_post_undeploy(Gameboard *g){
-  // set up new buttons
-  setup_pause_buttons(g,PAUSEBOX_WIDTH, PAUSEBOX_HEIGHT);
-
-  // draw pausebox
-  push_curtain(g,draw_pausebox);
-
-  // deploy new buttons
-  callback=0;
-  timer = g_timeout_add(BUTTON_ANIM_INTERVAL, pause_animate_buttons, (gpointer)g);
-  buttons_ready=1;
-}
-
-void pause_game(Gameboard *g){
-  // grab timer state
-  ui_paused=1;
-  pause();
-
-  push_background(g,0);
-  // undeploy buttonbar
-  undeploy_buttonbar(g,pause_game_post_undeploy);
-}
-
-// the 'about' box is nearly identical, including the fact it pauses the game.
-// we just piggyback it here
-
-static void draw_aboutbox(Gameboard *g){
-  int w= get_board_width();
-  int h= get_board_height();
-
-  cairo_t *c = cairo_create(g->background);
-  borderbox_path(c,
-		 w/2 - ABOUTBOX_WIDTH/2,
-		 h/2 - ABOUTBOX_HEIGHT/2,
-		 ABOUTBOX_WIDTH,
-		 ABOUTBOX_HEIGHT);
-  cairo_set_source_rgb(c,1,1,1);
-  cairo_fill(c);
-
-  centerbox(c,
-	    w/2 - ABOUTBOX_WIDTH/2,
-	    h/2 - ABOUTBOX_HEIGHT/2,
-	    ABOUTBOX_WIDTH,
-	    SCOREHEIGHT);
-
-  centerbox(c,
-	    w/2 - ABOUTBOX_WIDTH/2 ,
-	    h/2 + ABOUTBOX_HEIGHT/2 - SCOREHEIGHT,
-	    ABOUTBOX_WIDTH,
-	    SCOREHEIGHT);
-
-  {
-    cairo_matrix_t ma;
-    int y = h/2-ABOUTBOX_HEIGHT/2+SCOREHEIGHT/2;
-    cairo_select_font_face (c, "Sans",
-			    CAIRO_FONT_SLANT_NORMAL,
-			    CAIRO_FONT_WEIGHT_BOLD);
-
-    cairo_matrix_init_scale (&ma, 18.,18.);
-    cairo_set_font_matrix (c,&ma);
-    cairo_set_source_rgba (c, TEXT_COLOR);
-
-    render_text_centered(c,"gPlanarity", w/2,y);
-    cairo_select_font_face (c, "Sans",
-			    CAIRO_FONT_SLANT_NORMAL,
-			    CAIRO_FONT_WEIGHT_NORMAL);
-    y+=45;
-    render_text_centered(c,"Untangle the mess!", w/2,y);
-    y+=30;
-
-    cairo_matrix_init_scale (&ma, 13.,13.);
-    cairo_set_font_matrix (c,&ma);
-    render_text_centered(c,"Drag verticies to eliminate crossed lines.", w/2,y); y+=16;
-    render_text_centered(c,"The objective may be a complete solution or", w/2,y); y+=16;
-    render_text_centered(c,"getting as close as possible to solving an", w/2,y); y+=16;
-    render_text_centered(c,"unsolvable puzzle.  Work quickly and", w/2,y); y+=16;
-    render_text_centered(c,"exceed the objective for bonus points!", w/2,y); y+=16;
-
-    y+=16;
-    cairo_move_to (c, w/2-100,y);
-    cairo_line_to (c, w/2+100,y);
-    cairo_stroke(c);
-    y+=32;
-
-    cairo_matrix_init_scale (&ma, 12.,13.);
-    cairo_set_font_matrix (c,&ma);
-    render_text_centered(c,"gPlanarity written by Monty <monty at xiph.org>",w/2,y);y+=17;
-    render_text_centered(c,"as a demonstration of Gtk+/Cairo",w/2,y);y+=32;
-
-    render_text_centered(c,"Original Flash version of Planarity by",w/2,y);y+=17;
-    render_text_centered(c,"John Tantalo <john.tantalo at case.edu>",w/2,y);y+=32;
-
-    render_text_centered(c,"Original game concept by Mary Radcliffe",w/2,y);y+=17;
-
-
-    y = h/2+ABOUTBOX_HEIGHT/2-SCOREHEIGHT/2;
-    cairo_select_font_face (c, "Sans",
-			    CAIRO_FONT_SLANT_NORMAL,
-			    CAIRO_FONT_WEIGHT_BOLD);
-
-    cairo_matrix_init_scale (&ma, 10.,11.);
-    cairo_set_font_matrix (c,&ma);
-    render_text_centered(c,version, w/2,y);
-
-  }
-
-  cairo_destroy(c);
-}
-
-static void about_game_post_undeploy(Gameboard *g){
-  // set up new buttons
-  setup_pause_buttons(g,ABOUTBOX_WIDTH,ABOUTBOX_HEIGHT);
-
-  // draw about box
-  push_curtain(g,draw_aboutbox);
-
-  // deploy new buttons
-  callback=0;
-  timer = g_timeout_add(BUTTON_ANIM_INTERVAL, pause_animate_buttons, (gpointer)g);
-  buttons_ready=1;
-}
-
-void about_game(Gameboard *g){
-  // grab timer state
-  ui_about=1;
-  pause();
-  push_background(g,0);
-
-  // undeploy buttonbar
-  undeploy_buttonbar(g,about_game_post_undeploy);
-}
-
-int pause_dialog_active(){
-  return ui_paused;
-}
-
-int about_dialog_active(){
-  return ui_about;
-}

Deleted: trunk/planarity/pause.h
===================================================================
--- trunk/planarity/pause.h	2005-09-24 22:02:43 UTC (rev 10059)
+++ trunk/planarity/pause.h	2005-09-25 09:24:23 UTC (rev 10060)
@@ -1,13 +0,0 @@
-#define PAUSE_BUTTON_BORDER 35
-#define PAUSE_BUTTON_Y 25
-#define PAUSEBOX_WIDTH 180
-#define PAUSEBOX_HEIGHT 250
-
-#define ABOUTBOX_WIDTH 320
-#define ABOUTBOX_HEIGHT 400
-
-extern void pause_game(Gameboard *g);
-extern void about_game(Gameboard *g);
-
-extern int pause_dialog_active();
-extern int about_dialog_active();

Added: trunk/planarity/timer.c
===================================================================
--- trunk/planarity/timer.c	2005-09-24 22:02:43 UTC (rev 10059)
+++ trunk/planarity/timer.c	2005-09-25 09:24:23 UTC (rev 10060)
@@ -0,0 +1,52 @@
+#include <time.h>
+#include "timer.h"
+
+static int paused;
+static time_t begin_time_add;
+static time_t begin_time;
+static char timebuffer[160];
+
+time_t get_timer(){
+  if(paused)
+    return begin_time_add;
+  else{
+    time_t ret = time(NULL);
+    return ret - begin_time + begin_time_add;
+  }
+}
+
+char *get_timer_string(){
+  int  ho = get_timer() / 3600;
+  int  mi = get_timer() / 60 - ho*60;
+  int  se = get_timer() - ho*3600 - mi*60;
+  
+  if(ho){
+    snprintf(timebuffer,160,"%d:%02d:%02d",ho,mi,se);
+  }else if (mi){
+    snprintf(timebuffer,160,"%d:%02d",mi,se);
+  }else{
+    snprintf(timebuffer,160,"%d seconds",se);
+  }
+  
+  return timebuffer;
+}
+
+void set_timer(time_t off){
+  begin_time_add = off;
+  begin_time = time(NULL);
+}
+
+void pause_timer(){
+  begin_time_add = get_timer();
+  paused=1;
+}
+
+void unpause_timer(){
+  paused=0;
+  set_timer(begin_time_add);
+}
+
+int timer_paused_p(){
+  return paused;
+}
+

Added: trunk/planarity/timer.h
===================================================================
--- trunk/planarity/timer.h	2005-09-24 22:02:43 UTC (rev 10059)
+++ trunk/planarity/timer.h	2005-09-25 09:24:23 UTC (rev 10060)
@@ -0,0 +1,12 @@
+#define _GNU_SOURCE
+#include <stdlib.h>
+#include <stdio.h>
+#include <string.h>
+
+extern time_t get_timer();
+extern char *get_timer_string();
+extern void set_timer(time_t off);
+extern void pause_timer();
+extern void unpause_timer();
+extern int timer_paused_p();
+

Modified: trunk/planarity/version.h
===================================================================
--- trunk/planarity/version.h	2005-09-24 22:02:43 UTC (rev 10059)
+++ trunk/planarity/version.h	2005-09-25 09:24:23 UTC (rev 10060)
@@ -1,2 +1,2 @@
 #define VERSION "$Id$ "
-/* DO NOT EDIT: Automated versioning hack [Fri Aug 19 22:20:08 EDT 2005] */
+/* DO NOT EDIT: Automated versioning hack [Sun Sep 25 05:19:27 EDT 2005] */



More information about the commits mailing list