Fix game IDs, which I broke in the orientability change. Also
[sgt/puzzles] / gtk.c
diff --git a/gtk.c b/gtk.c
index d27f739..b85cfa9 100644 (file)
--- a/gtk.c
+++ b/gtk.c
 #include <gtk/gtk.h>
 #include <gdk/gdkkeysyms.h>
 
+#if GTK_CHECK_VERSION(2,0,0) && !defined HAVE_SENSIBLE_ABSOLUTE_SIZE_FUNCTION
+#include <gdk/gdkx.h>
+#include <X11/Xlib.h>
+#endif
+
 #include "puzzles.h"
 
 /* ----------------------------------------------------------------------
@@ -153,11 +158,60 @@ void draw_text(frontend *fe, int x, int y, int fonttype, int fontsize,
         fe->fonts[i].type = fonttype;
         fe->fonts[i].size = fontsize;
 
+#if GTK_CHECK_VERSION(2,0,0)
         /*
-         * FIXME: Really I should make at least _some_ effort to
-         * pick the correct font.
+         * Use Pango to find the closest match to the requested
+         * font.
          */
-        fe->fonts[i].font = gdk_font_load("variable");
+        {
+            PangoFontDescription *fd;
+
+            fd = pango_font_description_new();
+            /* `Monospace' and `Sans' are meta-families guaranteed to exist */
+            pango_font_description_set_family(fd, fonttype == FONT_FIXED ?
+                                              "Monospace" : "Sans");
+            pango_font_description_set_weight(fd, PANGO_WEIGHT_BOLD);
+            /*
+             * I found some online Pango documentation which
+             * described a function called
+             * pango_font_description_set_absolute_size(), which is
+             * _exactly_ what I want here. Unfortunately, none of
+             * my local Pango installations have it (presumably
+             * they're too old), so I'm going to have to hack round
+             * it by figuring out the point size myself. This
+             * limits me to X and probably also breaks in later
+             * Pango installations, so ideally I should add another
+             * CHECK_VERSION type ifdef and use set_absolute_size
+             * where available. All very annoying.
+             */
+#ifdef HAVE_SENSIBLE_ABSOLUTE_SIZE_FUNCTION
+            pango_font_description_set_absolute_size(fd, PANGO_SCALE*fontsize);
+#else
+            {
+                Display *d = GDK_DISPLAY();
+                int s = DefaultScreen(d);
+                double resolution =
+                    (PANGO_SCALE * 72.27 / 25.4) * 
+                    ((double) DisplayWidthMM(d, s) / DisplayWidth (d, s));
+                pango_font_description_set_size(fd, resolution * fontsize);
+            }
+#endif
+            fe->fonts[i].font = gdk_font_from_description(fd);
+            pango_font_description_free(fd);
+        }
+
+        if (!fe->fonts[i].font)
+#endif
+            /*
+             * In GTK 1.2, I don't know of any plausible way to
+             * pick a suitable font, so I'm just going to be
+             * tedious.
+             * 
+             * This is also fallback code called if the Pango
+             * approach fails to find an appropriate font.
+             */
+            fe->fonts[i].font = gdk_font_load(fonttype == FONT_FIXED ?
+                                              "fixed" : "variable");
     }
 
     /*
@@ -166,11 +220,24 @@ void draw_text(frontend *fe, int x, int y, int fonttype, int fontsize,
     {
         int lb, rb, wid, asc, desc;
 
-        gdk_string_extents(fe->fonts[i].font, text,
+       /*
+        * Measure vertical string extents with respect to the same
+        * string always...
+        */
+        gdk_string_extents(fe->fonts[i].font,
+                          "ABCDEFGHIJKLMNOPQRSTUVWXYZ",
                            &lb, &rb, &wid, &asc, &desc);
         if (align & ALIGN_VCENTRE)
             y += asc - (asc+desc)/2;
 
+       /*
+        * ... but horizontal extents with respect to the provided
+        * string. This means that multiple pieces of text centred
+        * on the same y-coordinate don't have different baselines.
+        */
+        gdk_string_extents(fe->fonts[i].font, text,
+                           &lb, &rb, &wid, &asc, &desc);
+
         if (align & ALIGN_HCENTRE)
             x -= wid / 2;
         else if (align & ALIGN_HRIGHT)
@@ -783,7 +850,7 @@ static frontend *new_window(char *game_id, char **error)
 
     fe = snew(frontend);
 
-    fe->me = midend_new(fe);
+    fe->me = midend_new(fe, &thegame);
     if (game_id) {
         *error = midend_game_id(fe->me, game_id, FALSE);
         if (*error) {
@@ -795,7 +862,7 @@ static frontend *new_window(char *game_id, char **error)
     midend_new_game(fe->me);
 
     fe->window = gtk_window_new(GTK_WINDOW_TOPLEVEL);
-    gtk_window_set_title(GTK_WINDOW(fe->window), game_name);
+    gtk_window_set_title(GTK_WINDOW(fe->window), thegame.name);
 #if 0
     gtk_window_set_resizable(GTK_WINDOW(fe->window), FALSE);
 #else
@@ -827,7 +894,7 @@ static frontend *new_window(char *game_id, char **error)
                       GTK_SIGNAL_FUNC(menu_config_event), fe);
     gtk_widget_show(menuitem);
 
-    if ((n = midend_num_presets(fe->me)) > 0 || game_can_configure) {
+    if ((n = midend_num_presets(fe->me)) > 0 || thegame.can_configure) {
         GtkWidget *submenu;
         int i;
 
@@ -852,7 +919,7 @@ static frontend *new_window(char *game_id, char **error)
             gtk_widget_show(menuitem);
         }
 
-       if (game_can_configure) {
+       if (thegame.can_configure) {
             menuitem = gtk_menu_item_new_with_label("Custom...");
             gtk_object_set_data(GTK_OBJECT(menuitem), "user-data",
                                GPOINTER_TO_INT(CFG_SETTINGS));
@@ -965,14 +1032,70 @@ int main(int argc, char **argv)
     char *pname = argv[0];
     char *error;
 
-    gtk_init(&argc, &argv);
+    /*
+     * Special standalone mode for generating puzzle IDs on the
+     * command line. Useful for generating puzzles to be printed
+     * out and solved offline (for puzzles where that even makes
+     * sense - Solo, for example, is a lot more pencil-and-paper
+     * friendly than Net!)
+     * 
+     * Usage:
+     * 
+     *   <puzzle-name> --generate [<n> [<params>]]
+     * 
+     * <n>, if present, is the number of puzzle IDs to generate.
+     * <params>, if present, is the same type of parameter string
+     * you would pass to the puzzle when running it in GUI mode,
+     * including optional extras such as the expansion factor in
+     * Rectangles and the difficulty level in Solo.
+     * 
+     * If you specify <params>, you must also specify <n> (although
+     * you may specify it to be 1). Sorry; that was the
+     * simplest-to-parse command-line syntax I came up with.
+     */
+    if (argc > 1 && !strcmp(argv[1], "--generate")) {
+       int n = 1;
+       char *params = NULL;
+       game_params *par;
+       random_state *rs;
+       char *parstr;
+
+       {
+           void *seed;
+           int seedlen;
+           get_random_seed(&seed, &seedlen);
+           rs = random_init(seed, seedlen);
+       }
 
-    if (!new_window(argc > 1 ? argv[1] : NULL, &error)) {
-        fprintf(stderr, "%s: %s\n", pname, error);
-        return 1;
-    }
+       if (argc > 2)
+           n = atoi(argv[2]);
+       if (argc > 3)
+           params = argv[3];
+
+       if (params)
+           par = thegame.decode_params(params);
+       else
+           par = thegame.default_params();
+       parstr = thegame.encode_params(par);
+
+       while (n-- > 0) {
+           char *seed = thegame.new_seed(par, rs);
+           printf("%s:%s\n", parstr, seed);
+           sfree(seed);
+       }
 
-    gtk_main();
+       return 0;
+    } else {
+
+       gtk_init(&argc, &argv);
+
+       if (!new_window(argc > 1 ? argv[1] : NULL, &error)) {
+           fprintf(stderr, "%s: %s\n", pname, error);
+           return 1;
+       }
+
+       gtk_main();
+    }
 
     return 0;
 }