mirror of
git://git.tartarus.org/simon/puzzles.git
synced 2025-04-21 08:01:30 -07:00
Rework the preset menu system to permit submenus.
To do this, I've completely replaced the API between mid-end and front end, so any downstream front end maintainers will have to do some rewriting of their own (sorry). I've done the necessary work in all five of the front ends I keep in-tree here - Windows, GTK, OS X, Javascript/Emscripten, and Java/NestedVM - and I've done it in various different styles (as each front end found most convenient), so that should provide a variety of sample code to show downstreams how, if they should need it. I've left in the old puzzle back-end API function to return a flat list of presets, so for the moment, all the puzzle backends are unchanged apart from an extra null pointer appearing in their top-level game structure. In a future commit I'll actually use the new feature in a puzzle; perhaps in the further future it might make sense to migrate all the puzzles to the new API and stop providing back ends with two alternative ways of doing things, but this seemed like enough upheaval for one day.
This commit is contained in:
126
gtk.c
126
gtk.c
@ -182,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;
|
||||
@ -1884,20 +1883,22 @@ 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), FALSE);
|
||||
}
|
||||
fe->preset_threaded = FALSE;
|
||||
|
||||
@ -2019,14 +2020,15 @@ 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);
|
||||
@ -2383,6 +2385,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)
|
||||
@ -2395,6 +2427,7 @@ 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)
|
||||
@ -2546,11 +2579,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);
|
||||
@ -2559,23 +2592,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 =
|
||||
@ -2826,6 +2843,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];
|
||||
@ -3229,23 +3262,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 {
|
||||
|
Reference in New Issue
Block a user