diff --git a/cmake/platforms/unix.cmake b/cmake/platforms/unix.cmake index 09eed6a..344ca99 100644 --- a/cmake/platforms/unix.cmake +++ b/cmake/platforms/unix.cmake @@ -12,6 +12,11 @@ in a crowded bin directory, e.g. \"sgt-\"") find_package(PkgConfig REQUIRED) +find_program(HALIBUT halibut) +if(NOT HALIBUT) + message(WARNING "HTML documentation cannot be built (did not find halibut)") +endif() + set(PUZZLES_GTK_FOUND FALSE) macro(try_gtk_package VER PACKAGENAME) if(NOT PUZZLES_GTK_FOUND AND @@ -52,6 +57,8 @@ if(STRICT AND (CMAKE_C_COMPILER_ID MATCHES "GNU" OR set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -Wall -Wwrite-strings -std=c99 -pedantic -Werror") endif() +add_compile_definitions(HELP_DIR="${CMAKE_INSTALL_PREFIX}/share/sgt-puzzles/help") + function(get_platform_puzzle_extra_source_files OUTVAR NAME) if(build_icons AND EXISTS ${CMAKE_SOURCE_DIR}/icons/${NAME}.sav) # If we have the equipment to rebuild the puzzles' icon images @@ -107,4 +114,20 @@ function(set_platform_puzzle_target_properties NAME TARGET) endfunction() function(build_platform_extras) + if(HALIBUT) + set(help_dir ${CMAKE_CURRENT_BINARY_DIR}/help) + add_custom_command(OUTPUT ${help_dir}/en + COMMAND ${CMAKE_COMMAND} -E make_directory ${help_dir}/en) + add_custom_command(OUTPUT ${help_dir}/en/index.html + COMMAND ${HALIBUT} --html ${CMAKE_CURRENT_SOURCE_DIR}/puzzles.but + DEPENDS + ${help_dir}/en + ${CMAKE_CURRENT_SOURCE_DIR}/puzzles.but + WORKING_DIRECTORY ${help_dir}/en) + add_custom_target(unix_help ALL + DEPENDS ${help_dir}/en/index.html) + + install(DIRECTORY ${help_dir} + DESTINATION share/sgt-puzzles) + endif() endfunction() diff --git a/gtk.c b/gtk.c index 52a1506..2373e38 100644 --- a/gtk.c +++ b/gtk.c @@ -14,6 +14,7 @@ #include #include #include +#include #include #include @@ -2989,6 +2990,78 @@ static void menu_config_event(GtkMenuItem *menuitem, gpointer data) midend_redraw(fe->me); } +#ifndef HELP_BROWSER_PATH +#define HELP_BROWSER_PATH "xdg-open:sensible-browser:$BROWSER" +#endif + +static bool try_show_help(const char *browser, const char *help_name) +{ + const char *argv[3] = { browser, help_name, NULL }; + + return g_spawn_async(NULL, (char **)argv, NULL, + G_SPAWN_SEARCH_PATH, + NULL, NULL, NULL, NULL); +} + +static void show_help(frontend *fe, const char *topic) +{ + char *path = dupstr(HELP_BROWSER_PATH); + char *path_entry; + char *help_name; + size_t help_name_size; + bool succeeded = true; + + help_name_size = strlen(HELP_DIR) + 4 + strlen(topic) + 6; + help_name = snewn(help_name_size, char); + sprintf(help_name, "%s/en/%s.html", + HELP_DIR, topic); + + if (access(help_name, R_OK)) { + error_box(fe->window, "Help file is not installed"); + sfree(path); + sfree(help_name); + return; + } + + path_entry = path; + for (;;) { + size_t len; + bool last; + + len = strcspn(path_entry, ":"); + last = path_entry[len] == 0; + path_entry[len] = 0; + + if (path_entry[0] == '$') { + const char *command = getenv(path_entry + 1); + + if (command) + succeeded = try_show_help(command, help_name); + } else { + succeeded = try_show_help(path_entry, help_name); + } + + if (last || succeeded) + break; + path_entry += len + 1; + } + + if (!succeeded) + error_box(fe->window, "Failed to start a help browser"); + sfree(path); + sfree(help_name); +} + +static void menu_help_contents_event(GtkMenuItem *menuitem, gpointer data) +{ + show_help((frontend *)data, "index"); +} + +static void menu_help_specific_event(GtkMenuItem *menuitem, gpointer data) +{ + show_help((frontend *)data, thegame.htmlhelp_topic); +} + static void menu_about_event(GtkMenuItem *menuitem, gpointer data) { frontend *fe = (frontend *)data; @@ -3410,6 +3483,25 @@ static frontend *new_window( menu = gtk_menu_new(); gtk_menu_item_set_submenu(GTK_MENU_ITEM(menuitem), menu); + menuitem = gtk_menu_item_new_with_label("Contents"); + gtk_container_add(GTK_CONTAINER(menu), menuitem); + g_signal_connect(G_OBJECT(menuitem), "activate", + G_CALLBACK(menu_help_contents_event), fe); + gtk_widget_show(menuitem); + + if (thegame.htmlhelp_topic) { + char *item; + assert(thegame.name); + item = snewn(9 + strlen(thegame.name), char); + sprintf(item, "Help on %s", thegame.name); + menuitem = gtk_menu_item_new_with_label(item); + sfree(item); + gtk_container_add(GTK_CONTAINER(menu), menuitem); + g_signal_connect(G_OBJECT(menuitem), "activate", + G_CALLBACK(menu_help_specific_event), fe); + gtk_widget_show(menuitem); + } + menuitem = gtk_menu_item_new_with_label("About"); gtk_container_add(GTK_CONTAINER(menu), menuitem); g_signal_connect(G_OBJECT(menuitem), "activate",