mirror of
git://git.tartarus.org/simon/puzzles.git
synced 2025-04-21 16:05:44 -07:00
Support user preferences on Windows.
This is done using basically the same methods as on Unix, and just translating the system calls in save_prefs to a different API.
This commit is contained in:
@ -179,10 +179,10 @@ solving it yourself after seeing the answer, you can just press Undo.
|
|||||||
|
|
||||||
\dt \i\e{Preferences}
|
\dt \i\e{Preferences}
|
||||||
|
|
||||||
\dd Where supported (currently only on Unix), brings up a dialog
|
\dd Where supported (currently only on Windows and Unix), brings up a
|
||||||
allowing you to configure personal preferences about a particular
|
dialog allowing you to configure personal preferences about a
|
||||||
game. Some of these preferences will be specific to a particular game;
|
particular game. Some of these preferences will be specific to a
|
||||||
others will be common to all games.
|
particular game; others will be common to all games.
|
||||||
|
|
||||||
\lcont{
|
\lcont{
|
||||||
|
|
||||||
|
152
windows.c
152
windows.c
@ -7,6 +7,7 @@
|
|||||||
#ifndef NO_HTMLHELP
|
#ifndef NO_HTMLHELP
|
||||||
#include <htmlhelp.h>
|
#include <htmlhelp.h>
|
||||||
#endif /* NO_HTMLHELP */
|
#endif /* NO_HTMLHELP */
|
||||||
|
#include <io.h>
|
||||||
|
|
||||||
#include <stdio.h>
|
#include <stdio.h>
|
||||||
#include <assert.h>
|
#include <assert.h>
|
||||||
@ -35,6 +36,7 @@
|
|||||||
#define IDM_LOAD 0x00F0
|
#define IDM_LOAD 0x00F0
|
||||||
#define IDM_PRINT 0x0100
|
#define IDM_PRINT 0x0100
|
||||||
#define IDM_PRESETS 0x0110
|
#define IDM_PRESETS 0x0110
|
||||||
|
#define IDM_PREFS 0x0120
|
||||||
#define IDM_GAMES 0x0300
|
#define IDM_GAMES 0x0300
|
||||||
|
|
||||||
#define IDM_KEYEMUL 0x0400
|
#define IDM_KEYEMUL 0x0400
|
||||||
@ -119,6 +121,8 @@ void debug_printf(const char *fmt, ...)
|
|||||||
(WS_MAXIMIZEBOX | WS_OVERLAPPED))
|
(WS_MAXIMIZEBOX | WS_OVERLAPPED))
|
||||||
|
|
||||||
static void new_game_size(frontend *fe, float scale);
|
static void new_game_size(frontend *fe, float scale);
|
||||||
|
static void load_prefs(midend *me);
|
||||||
|
static char *save_prefs(midend *me);
|
||||||
|
|
||||||
struct font {
|
struct font {
|
||||||
HFONT font;
|
HFONT font;
|
||||||
@ -935,6 +939,7 @@ void print(frontend *fe)
|
|||||||
game_params *params;
|
game_params *params;
|
||||||
|
|
||||||
nme = midend_new(NULL, fe->game, NULL, NULL);
|
nme = midend_new(NULL, fe->game, NULL, NULL);
|
||||||
|
load_prefs(nme);
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Set the non-interactive mid-end to have the same
|
* Set the non-interactive mid-end to have the same
|
||||||
@ -1442,6 +1447,7 @@ static midend *midend_for_new_game(frontend *fe, const game *cgame,
|
|||||||
if (!arg) {
|
if (!arg) {
|
||||||
if (me) midend_free(me);
|
if (me) midend_free(me);
|
||||||
me = midend_new(fe, cgame, &win_drawing, fe);
|
me = midend_new(fe, cgame, &win_drawing, fe);
|
||||||
|
load_prefs(me);
|
||||||
midend_new_game(me);
|
midend_new_game(me);
|
||||||
} else {
|
} else {
|
||||||
FILE *fp;
|
FILE *fp;
|
||||||
@ -1482,6 +1488,7 @@ static midend *midend_for_new_game(frontend *fe, const game *cgame,
|
|||||||
if (!err_load) {
|
if (!err_load) {
|
||||||
if (me) midend_free(me);
|
if (me) midend_free(me);
|
||||||
me = midend_new(fe, loadgame, &win_drawing, fe);
|
me = midend_new(fe, loadgame, &win_drawing, fe);
|
||||||
|
load_prefs(me);
|
||||||
err_load = midend_deserialise(me, savefile_read, fp);
|
err_load = midend_deserialise(me, savefile_read, fp);
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
@ -1494,6 +1501,7 @@ static midend *midend_for_new_game(frontend *fe, const game *cgame,
|
|||||||
*/
|
*/
|
||||||
if (me) midend_free(me);
|
if (me) midend_free(me);
|
||||||
me = midend_new(fe, cgame, &win_drawing, fe);
|
me = midend_new(fe, cgame, &win_drawing, fe);
|
||||||
|
load_prefs(me);
|
||||||
err_param = midend_game_id(me, arg);
|
err_param = midend_game_id(me, arg);
|
||||||
if (!err_param) {
|
if (!err_param) {
|
||||||
midend_new_game(me);
|
midend_new_game(me);
|
||||||
@ -1719,6 +1727,8 @@ static int fe_set_midend(frontend *fe, midend *me)
|
|||||||
AppendMenu(menu, MF_ENABLED, IDM_SOLVE, TEXT("Sol&ve"));
|
AppendMenu(menu, MF_ENABLED, IDM_SOLVE, TEXT("Sol&ve"));
|
||||||
}
|
}
|
||||||
AppendMenu(menu, MF_SEPARATOR, 0, 0);
|
AppendMenu(menu, MF_SEPARATOR, 0, 0);
|
||||||
|
AppendMenu(menu, MF_ENABLED, IDM_PREFS, TEXT("Pre&ferences"));
|
||||||
|
AppendMenu(menu, MF_SEPARATOR, 0, 0);
|
||||||
AppendMenu(menu, MF_ENABLED, IDM_QUIT, TEXT("E&xit"));
|
AppendMenu(menu, MF_ENABLED, IDM_QUIT, TEXT("E&xit"));
|
||||||
menu = CreateMenu();
|
menu = CreateMenu();
|
||||||
AppendMenu(bar, MF_ENABLED|MF_POPUP, (UINT_PTR)menu, TEXT("&Help"));
|
AppendMenu(bar, MF_ENABLED|MF_POPUP, (UINT_PTR)menu, TEXT("&Help"));
|
||||||
@ -2488,6 +2498,138 @@ static void update_type_menu_tick(frontend *fe)
|
|||||||
DrawMenuBar(fe->hwnd);
|
DrawMenuBar(fe->hwnd);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static char *prefs_dir(void)
|
||||||
|
{
|
||||||
|
const char *var;
|
||||||
|
if ((var = getenv("APPDATA")) != NULL) {
|
||||||
|
size_t size = strlen(var) + 80;
|
||||||
|
char *dir = snewn(size, char);
|
||||||
|
sprintf(dir, "%s\\Simon Tatham's Portable Puzzle Collection", var);
|
||||||
|
return dir;
|
||||||
|
}
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
static char *prefs_path_general(const game *game, const char *suffix)
|
||||||
|
{
|
||||||
|
char *dir, *path;
|
||||||
|
|
||||||
|
dir = prefs_dir();
|
||||||
|
if (!dir)
|
||||||
|
return NULL;
|
||||||
|
|
||||||
|
path = make_prefs_path(dir, "\\", game, suffix);
|
||||||
|
|
||||||
|
sfree(dir);
|
||||||
|
return path;
|
||||||
|
}
|
||||||
|
|
||||||
|
static char *prefs_path(const game *game)
|
||||||
|
{
|
||||||
|
return prefs_path_general(game, ".conf");
|
||||||
|
}
|
||||||
|
|
||||||
|
static char *prefs_tmp_path(const game *game)
|
||||||
|
{
|
||||||
|
return prefs_path_general(game, ".tmp");
|
||||||
|
}
|
||||||
|
|
||||||
|
static void load_prefs(midend *me)
|
||||||
|
{
|
||||||
|
const game *game = midend_which_game(me);
|
||||||
|
char *path = prefs_path(game);
|
||||||
|
if (!path)
|
||||||
|
return;
|
||||||
|
FILE *fp = fopen(path, "r");
|
||||||
|
if (!fp)
|
||||||
|
return;
|
||||||
|
const char *err = midend_load_prefs(me, savefile_read, fp);
|
||||||
|
fclose(fp);
|
||||||
|
if (err)
|
||||||
|
fprintf(stderr, "Unable to load preferences file %s:\n%s\n",
|
||||||
|
path, err);
|
||||||
|
sfree(path);
|
||||||
|
}
|
||||||
|
|
||||||
|
static char *save_prefs(midend *me)
|
||||||
|
{
|
||||||
|
const game *game = midend_which_game(me);
|
||||||
|
char *dir_path = prefs_dir();
|
||||||
|
char *file_path = prefs_path(game);
|
||||||
|
char *tmp_path = prefs_tmp_path(game);
|
||||||
|
HANDLE fh;
|
||||||
|
FILE *fp;
|
||||||
|
bool cleanup_dir = false, cleanup_tmpfile = false;
|
||||||
|
char *err = NULL;
|
||||||
|
|
||||||
|
if (!dir_path || !file_path || !tmp_path) {
|
||||||
|
sprintf(err = snewn(256, char),
|
||||||
|
"Unable to save preferences:\n"
|
||||||
|
"Could not determine pathname for configuration files");
|
||||||
|
goto out;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!CreateDirectory(dir_path, NULL)) {
|
||||||
|
/* Ignore errors while trying to make the directory. It may
|
||||||
|
* well already exist, and even if we got some error code
|
||||||
|
* other than EEXIST, it's still worth at least _trying_ to
|
||||||
|
* make the file inside it, and see if that goes wrong. */
|
||||||
|
} else {
|
||||||
|
cleanup_dir = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
fh = CreateFile(tmp_path, GENERIC_WRITE, 0, NULL, CREATE_ALWAYS,
|
||||||
|
FILE_ATTRIBUTE_NORMAL, NULL);
|
||||||
|
if (fh == INVALID_HANDLE_VALUE) {
|
||||||
|
char *os_err = geterrstr();
|
||||||
|
sprintf(err = snewn(256 + strlen(tmp_path) + strlen(os_err), char),
|
||||||
|
"Unable to save preferences:\n"
|
||||||
|
"Unable to create file '%s':\n%s", tmp_path, os_err);
|
||||||
|
sfree(os_err);
|
||||||
|
goto out;
|
||||||
|
} else {
|
||||||
|
cleanup_tmpfile = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
fp = _fdopen(_open_osfhandle((intptr_t)fh, 0), "w");
|
||||||
|
SetLastError(0);
|
||||||
|
midend_save_prefs(me, savefile_write, fp);
|
||||||
|
fclose(fp);
|
||||||
|
if (GetLastError()) {
|
||||||
|
char *os_err = geterrstr();
|
||||||
|
sprintf(err = snewn(80 + strlen(tmp_path) + strlen(os_err), char),
|
||||||
|
"Unable to write file '%s':\n%s", tmp_path, os_err);
|
||||||
|
sfree(os_err);
|
||||||
|
goto out;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (MoveFileEx(tmp_path, file_path, MOVEFILE_REPLACE_EXISTING) < 0) {
|
||||||
|
char *os_err = geterrstr();
|
||||||
|
sprintf(err = snewn(256 + strlen(tmp_path) + strlen(file_path) +
|
||||||
|
strlen(os_err), char),
|
||||||
|
"Unable to save preferences:\n"
|
||||||
|
"Unable to rename '%s' to '%s':\n%s", tmp_path, file_path,
|
||||||
|
os_err);
|
||||||
|
sfree(os_err);
|
||||||
|
goto out;
|
||||||
|
} else {
|
||||||
|
cleanup_dir = false;
|
||||||
|
cleanup_tmpfile = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
out:
|
||||||
|
if (cleanup_tmpfile) {
|
||||||
|
if (!DeleteFile(tmp_path)) { /* can't do anything about this */ }
|
||||||
|
}
|
||||||
|
if (cleanup_dir) {
|
||||||
|
if (!RemoveDirectory(dir_path)) { /* can't do anything about this */ }
|
||||||
|
}
|
||||||
|
sfree(dir_path);
|
||||||
|
sfree(file_path);
|
||||||
|
sfree(tmp_path);
|
||||||
|
return err;
|
||||||
|
}
|
||||||
|
|
||||||
static void update_copy_menu_greying(frontend *fe)
|
static void update_copy_menu_greying(frontend *fe)
|
||||||
{
|
{
|
||||||
UINT enable = (midend_can_format_as_text_now(fe->me) ?
|
UINT enable = (midend_can_format_as_text_now(fe->me) ?
|
||||||
@ -2582,6 +2724,16 @@ static LRESULT CALLBACK WndProc(HWND hwnd, UINT message,
|
|||||||
if (get_config(fe, CFG_PRINT))
|
if (get_config(fe, CFG_PRINT))
|
||||||
print(fe);
|
print(fe);
|
||||||
break;
|
break;
|
||||||
|
case IDM_PREFS:
|
||||||
|
if (get_config(fe, CFG_PREFS)) {
|
||||||
|
char *prefs_err = save_prefs(fe->me);
|
||||||
|
if (prefs_err) {
|
||||||
|
MessageBox(fe->hwnd, prefs_err, "Error saving preferences",
|
||||||
|
MB_ICONERROR | MB_OK);
|
||||||
|
sfree(prefs_err);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
break;
|
||||||
case IDM_ABOUT:
|
case IDM_ABOUT:
|
||||||
about(fe);
|
about(fe);
|
||||||
break;
|
break;
|
||||||
|
Reference in New Issue
Block a user