X-Git-Url: http://www.chiark.greenend.org.uk/ucgi/~ian/git?a=blobdiff_plain;f=gtk.c;h=b1175efd03c6d8a2b1ebc932f17d2a2f0e64d470;hb=db313b3948d27244dd7c34c2609c66d6204d8931;hp=94a072216902fbc21398e95535c136b5c30b74d0;hpb=d0500732f7bdab97ecaece2c5c74dd18f8122ea4;p=sgt-puzzles.git diff --git a/gtk.c b/gtk.c index 94a0722..b1175ef 100644 --- a/gtk.c +++ b/gtk.c @@ -140,10 +140,13 @@ struct font { */ struct frontend { GtkWidget *window; - GtkAccelGroup *accelgroup; + GtkAccelGroup *dummy_accelgroup; GtkWidget *area; GtkWidget *statusbar; GtkWidget *menubar; +#if GTK_CHECK_VERSION(3,20,0) + GtkCssProvider *css_provider; +#endif guint statusctx; int w, h; midend *me; @@ -179,7 +182,6 @@ struct frontend { char *filesel_name; #endif GSList *preset_radio; - int n_preset_menu_items; int preset_threaded; GtkWidget *preset_custom; GtkWidget *copy_menu_item; @@ -187,6 +189,38 @@ struct frontend { int drawing_area_shrink_pending; int menubar_is_local; #endif +#if GTK_CHECK_VERSION(3,0,0) + /* + * This is used to get round an annoying lack of GTK notification + * message. If we request a window resize with + * gtk_window_resize(), we normally get back a "configure" event + * on the window and on its drawing area, and we respond to the + * latter by doing an appropriate resize of the puzzle. If the + * window is maximised, so that gtk_window_resize() _doesn't_ + * change its size, then that configure event never shows up. But + * if we requested the resize in response to a change of puzzle + * parameters (say, the user selected a differently-sized preset + * from the menu), then we would still like to be _notified_ that + * the window size was staying the same, so that we can respond by + * choosing an appropriate tile size for the new puzzle preset in + * the existing window size. + * + * Fortunately, in GTK 3, we may not get a "configure" event on + * the drawing area in this situation, but we still get a + * "size_allocate" event on the whole window (which, in other + * situations when we _do_ get a "configure" on the area, turns up + * second). So we treat _that_ event as indicating that if the + * "configure" event hasn't already shown up then it's not going + * to arrive. + * + * This flag is where we bookkeep this system. On + * gtk_window_resize we set this flag to true; the area's + * configure handler sets it back to false; then if that doesn't + * happen, the window's size_allocate handler does a fallback + * puzzle resize when it sees this flag still set to true. + */ + int awaiting_resize_ack; +#endif }; struct blitter { @@ -290,7 +324,27 @@ static void set_colour(frontend *fe, int colour) static void set_window_background(frontend *fe, int colour) { -#if GTK_CHECK_VERSION(3,0,0) +#if GTK_CHECK_VERSION(3,20,0) + char css_buf[512]; + sprintf(css_buf, ".background { " + "background-color: #%02x%02x%02x; }", + (unsigned)(fe->colours[3*colour + 0] * 255), + (unsigned)(fe->colours[3*colour + 1] * 255), + (unsigned)(fe->colours[3*colour + 2] * 255)); + if (!fe->css_provider) + fe->css_provider = gtk_css_provider_new(); + if (!gtk_css_provider_load_from_data( + GTK_CSS_PROVIDER(fe->css_provider), css_buf, -1, NULL)) + assert(0 && "Couldn't load CSS"); + gtk_style_context_add_provider( + gtk_widget_get_style_context(fe->window), + GTK_STYLE_PROVIDER(fe->css_provider), + GTK_STYLE_PROVIDER_PRIORITY_APPLICATION); + gtk_style_context_add_provider( + gtk_widget_get_style_context(fe->area), + GTK_STYLE_PROVIDER(fe->css_provider), + GTK_STYLE_PROVIDER_PRIORITY_APPLICATION); +#elif GTK_CHECK_VERSION(3,0,0) GdkRGBA rgba; rgba.red = fe->colours[3*colour + 0]; rgba.green = fe->colours[3*colour + 1]; @@ -439,11 +493,13 @@ static void clear_backing_store(frontend *fe) fe->image = NULL; } -static void wipe_and_destroy_cairo(frontend *fe, cairo_t *cr) +static void wipe_and_maybe_destroy_cairo(frontend *fe, cairo_t *cr, + int destroy) { cairo_set_source_rgb(cr, fe->colours[0], fe->colours[1], fe->colours[2]); cairo_paint(cr); - cairo_destroy(cr); + if (destroy) + cairo_destroy(cr); } static void setup_backing_store(frontend *fe) @@ -455,12 +511,29 @@ static void setup_backing_store(frontend *fe) fe->image = cairo_image_surface_create(CAIRO_FORMAT_RGB24, fe->pw, fe->ph); - wipe_and_destroy_cairo(fe, cairo_create(fe->image)); + wipe_and_maybe_destroy_cairo(fe, cairo_create(fe->image), TRUE); #ifndef USE_CAIRO_WITHOUT_PIXMAP - wipe_and_destroy_cairo(fe, gdk_cairo_create(fe->pixmap)); + wipe_and_maybe_destroy_cairo(fe, gdk_cairo_create(fe->pixmap), TRUE); +#endif +#if GTK_CHECK_VERSION(3,22,0) + { + GdkWindow *gdkwin; + cairo_region_t *region; + GdkDrawingContext *drawctx; + cairo_t *cr; + + gdkwin = gtk_widget_get_window(fe->area); + region = gdk_window_get_clip_region(gdkwin); + drawctx = gdk_window_begin_draw_frame(gdkwin, region); + cr = gdk_drawing_context_get_cairo_context(drawctx); + wipe_and_maybe_destroy_cairo(fe, cr, FALSE); + gdk_window_end_draw_frame(gdkwin, drawctx); + cairo_region_destroy(region); + } +#else + wipe_and_maybe_destroy_cairo( + fe, gdk_cairo_create(gtk_widget_get_window(fe->area)), TRUE); #endif - wipe_and_destroy_cairo(fe, gdk_cairo_create - (gtk_widget_get_window(fe->area))); } static int backing_store_ok(frontend *fe) @@ -1119,16 +1192,6 @@ static gint key_event(GtkWidget *widget, GdkEventKey *event, gpointer data) if (!backing_store_ok(fe)) return TRUE; -#if !GTK_CHECK_VERSION(2,0,0) - /* Gtk 1.2 passes a key event to this function even if it's also - * defined as an accelerator. - * Gtk 2 doesn't do this, and this function appears not to exist there. */ - if (fe->accelgroup && - gtk_accel_group_get_entry(fe->accelgroup, - event->keyval, event->state)) - return TRUE; -#endif - /* Handle mnemonics. */ if (gtk_window_activate_key(GTK_WINDOW(fe->window), event)) return TRUE; @@ -1175,6 +1238,8 @@ static gint key_event(GtkWidget *widget, GdkEventKey *event, gpointer data) event->keyval == GDK_KEY_Delete || event->keyval == GDK_KEY_KP_Delete) keyval = '\177'; + else if ((event->keyval == 'z' || event->keyval == 'Z') && shift && ctrl) + keyval = UI_REDO; else if (event->string[0] && !event->string[1]) keyval = (unsigned char)event->string[0]; else @@ -1305,35 +1370,51 @@ static gint map_window(GtkWidget *widget, GdkEvent *event, return TRUE; } -static gint configure_area(GtkWidget *widget, - GdkEventConfigure *event, gpointer data) +static void resize_puzzle_to_area(frontend *fe, int x, int y) { - frontend *fe = (frontend *)data; - int x, y; + int oldw = fe->w, oldpw = fe->pw, oldh = fe->h, oldph = fe->ph; - x = event->width; - y = event->height; + fe->w = x; + fe->h = y; + midend_size(fe->me, &x, &y, TRUE); + fe->pw = x; + fe->ph = y; + fe->ox = (fe->w - fe->pw) / 2; + fe->oy = (fe->h - fe->ph) / 2; - if (x != fe->w || y != fe->h || !backing_store_ok(fe)) { + if (oldw != fe->w || oldpw != fe->pw || + oldh != fe->h || oldph != fe->ph || !backing_store_ok(fe)) { if (backing_store_ok(fe)) teardown_backing_store(fe); - - fe->w = x; - fe->h = y; - midend_size(fe->me, &x, &y, TRUE); - fe->pw = x; - fe->ph = y; - fe->ox = (fe->w - fe->pw) / 2; - fe->oy = (fe->h - fe->ph) / 2; - setup_backing_store(fe); } midend_force_redraw(fe->me); +} +static gint configure_area(GtkWidget *widget, + GdkEventConfigure *event, gpointer data) +{ + frontend *fe = (frontend *)data; + resize_puzzle_to_area(fe, event->width, event->height); + fe->awaiting_resize_ack = FALSE; return TRUE; } +#if GTK_CHECK_VERSION(3,0,0) +static void window_size_alloc(GtkWidget *widget, GtkAllocation *allocation, + gpointer data) +{ + frontend *fe = (frontend *)data; + if (fe->awaiting_resize_ack) { + GtkAllocation a; + gtk_widget_get_allocation(fe->area, &a); + resize_puzzle_to_area(fe, a.width, a.height); + fe->awaiting_resize_ack = FALSE; + } +} +#endif + static gint timer_func(gpointer data) { frontend *fe = (frontend *)data; @@ -1398,6 +1479,13 @@ static void align_label(GtkLabel *label, double x, double y) #if GTK_CHECK_VERSION(3,16,0) gtk_label_set_xalign(label, x); gtk_label_set_yalign(label, y); +#elif GTK_CHECK_VERSION(3,14,0) + gtk_widget_set_halign(GTK_WIDGET(label), + x == 0 ? GTK_ALIGN_START : + x == 1 ? GTK_ALIGN_END : GTK_ALIGN_CENTER); + gtk_widget_set_valign(GTK_WIDGET(label), + y == 0 ? GTK_ALIGN_START : + y == 1 ? GTK_ALIGN_END : GTK_ALIGN_CENTER); #else gtk_misc_set_alignment(GTK_MISC(label), x, y); #endif @@ -1835,20 +1923,21 @@ static void changed_preset(frontend *fe) TRUE); } else { GSList *gs = fe->preset_radio; - int i = fe->n_preset_menu_items - 1 - n; - if (fe->preset_custom) - gs = gs->next; - while (i && gs) { - i--; - gs = gs->next; - } - if (gs) { - gtk_check_menu_item_set_active(GTK_CHECK_MENU_ITEM(gs->data), - TRUE); - } else for (gs = fe->preset_radio; gs; gs = gs->next) { - gtk_check_menu_item_set_active(GTK_CHECK_MENU_ITEM(gs->data), - FALSE); - } + GSList *found = NULL; + + for (gs = fe->preset_radio; gs; gs = gs->next) { + struct preset_menu_entry *entry = + (struct preset_menu_entry *)g_object_get_data( + G_OBJECT(gs->data), "user-data"); + if (!entry || entry->id != n) + gtk_check_menu_item_set_active( + GTK_CHECK_MENU_ITEM(gs->data), FALSE); + else + found = gs; + } + if (found) + gtk_check_menu_item_set_active( + GTK_CHECK_MENU_ITEM(found->data), TRUE); } fe->preset_threaded = FALSE; @@ -1928,6 +2017,24 @@ static gint configure_window(GtkWidget *widget, return FALSE; } +#if GTK_CHECK_VERSION(3,0,0) +static int window_extra_height(frontend *fe) +{ + int ret = 0; + if (fe->menubar) { + GtkRequisition req; + gtk_widget_get_preferred_size(fe->menubar, &req, NULL); + ret += req.height; + } + if (fe->statusbar) { + GtkRequisition req; + gtk_widget_get_preferred_size(fe->statusbar, &req, NULL); + ret += req.height; + } + return ret; +} +#endif + static void resize_fe(frontend *fe) { int x, y; @@ -1935,7 +2042,8 @@ static void resize_fe(frontend *fe) get_size(fe, &x, &y); #if GTK_CHECK_VERSION(3,0,0) - gtk_window_resize_to_geometry(GTK_WINDOW(fe->window), x, y); + gtk_window_resize(GTK_WINDOW(fe->window), x, y + window_extra_height(fe)); + fe->awaiting_resize_ack = TRUE; #else fe->drawing_area_shrink_pending = FALSE; gtk_drawing_area_size(GTK_DRAWING_AREA(fe->area), x, y); @@ -1952,17 +2060,19 @@ static void resize_fe(frontend *fe) static void menu_preset_event(GtkMenuItem *menuitem, gpointer data) { frontend *fe = (frontend *)data; - game_params *params = - (game_params *)g_object_get_data(G_OBJECT(menuitem), "user-data"); + struct preset_menu_entry *entry = + (struct preset_menu_entry *)g_object_get_data( + G_OBJECT(menuitem), "user-data"); if (fe->preset_threaded || (GTK_IS_CHECK_MENU_ITEM(menuitem) && !gtk_check_menu_item_get_active(GTK_CHECK_MENU_ITEM(menuitem)))) return; - midend_set_params(fe->me, params); + midend_set_params(fe->me, entry->params); midend_new_game(fe->me); changed_preset(fe); resize_fe(fe); + midend_redraw(fe->me); } GdkAtom compound_text_atom, utf8_string_atom; @@ -2098,8 +2208,9 @@ static char *file_selector(frontend *fe, char *title, int save) NULL); if (gtk_dialog_run(GTK_DIALOG(filesel)) == GTK_RESPONSE_ACCEPT) { - const char *name = gtk_file_chooser_get_filename(GTK_FILE_CHOOSER(filesel)); + char *name = gtk_file_chooser_get_filename(GTK_FILE_CHOOSER(filesel)); filesel_name = dupstr(name); + g_free(name); } gtk_widget_destroy(filesel); @@ -2149,15 +2260,14 @@ static void menu_save_event(GtkMenuItem *menuitem, gpointer data) " file \"%.*s\"?", FILENAME_MAX, name); if (!message_box(fe->window, "Question", buf, TRUE, MB_YESNO)) - return; + goto free_and_return; } fp = fopen(name, "w"); - sfree(name); if (!fp) { error_box(fe->window, "Unable to open save file"); - return; + goto free_and_return; } { @@ -2171,10 +2281,11 @@ static void menu_save_event(GtkMenuItem *menuitem, gpointer data) sprintf(boxmsg, "Error writing save file: %.400s", strerror(errno)); error_box(fe->window, boxmsg); - return; + goto free_and_return; } } - + free_and_return: + sfree(name); } } @@ -2205,6 +2316,7 @@ static void menu_load_event(GtkMenuItem *menuitem, gpointer data) changed_preset(fe); resize_fe(fe); + midend_redraw(fe->me); } } @@ -2242,6 +2354,7 @@ static void menu_config_event(GtkMenuItem *menuitem, gpointer data) midend_new_game(fe->me); resize_fe(fe); + midend_redraw(fe->me); } static void menu_about_event(GtkMenuItem *menuitem, gpointer data) @@ -2275,32 +2388,34 @@ static void menu_about_event(GtkMenuItem *menuitem, gpointer data) #endif } -static GtkWidget *add_menu_item_with_key(frontend *fe, GtkContainer *cont, - char *text, int key) +static GtkWidget *add_menu_ui_item( + frontend *fe, GtkContainer *cont, char *text, int action, + int accel_key, int accel_keyqual) { GtkWidget *menuitem = gtk_menu_item_new_with_label(text); - int keyqual; gtk_container_add(cont, menuitem); - g_object_set_data(G_OBJECT(menuitem), "user-data", GINT_TO_POINTER(key)); + g_object_set_data(G_OBJECT(menuitem), "user-data", + GINT_TO_POINTER(action)); g_signal_connect(G_OBJECT(menuitem), "activate", G_CALLBACK(menu_key_event), fe); - switch (key & ~0x1F) { - case 0x00: - key += 0x60; - keyqual = GDK_CONTROL_MASK; - break; - case 0x40: - key += 0x20; - keyqual = GDK_SHIFT_MASK; - break; - default: - keyqual = 0; - break; + + if (accel_key) { + /* + * Display a keyboard accelerator alongside this menu item. + * Actually this won't be processed via the usual GTK + * accelerator system, because we add it to a dummy + * accelerator group which is never actually activated on the + * main window; this permits back ends to override special + * keys like 'n' and 'r' and 'u' in some UI states. So + * whatever keystroke we display here will still go to + * key_event and be handled in the normal way. + */ + gtk_widget_add_accelerator(menuitem, + "activate", fe->dummy_accelgroup, + accel_key, accel_keyqual, + GTK_ACCEL_VISIBLE | GTK_ACCEL_LOCKED); } - gtk_widget_add_accelerator(menuitem, - "activate", fe->accelgroup, - key, keyqual, - GTK_ACCEL_VISIBLE); + gtk_widget_show(menuitem); return menuitem; } @@ -2312,6 +2427,36 @@ static void add_menu_separator(GtkContainer *cont) gtk_widget_show(menuitem); } +static void populate_gtk_preset_menu(frontend *fe, struct preset_menu *menu, + GtkWidget *gtkmenu) +{ + int i; + + for (i = 0; i < menu->n_entries; i++) { + struct preset_menu_entry *entry = &menu->entries[i]; + GtkWidget *menuitem; + + if (entry->params) { + menuitem = gtk_radio_menu_item_new_with_label( + fe->preset_radio, entry->title); + fe->preset_radio = gtk_radio_menu_item_get_group( + GTK_RADIO_MENU_ITEM(menuitem)); + g_object_set_data(G_OBJECT(menuitem), "user-data", entry); + g_signal_connect(G_OBJECT(menuitem), "activate", + G_CALLBACK(menu_preset_event), fe); + } else { + GtkWidget *submenu; + menuitem = gtk_menu_item_new_with_label(entry->title); + submenu = gtk_menu_new(); + gtk_menu_item_set_submenu(GTK_MENU_ITEM(menuitem), submenu); + populate_gtk_preset_menu(fe, entry->submenu, submenu); + } + + gtk_container_add(GTK_CONTAINER(gtkmenu), menuitem); + gtk_widget_show(menuitem); + } +} + enum { ARG_EITHER, ARG_SAVE, ARG_ID }; /* for argtype */ static frontend *new_window(char *arg, int argtype, char **error) @@ -2324,8 +2469,12 @@ static frontend *new_window(char *arg, int argtype, char **error) char errbuf[1024]; extern char *const *const xpm_icons[]; extern const int n_xpm_icons; + struct preset_menu *preset_menu; fe = snew(frontend); +#if GTK_CHECK_VERSION(3,20,0) + fe->css_provider = NULL; +#endif fe->timer_active = FALSE; fe->timer_id = -1; @@ -2421,6 +2570,10 @@ static frontend *new_window(char *arg, int argtype, char **error) } #endif +#if GTK_CHECK_VERSION(3,0,0) + fe->awaiting_resize_ack = FALSE; +#endif + fe->window = gtk_window_new(GTK_WINDOW_TOPLEVEL); gtk_window_set_title(GTK_WINDOW(fe->window), thegame.name); @@ -2428,8 +2581,11 @@ static frontend *new_window(char *arg, int argtype, char **error) gtk_container_add(GTK_CONTAINER(fe->window), GTK_WIDGET(vbox)); gtk_widget_show(GTK_WIDGET(vbox)); - fe->accelgroup = gtk_accel_group_new(); - gtk_window_add_accel_group(GTK_WINDOW(fe->window), fe->accelgroup); + fe->dummy_accelgroup = gtk_accel_group_new(); + /* + * Intentionally _not_ added to the window via + * gtk_window_add_accel_group; see menu_key_event + */ hbox = GTK_BOX(gtk_hbox_new(FALSE, 0)); gtk_box_pack_start(vbox, GTK_WIDGET(hbox), FALSE, FALSE, 0); @@ -2446,7 +2602,7 @@ static frontend *new_window(char *arg, int argtype, char **error) menu = gtk_menu_new(); gtk_menu_item_set_submenu(GTK_MENU_ITEM(menuitem), menu); - add_menu_item_with_key(fe, GTK_CONTAINER(menu), "New", 'n'); + add_menu_ui_item(fe, GTK_CONTAINER(menu), "New", UI_NEWGAME, 'n', 0); menuitem = gtk_menu_item_new_with_label("Restart"); gtk_container_add(GTK_CONTAINER(menu), menuitem); @@ -2472,11 +2628,11 @@ static frontend *new_window(char *arg, int argtype, char **error) fe->preset_radio = NULL; fe->preset_custom = NULL; - fe->n_preset_menu_items = 0; fe->preset_threaded = FALSE; - if ((n = midend_num_presets(fe->me)) > 0 || thegame.can_configure) { + + preset_menu = midend_get_presets(fe->me, NULL); + if (preset_menu->n_entries > 0 || thegame.can_configure) { GtkWidget *submenu; - int i; menuitem = gtk_menu_item_new_with_mnemonic("_Type"); gtk_container_add(GTK_CONTAINER(fe->menubar), menuitem); @@ -2485,23 +2641,7 @@ static frontend *new_window(char *arg, int argtype, char **error) submenu = gtk_menu_new(); gtk_menu_item_set_submenu(GTK_MENU_ITEM(menuitem), submenu); - for (i = 0; i < n; i++) { - char *name; - game_params *params; - - midend_fetch_preset(fe->me, i, &name, ¶ms); - - menuitem = - gtk_radio_menu_item_new_with_label(fe->preset_radio, name); - fe->preset_radio = - gtk_radio_menu_item_get_group(GTK_RADIO_MENU_ITEM(menuitem)); - fe->n_preset_menu_items++; - gtk_container_add(GTK_CONTAINER(submenu), menuitem); - g_object_set_data(G_OBJECT(menuitem), "user-data", params); - g_signal_connect(G_OBJECT(menuitem), "activate", - G_CALLBACK(menu_preset_event), fe); - gtk_widget_show(menuitem); - } + populate_gtk_preset_menu(fe, preset_menu, submenu); if (thegame.can_configure) { menuitem = fe->preset_custom = @@ -2532,8 +2672,8 @@ static frontend *new_window(char *arg, int argtype, char **error) gtk_widget_show(menuitem); #ifndef STYLUS_BASED add_menu_separator(GTK_CONTAINER(menu)); - add_menu_item_with_key(fe, GTK_CONTAINER(menu), "Undo", 'u'); - add_menu_item_with_key(fe, GTK_CONTAINER(menu), "Redo", 'r'); + add_menu_ui_item(fe, GTK_CONTAINER(menu), "Undo", UI_UNDO, 'u', 0); + add_menu_ui_item(fe, GTK_CONTAINER(menu), "Redo", UI_REDO, 'r', 0); #endif if (thegame.can_format_as_text_ever) { add_menu_separator(GTK_CONTAINER(menu)); @@ -2555,7 +2695,7 @@ static frontend *new_window(char *arg, int argtype, char **error) gtk_widget_show(menuitem); } add_menu_separator(GTK_CONTAINER(menu)); - add_menu_item_with_key(fe, GTK_CONTAINER(menu), "Exit", 'q'); + add_menu_ui_item(fe, GTK_CONTAINER(menu), "Exit", UI_QUIT, 'q', 0); menuitem = gtk_menu_item_new_with_mnemonic("_Help"); gtk_container_add(GTK_CONTAINER(fe->menubar), menuitem); @@ -2573,7 +2713,7 @@ static frontend *new_window(char *arg, int argtype, char **error) #ifdef STYLUS_BASED menuitem=gtk_button_new_with_mnemonic("_Redo"); g_object_set_data(G_OBJECT(menuitem), "user-data", - GINT_TO_POINTER((int)('r'))); + GINT_TO_POINTER(UI_REDO)); g_signal_connect(G_OBJECT(menuitem), "clicked", G_CALLBACK(menu_key_event), fe); gtk_box_pack_end(hbox, menuitem, FALSE, FALSE, 0); @@ -2581,7 +2721,7 @@ static frontend *new_window(char *arg, int argtype, char **error) menuitem=gtk_button_new_with_mnemonic("_Undo"); g_object_set_data(G_OBJECT(menuitem), "user-data", - GINT_TO_POINTER((int)('u'))); + GINT_TO_POINTER(UI_UNDO)); g_signal_connect(G_OBJECT(menuitem), "clicked", G_CALLBACK(menu_key_event), fe); gtk_box_pack_end(hbox, menuitem, FALSE, FALSE, 0); @@ -2640,15 +2780,23 @@ static frontend *new_window(char *arg, int argtype, char **error) #endif { GdkGeometry geom; - geom.base_width = geom.base_height = 0; + geom.base_width = 0; +#if GTK_CHECK_VERSION(3,0,0) + geom.base_height = window_extra_height(fe); + gtk_window_set_geometry_hints(GTK_WINDOW(fe->window), NULL, + &geom, GDK_HINT_BASE_SIZE); +#else + geom.base_height = 0; gtk_window_set_geometry_hints(GTK_WINDOW(fe->window), fe->area, &geom, GDK_HINT_BASE_SIZE); +#endif } fe->w = -1; fe->h = -1; get_size(fe, &x, &y); #if GTK_CHECK_VERSION(3,0,0) - gtk_window_set_default_geometry(GTK_WINDOW(fe->window), x, y); + gtk_window_set_default_size(GTK_WINDOW(fe->window), + x, y + window_extra_height(fe)); #else fe->drawing_area_shrink_pending = FALSE; gtk_drawing_area_size(GTK_DRAWING_AREA(fe->area), x, y); @@ -2690,6 +2838,10 @@ static frontend *new_window(char *arg, int argtype, char **error) G_CALLBACK(configure_area), fe); g_signal_connect(G_OBJECT(fe->window), "configure_event", G_CALLBACK(configure_window), fe); +#if GTK_CHECK_VERSION(3,0,0) + g_signal_connect(G_OBJECT(fe->window), "size_allocate", + G_CALLBACK(window_size_alloc), fe); +#endif gtk_widget_add_events(GTK_WIDGET(fe->area), GDK_BUTTON_PRESS_MASK | @@ -2744,6 +2896,22 @@ char *fgetline(FILE *fp) return ret; } +static void list_presets_from_menu(struct preset_menu *menu) +{ + int i; + + for (i = 0; i < menu->n_entries; i++) { + if (menu->entries[i].params) { + char *paramstr = thegame.encode_params( + menu->entries[i].params, TRUE); + printf("%s %s\n", paramstr, menu->entries[i].title); + sfree(paramstr); + } else { + list_presets_from_menu(menu->entries[i].submenu); + } + } +} + int main(int argc, char **argv) { char *pname = argv[0]; @@ -3147,23 +3315,12 @@ int main(int argc, char **argv) * Another specialist mode which causes the puzzle to list the * game_params strings for all its preset configurations. */ - int i, npresets; midend *me; + struct preset_menu *menu; me = midend_new(NULL, &thegame, NULL, NULL); - npresets = midend_num_presets(me); - - for (i = 0; i < npresets; i++) { - game_params *params; - char *name, *paramstr; - - midend_fetch_preset(me, i, &name, ¶ms); - paramstr = thegame.encode_params(params, TRUE); - - printf("%s %s\n", paramstr, name); - sfree(paramstr); - } - + menu = midend_get_presets(me, NULL); + list_presets_from_menu(menu); midend_free(me); return 0; } else {