chiark / gitweb /
Implemented text and clipping primitives in the frontend, and added
[sgt-puzzles.git] / gtk.c
diff --git a/gtk.c b/gtk.c
index 102ed9c7a22666f1a165ebd3d1ed44807ca57246..778868a614d610d2fd65eb6a350299a331dfb713 100644 (file)
--- a/gtk.c
+++ b/gtk.c
@@ -34,6 +34,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
@@ -52,7 +58,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)
@@ -72,6 +80,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 +226,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 +268,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
@@ -233,12 +332,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 +401,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
@@ -389,6 +492,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);