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:
Simon Tatham
2017-04-24 16:00:24 +01:00
parent bc2c1f69fd
commit a7dc17c425
56 changed files with 813 additions and 345 deletions

122
windows.c
View File

@ -195,6 +195,11 @@ struct blitter {
enum { CFG_PRINT = CFG_FRONTEND_SPECIFIC };
struct preset_menuitemref {
HMENU which_menu;
int item_index;
};
struct frontend {
const game *game;
midend *me;
@ -213,8 +218,9 @@ struct frontend {
HMENU gamemenu, typemenu;
UINT timer;
DWORD timer_last_tickcount;
int npresets;
game_params **presets;
struct preset_menu *preset_menu;
struct preset_menuitemref *preset_menuitems;
int n_preset_menuitems;
struct font *fonts;
int nfonts, fontsize;
config_item *cfg;
@ -244,7 +250,6 @@ void frontend_free(frontend *fe)
sfree(fe->colours);
sfree(fe->brushes);
sfree(fe->pens);
sfree(fe->presets);
sfree(fe->fonts);
sfree(fe);
@ -1530,12 +1535,12 @@ static frontend *frontend_new(HINSTANCE inst)
NULL, NULL, inst, NULL);
if (!fe->hwnd) {
DWORD lerr = GetLastError();
printf("no window: 0x%x\n", lerr);
printf("no window: 0x%x\n", (unsigned)lerr);
}
#endif
fe->gamemenu = NULL;
fe->presets = NULL;
fe->preset_menu = NULL;
fe->statusbar = NULL;
fe->bitmap = NULL;
@ -1658,6 +1663,46 @@ static midend *midend_for_new_game(frontend *fe, const game *cgame,
return me;
}
static void populate_preset_menu(frontend *fe,
struct preset_menu *menu, HMENU winmenu)
{
int i;
for (i = 0; i < menu->n_entries; i++) {
struct preset_menu_entry *entry = &menu->entries[i];
UINT_PTR id_or_sub;
UINT flags = MF_ENABLED;
if (entry->params) {
id_or_sub = (UINT_PTR)(IDM_PRESETS + 0x10 * entry->id);
fe->preset_menuitems[entry->id].which_menu = winmenu;
fe->preset_menuitems[entry->id].item_index =
GetMenuItemCount(winmenu);
} else {
HMENU winsubmenu = CreateMenu();
id_or_sub = (UINT_PTR)winsubmenu;
flags |= MF_POPUP;
populate_preset_menu(fe, entry->submenu, winsubmenu);
}
/*
* FIXME: we ought to go through and do something with ampersands
* here.
*/
#ifndef _WIN32_WCE
AppendMenu(winmenu, flags, id_or_sub, entry->title);
#else
{
TCHAR wName[255];
MultiByteToWideChar(CP_ACP, 0, entry->title, -1, wName, 255);
AppendMenu(winmenu, flags, id_or_sub, wName);
}
#endif
}
}
/*
* Populate a frontend structure with a new midend structure, and
* create any window furniture that it needs.
@ -1799,11 +1844,16 @@ static int fe_set_midend(frontend *fe, midend *me)
AppendMenu(menu, MF_ENABLED, IDM_SEED, TEXT("Rando&m Seed..."));
#endif
if (fe->presets)
sfree(fe->presets);
if ((fe->npresets = midend_num_presets(fe->me)) > 0 ||
fe->game->can_configure) {
int i;
if (!fe->preset_menu) {
int i;
fe->preset_menu = midend_get_presets(
fe->me, &fe->n_preset_menuitems);
fe->preset_menuitems = snewn(fe->n_preset_menuitems,
struct preset_menuitemref);
for (i = 0; i < fe->n_preset_menuitems; i++)
fe->preset_menuitems[i].which_menu = NULL;
}
if (fe->preset_menu->n_entries > 0 || fe->game->can_configure) {
#ifndef _WIN32_WCE
HMENU sub = CreateMenu();
@ -1812,28 +1862,9 @@ static int fe_set_midend(frontend *fe, midend *me)
HMENU sub = SHGetSubMenu(SHFindMenuBar(fe->hwnd), ID_TYPE);
DeleteMenu(sub, 0, MF_BYPOSITION);
#endif
fe->presets = snewn(fe->npresets, game_params *);
for (i = 0; i < fe->npresets; i++) {
char *name;
#ifdef _WIN32_WCE
TCHAR wName[255];
#endif
populate_preset_menu(fe, fe->preset_menu, sub);
midend_fetch_preset(fe->me, i, &name, &fe->presets[i]);
/*
* FIXME: we ought to go through and do something
* with ampersands here.
*/
#ifndef _WIN32_WCE
AppendMenu(sub, MF_ENABLED, IDM_PRESETS + 0x10 * i, name);
#else
MultiByteToWideChar (CP_ACP, 0, name, -1, wName, 255);
AppendMenu(sub, MF_ENABLED, IDM_PRESETS + 0x10 * i, wName);
#endif
}
if (fe->game->can_configure) {
AppendMenu(sub, MF_ENABLED, IDM_CONFIG, TEXT("&Custom..."));
}
@ -1841,7 +1872,6 @@ static int fe_set_midend(frontend *fe, midend *me)
fe->typemenu = sub;
} else {
fe->typemenu = INVALID_HANDLE_VALUE;
fe->presets = NULL;
}
#ifdef COMBINED
@ -2893,14 +2923,22 @@ static void update_type_menu_tick(frontend *fe)
if (fe->typemenu == INVALID_HANDLE_VALUE)
return;
total = GetMenuItemCount(fe->typemenu);
n = midend_which_preset(fe->me);
if (n < 0)
n = total - 1; /* "Custom" item */
for (i = 0; i < total; i++) {
int flag = (i == n ? MF_CHECKED : MF_UNCHECKED);
CheckMenuItem(fe->typemenu, i, MF_BYPOSITION | flag);
for (i = 0; i < fe->n_preset_menuitems; i++) {
if (fe->preset_menuitems[i].which_menu) {
int flag = (i == n ? MF_CHECKED : MF_UNCHECKED);
CheckMenuItem(fe->preset_menuitems[i].which_menu,
fe->preset_menuitems[i].item_index,
MF_BYPOSITION | flag);
}
}
if (fe->game->can_configure) {
int flag = (n < 0 ? MF_CHECKED : MF_UNCHECKED);
/* "Custom" menu item is at the bottom of the top-level Type menu */
total = GetMenuItemCount(fe->typemenu);
CheckMenuItem(fe->typemenu, total - 1, MF_BYPOSITION | flag);
}
DrawMenuBar(fe->hwnd);
@ -3146,10 +3184,12 @@ static LRESULT CALLBACK WndProc(HWND hwnd, UINT message,
} else
#endif
{
int p = ((wParam &~ 0xF) - IDM_PRESETS) / 0x10;
game_params *preset = preset_menu_lookup_by_id(
fe->preset_menu,
((wParam &~ 0xF) - IDM_PRESETS) / 0x10);
if (p >= 0 && p < fe->npresets) {
midend_set_params(fe->me, fe->presets[p]);
if (preset) {
midend_set_params(fe->me, preset);
new_game_type(fe);
}
}
@ -3653,7 +3693,7 @@ void split_into_argv(char *cmdline, int *argc, char ***argv,
int WINAPI WinMain(HINSTANCE inst, HINSTANCE prev, LPSTR cmdline, int show)
{
MSG msg;
char *error;
char *error = NULL;
const game *gg;
frontend *fe;
midend *me;