X-Git-Url: https://git.distorted.org.uk/~mdw/sgt/puzzles/blobdiff_plain/eb2ad6f1d0284c83b3cb5caf631c659441189d7d..ccd4e210f26dbe842ef9417e0df8c0a06f5c822f:/gtk.c diff --git a/gtk.c b/gtk.c index 102ed9c..9ffc96a 100644 --- a/gtk.c +++ b/gtk.c @@ -3,6 +3,7 @@ */ #include +#include #include #include #include @@ -34,6 +35,12 @@ void fatal(char *fmt, ...) * GTK front end to puzzles. */ +struct font { + GdkFont *font; + int type; + int size; +}; + /* * This structure holds all the data relevant to a single window. * In principle this would allow us to open multiple independent @@ -44,6 +51,8 @@ void fatal(char *fmt, ...) struct frontend { GtkWidget *window; GtkWidget *area; + GtkWidget *statusbar; + guint statusctx; GdkPixmap *pixmap; GdkColor *colours; int ncolours; @@ -52,7 +61,9 @@ struct frontend { midend_data *me; GdkGC *gc; int bbox_l, bbox_r, bbox_u, bbox_d; - int timer_active; + int timer_active, timer_id; + struct font *fonts; + int nfonts, fontsize; }; void frontend_default_colour(frontend *fe, float *output) @@ -63,6 +74,14 @@ void frontend_default_colour(frontend *fe, float *output) output[2] = col.blue / 65535.0; } +void status_bar(frontend *fe, char *text) +{ + assert(fe->statusbar); + + gtk_statusbar_pop(GTK_STATUSBAR(fe->statusbar), fe->statusctx); + gtk_statusbar_push(GTK_STATUSBAR(fe->statusbar), fe->statusctx, text); +} + void start_draw(frontend *fe) { fe->gc = gdk_gc_new(fe->area->window); @@ -72,6 +91,85 @@ void start_draw(frontend *fe) fe->bbox_d = 0; } +void clip(frontend *fe, int x, int y, int w, int h) +{ + GdkRectangle rect; + + rect.x = x; + rect.y = y; + rect.width = w; + rect.height = h; + + gdk_gc_set_clip_rectangle(fe->gc, &rect); +} + +void unclip(frontend *fe) +{ + GdkRectangle rect; + + rect.x = 0; + rect.y = 0; + rect.width = fe->w; + rect.height = fe->h; + + gdk_gc_set_clip_rectangle(fe->gc, &rect); +} + +void draw_text(frontend *fe, int x, int y, int fonttype, int fontsize, + int align, int colour, char *text) +{ + int i; + + /* + * Find or create the font. + */ + for (i = 0; i < fe->nfonts; i++) + if (fe->fonts[i].type == fonttype && fe->fonts[i].size == fontsize) + break; + + if (i == fe->nfonts) { + if (fe->fontsize <= fe->nfonts) { + fe->fontsize = fe->nfonts + 10; + fe->fonts = sresize(fe->fonts, fe->fontsize, struct font); + } + + fe->nfonts++; + + fe->fonts[i].type = fonttype; + fe->fonts[i].size = fontsize; + + /* + * FIXME: Really I should make at least _some_ effort to + * pick the correct font. + */ + fe->fonts[i].font = gdk_font_load("variable"); + } + + /* + * Find string dimensions and process alignment. + */ + { + int lb, rb, wid, asc, desc; + + gdk_string_extents(fe->fonts[i].font, text, + &lb, &rb, &wid, &asc, &desc); + if (align & ALIGN_VCENTRE) + y += asc - (asc+desc)/2; + + if (align & ALIGN_HCENTRE) + x -= wid / 2; + else if (align & ALIGN_HRIGHT) + x -= wid; + + } + + /* + * Set colour and actually draw text. + */ + gdk_gc_set_foreground(fe->gc, &fe->colours[colour]); + gdk_draw_string(fe->pixmap, fe->fonts[i].font, fe->gc, x, y, text); +} + void draw_rect(frontend *fe, int x, int y, int w, int h, int colour) { gdk_gc_set_foreground(fe->gc, &fe->colours[colour]); @@ -139,14 +237,26 @@ static gint key_event(GtkWidget *widget, GdkEventKey *event, gpointer data) if (event->string[0] && !event->string[1]) keyval = (unsigned char)event->string[0]; - else if (event->keyval == GDK_Up || event->keyval == GDK_KP_Up) + else if (event->keyval == GDK_Up || event->keyval == GDK_KP_Up || + event->keyval == GDK_KP_8) keyval = CURSOR_UP; - else if (event->keyval == GDK_Down || event->keyval == GDK_KP_Down) + else if (event->keyval == GDK_Down || event->keyval == GDK_KP_Down || + event->keyval == GDK_KP_2) keyval = CURSOR_DOWN; - else if (event->keyval == GDK_Left || event->keyval == GDK_KP_Left) + else if (event->keyval == GDK_Left || event->keyval == GDK_KP_Left || + event->keyval == GDK_KP_4) keyval = CURSOR_LEFT; - else if (event->keyval == GDK_Right || event->keyval == GDK_KP_Right) + else if (event->keyval == GDK_Right || event->keyval == GDK_KP_Right || + event->keyval == GDK_KP_6) keyval = CURSOR_RIGHT; + else if (event->keyval == GDK_KP_Home || event->keyval == GDK_KP_7) + keyval = CURSOR_UP_LEFT; + else if (event->keyval == GDK_KP_End || event->keyval == GDK_KP_1) + keyval = CURSOR_DOWN_LEFT; + else if (event->keyval == GDK_KP_Page_Up || event->keyval == GDK_KP_9) + keyval = CURSOR_UP_RIGHT; + else if (event->keyval == GDK_KP_Page_Down || event->keyval == GDK_KP_3) + keyval = CURSOR_DOWN_RIGHT; else keyval = -1; @@ -169,10 +279,10 @@ static gint button_event(GtkWidget *widget, GdkEventButton *event, if (event->type != GDK_BUTTON_PRESS) return TRUE; - if (event->button == 1) - button = LEFT_BUTTON; - else if (event->button == 2) + if (event->button == 2 || (event->state & GDK_SHIFT_MASK)) button = MIDDLE_BUTTON; + else if (event->button == 1) + button = LEFT_BUTTON; else if (event->button == 3) button = RIGHT_BUTTON; else @@ -200,6 +310,21 @@ static gint expose_area(GtkWidget *widget, GdkEventExpose *event, return TRUE; } +static gint map_window(GtkWidget *widget, GdkEvent *event, + gpointer data) +{ + frontend *fe = (frontend *)data; + + /* + * Apparently we need to do this because otherwise the status + * bar will fail to update immediately. Annoying, but there we + * go. + */ + gtk_widget_queue_draw(fe->window); + + return TRUE; +} + static gint configure_area(GtkWidget *widget, GdkEventConfigure *event, gpointer data) { @@ -233,12 +358,15 @@ static gint timer_func(gpointer data) void deactivate_timer(frontend *fe) { + if (fe->timer_active) + gtk_timeout_remove(fe->timer_id); fe->timer_active = FALSE; } void activate_timer(frontend *fe) { - gtk_timeout_add(20, timer_func, fe); + if (!fe->timer_active) + fe->timer_id = gtk_timeout_add(20, timer_func, fe); fe->timer_active = TRUE; } @@ -299,6 +427,7 @@ static frontend *new_window(void) midend_new_game(fe->me, NULL); fe->window = gtk_window_new(GTK_WINDOW_TOPLEVEL); + gtk_window_set_title(GTK_WINDOW(fe->window), game_name); #if 0 gtk_window_set_resizable(GTK_WINDOW(fe->window), FALSE); #else @@ -380,6 +509,17 @@ static frontend *new_window(void) } } + if (midend_wants_statusbar(fe->me)) { + fe->statusbar = gtk_statusbar_new(); + gtk_box_pack_end(vbox, fe->statusbar, FALSE, FALSE, 0); + gtk_widget_show(fe->statusbar); + fe->statusctx = gtk_statusbar_get_context_id + (GTK_STATUSBAR(fe->statusbar), "game"); + gtk_statusbar_push(GTK_STATUSBAR(fe->statusbar), fe->statusctx, + ""); + } else + fe->statusbar = NULL; + fe->area = gtk_drawing_area_new(); midend_size(fe->me, &x, &y); gtk_drawing_area_size(GTK_DRAWING_AREA(fe->area), x, y); @@ -389,6 +529,10 @@ static frontend *new_window(void) gtk_box_pack_end(vbox, fe->area, FALSE, FALSE, 0); fe->pixmap = NULL; + fe->fonts = NULL; + fe->nfonts = fe->fontsize = 0; + + fe->timer_active = FALSE; gtk_signal_connect(GTK_OBJECT(fe->window), "destroy", GTK_SIGNAL_FUNC(destroy), fe); @@ -398,6 +542,8 @@ static frontend *new_window(void) GTK_SIGNAL_FUNC(button_event), fe); gtk_signal_connect(GTK_OBJECT(fe->area), "expose_event", GTK_SIGNAL_FUNC(expose_area), fe); + gtk_signal_connect(GTK_OBJECT(fe->window), "map_event", + GTK_SIGNAL_FUNC(map_window), fe); gtk_signal_connect(GTK_OBJECT(fe->area), "configure_event", GTK_SIGNAL_FUNC(configure_area), fe);