Replicate r7285 from PuTTY: make keyboard input work in HTML Help.
[sgt/puzzles] / windows.c
index f4cbfe4..2f1a6f0 100644 (file)
--- a/windows.c
+++ b/windows.c
@@ -4,6 +4,9 @@
 
 #include <windows.h>
 #include <commctrl.h>
+#ifndef NO_HTMLHELP
+#include <htmlhelp.h>
+#endif /* NO_HTMLHELP */
 
 #include <stdio.h>
 #include <assert.h>
 
 #define HELP_FILE_NAME  "puzzles.hlp"
 #define HELP_CNT_NAME   "puzzles.cnt"
+#ifndef NO_HTMLHELP
+#define CHM_FILE_NAME   "puzzles.chm"
+#endif /* NO_HTMLHELP */
+
+#ifndef NO_HTMLHELP
+typedef HWND (CALLBACK *htmlhelp_t)(HWND, LPCSTR, UINT, DWORD);
+static htmlhelp_t htmlhelp;
+static HINSTANCE hh_dll;
+#endif /* NO_HTMLHELP */
+enum { NONE, HLP, CHM } help_type;
+char *help_path;
+const char *help_topic;
+int help_has_contents;
 
 #ifdef DEBUGGING
 static FILE *debug_fp = NULL;
@@ -119,8 +135,7 @@ struct frontend {
     HFONT cfgfont;
     HBRUSH oldbr;
     HPEN oldpen;
-    char *help_path;
-    int help_has_contents;
+    int help_running;
     enum { DRAWING, PRINTING, NOTHING } drawstatus;
     DOCINFO di;
     int printcount, printw, printh, printsolns, printcurr, printcolour;
@@ -981,32 +996,155 @@ void write_clip(HWND hwnd, char *data)
 }
 
 /*
- * See if we can find a help file.
+ * Set up Help and see if we can find a help file.
  */
-static void find_help_file(frontend *fe)
+static void init_help(void)
 {
     char b[2048], *p, *q, *r;
     FILE *fp;
-    if (!fe->help_path) {
-        GetModuleFileName(NULL, b, sizeof(b) - 1);
-        r = b;
-        p = strrchr(b, '\\');
-        if (p && p >= r) r = p+1;
-        q = strrchr(b, ':');
-        if (q && q >= r) r = q+1;
-        strcpy(r, HELP_FILE_NAME);
-        if ( (fp = fopen(b, "r")) != NULL) {
-            fe->help_path = dupstr(b);
-            fclose(fp);
-        } else
-            fe->help_path = NULL;
-        strcpy(r, HELP_CNT_NAME);
-        if ( (fp = fopen(b, "r")) != NULL) {
-            fe->help_has_contents = TRUE;
-            fclose(fp);
-        } else
-            fe->help_has_contents = FALSE;
+
+    /*
+     * Find the executable file path, so we can look alongside
+     * it for help files. Trim the filename off the end.
+     */
+    GetModuleFileName(NULL, b, sizeof(b) - 1);
+    r = b;
+    p = strrchr(b, '\\');
+    if (p && p >= r) r = p+1;
+    q = strrchr(b, ':');
+    if (q && q >= r) r = q+1;
+
+#ifndef NO_HTMLHELP
+    /*
+     * Try HTML Help first.
+     */
+    strcpy(r, CHM_FILE_NAME);
+    if ( (fp = fopen(b, "r")) != NULL) {
+       fclose(fp);
+
+       /*
+        * We have a .CHM. See if we can use it.
+        */
+       hh_dll = LoadLibrary("hhctrl.ocx");
+       if (hh_dll) {
+           htmlhelp = (htmlhelp_t)GetProcAddress(hh_dll, "HtmlHelpA");
+           if (!htmlhelp)
+               FreeLibrary(hh_dll);
+       }
+       if (htmlhelp) {
+           help_path = dupstr(b);
+           help_type = CHM;
+           help_topic = thegame.htmlhelp_topic;
+           return;
+       }
     }
+#endif /* NO_HTMLHELP */
+
+    /*
+     * Now try old-style .HLP.
+     */
+    strcpy(r, HELP_FILE_NAME);
+    if ( (fp = fopen(b, "r")) != NULL) {
+       fclose(fp);
+
+       help_path = dupstr(b);
+       help_type = HLP;
+
+       help_topic = thegame.winhelp_topic;
+
+       /*
+        * See if there's a .CNT file alongside it.
+        */
+       strcpy(r, HELP_CNT_NAME);
+       if ( (fp = fopen(b, "r")) != NULL) {
+           fclose(fp);
+           help_has_contents = TRUE;
+       } else
+           help_has_contents = FALSE;
+
+       return;
+    }
+
+    help_type = NONE;         /* didn't find any */
+}
+
+/*
+ * Start Help.
+ */
+static void start_help(frontend *fe, const char *topic)
+{
+    char *str = NULL;
+    int cmd;
+
+    switch (help_type) {
+      case HLP:
+       assert(help_path);
+       if (topic) {
+           str = snewn(10+strlen(topic), char);
+           sprintf(str, "JI(`',`%s')", topic);
+           cmd = HELP_COMMAND;
+       } else if (help_has_contents) {
+           cmd = HELP_FINDER;
+       } else {
+           cmd = HELP_CONTENTS;
+       }
+       WinHelp(fe->hwnd, help_path, cmd, (DWORD)str);
+       fe->help_running = TRUE;
+       break;
+      case CHM:
+#ifndef NO_HTMLHELP
+       assert(help_path);
+       assert(htmlhelp);
+       if (topic) {
+           str = snewn(20 + strlen(topic) + strlen(help_path), char);
+           sprintf(str, "%s::/%s.html>main", help_path, topic);
+       } else {
+           str = dupstr(help_path);
+       }
+       htmlhelp(fe->hwnd, str, HH_DISPLAY_TOPIC, 0);
+       fe->help_running = TRUE;
+       break;
+#endif /* NO_HTMLHELP */
+      case NONE:
+       assert(!"This shouldn't happen");
+       break;
+    }
+
+    sfree(str);
+}
+
+/*
+ * Stop Help on window cleanup.
+ */
+static void stop_help(frontend *fe)
+{
+    if (fe->help_running) {
+       switch (help_type) {
+         case HLP:
+           WinHelp(fe->hwnd, help_path, HELP_QUIT, 0);
+           break;
+         case CHM:
+#ifndef NO_HTMLHELP
+           assert(htmlhelp);
+           htmlhelp(NULL, NULL, HH_CLOSE_ALL, 0);
+           break;
+#endif /* NO_HTMLHELP */
+         case NONE:
+           assert(!"This shouldn't happen");
+           break;
+       }
+       fe->help_running = FALSE;
+    }
+}
+
+/*
+ * Terminate Help on process exit.
+ */
+static void cleanup_help(void)
+{
+    /* Nothing to do currently.
+     * (If we were running HTML Help single-threaded, this is where we'd
+     * call HH_UNINITIALIZE.) */
 }
 
 static void check_window_size(frontend *fe, int *px, int *py)
@@ -1096,14 +1234,13 @@ static frontend *new_window(HINSTANCE inst, char *game_id, char **error)
         }
     }
 
-    fe->help_path = NULL;
-    find_help_file(fe);
-
     fe->inst = inst;
 
     fe->timer = 0;
     fe->hwnd = NULL;
 
+    fe->help_running = FALSE;
+
     fe->drawstatus = NOTHING;
     fe->dr = NULL;
     fe->fontstart = 0;
@@ -1213,11 +1350,11 @@ static frontend *new_window(HINSTANCE inst, char *game_id, char **error)
        }
 
        AppendMenu(menu, MF_SEPARATOR, 0, 0);
-       AppendMenu(menu, MF_ENABLED, IDM_LOAD, "Load");
-       AppendMenu(menu, MF_ENABLED, IDM_SAVE, "Save");
+       AppendMenu(menu, MF_ENABLED, IDM_LOAD, "Load...");
+       AppendMenu(menu, MF_ENABLED, IDM_SAVE, "Save...");
        AppendMenu(menu, MF_SEPARATOR, 0, 0);
        if (thegame.can_print) {
-           AppendMenu(menu, MF_ENABLED, IDM_PRINT, "Print");
+           AppendMenu(menu, MF_ENABLED, IDM_PRINT, "Print...");
            AppendMenu(menu, MF_SEPARATOR, 0, 0);
        }
        AppendMenu(menu, MF_ENABLED, IDM_UNDO, "Undo");
@@ -1235,10 +1372,10 @@ static frontend *new_window(HINSTANCE inst, char *game_id, char **error)
        menu = CreateMenu();
        AppendMenu(bar, MF_ENABLED|MF_POPUP, (UINT)menu, "Help");
        AppendMenu(menu, MF_ENABLED, IDM_ABOUT, "About");
-        if (fe->help_path) {
+        if (help_type != NONE) {
            AppendMenu(menu, MF_SEPARATOR, 0, 0);
             AppendMenu(menu, MF_ENABLED, IDM_HELPC, "Contents");
-            if (thegame.winhelp_topic) {
+            if (help_topic) {
                 char *item;
                 assert(thegame.name);
                 item = snewn(9+strlen(thegame.name), char); /*ick*/
@@ -2091,19 +2228,10 @@ static LRESULT CALLBACK WndProc(HWND hwnd, UINT message,
 
            break;
           case IDM_HELPC:
-            assert(fe->help_path);
-            WinHelp(hwnd, fe->help_path,
-                    fe->help_has_contents ? HELP_FINDER : HELP_CONTENTS, 0);
-            break;
+           start_help(fe, NULL);
+           break;
           case IDM_GAMEHELP:
-            assert(fe->help_path);
-            assert(thegame.winhelp_topic);
-            {
-                char *cmd = snewn(10+strlen(thegame.winhelp_topic), char);
-                sprintf(cmd, "JI(`',`%s')", thegame.winhelp_topic);
-                WinHelp(hwnd, fe->help_path, HELP_COMMAND, (DWORD)cmd);
-                sfree(cmd);
-            }
+           start_help(fe, help_topic);
             break;
          default:
            {
@@ -2118,6 +2246,7 @@ static LRESULT CALLBACK WndProc(HWND hwnd, UINT message,
        }
        break;
       case WM_DESTROY:
+       stop_help(fe);
        PostQuitMessage(0);
        return 0;
       case WM_PAINT:
@@ -2321,7 +2450,9 @@ int WINAPI WinMain(HINSTANCE inst, HINSTANCE prev, LPSTR cmdline, int show)
        wndclass.cbClsExtra = 0;
        wndclass.cbWndExtra = 0;
        wndclass.hInstance = inst;
-       wndclass.hIcon = LoadIcon(inst, IDI_APPLICATION);
+       wndclass.hIcon = LoadIcon(inst, MAKEINTRESOURCE(200));
+       if (!wndclass.hIcon)           /* in case resource file is absent */
+           wndclass.hIcon = LoadIcon(inst, IDI_APPLICATION);
        wndclass.hCursor = LoadCursor(NULL, IDC_ARROW);
        wndclass.hbrBackground = NULL;
        wndclass.lpszMenuName = NULL;
@@ -2333,6 +2464,8 @@ int WINAPI WinMain(HINSTANCE inst, HINSTANCE prev, LPSTR cmdline, int show)
     while (*cmdline && isspace((unsigned char)*cmdline))
        cmdline++;
 
+    init_help();
+
     if (!new_window(inst, *cmdline ? cmdline : NULL, &error)) {
        char buf[128];
        sprintf(buf, "%.100s Error", thegame.name);
@@ -2344,5 +2477,7 @@ int WINAPI WinMain(HINSTANCE inst, HINSTANCE prev, LPSTR cmdline, int show)
        DispatchMessage(&msg);
     }
 
+    cleanup_help();
+
     return msg.wParam;
 }