Search code examples
c++gtkgnomegtkmmmallard

Show help for my Gtkmm application without Autotools or Meson


I have written a small application using Gtkmm for which I would like to provide documentation for users. For this, I would like to launch the Gnome help system from my application's menu (For a working example, one can look at gedit).

First, I wrote a minimal Mallard index.page file:

<page xmlns="http://projectmallard.org/1.0/"
      type="guide"
      id="index">
<title>Example help</title>
</page>

which I can view by calling the command yelp help/C/index.page from my project's root directory:

Basic help from yelp

Then, I wrote some minimal C++ application (Gtkmm 3.22) to call the help system:

#include <iostream>
#include <gtkmm.h>

constexpr char HELP_URI[] = "help:myapp";

int main(int argc, char* argv[]) 
{
    auto app = Gtk::Application::create(argc, argv, "bob.morane.question");

    Gtk::Window window;

    // 1. Add the help menu item:
    Gtk::MenuItem helpItem;
    helpItem.set_label("Help");

    Gtk::Menu helpMenu;
    Gtk::MenuItem openHelpItem;
    openHelpItem.set_label("Open help...");

    Gtk::MenuBar menuBar;
    menuBar.append(helpItem);
    helpItem.set_submenu(helpMenu);
    helpMenu.append(openHelpItem);

    window.add(menuBar);
    

    // 2. Link the help menu item to Mallard documentation:
    openHelpItem.signal_activate().connect([&window](){
        const guint32 timestamp = gtk_get_current_event_time();
        GError* error = nullptr;

        // This is the call that triggers gnome help system:
        const bool status = gtk_show_uri_on_window (window.gobj(),
                                                    HELP_URI,
                                                    timestamp,
                                                    &error);
        if(!status)
        {
            std::cout << "Unable to show help : " + std::string{error->message} << std::endl;
            return;
        }
    });

    window.show_all();
    return app->run(window);
}

which I build using a very simple Makefile:

all: main.cpp
    g++ -std=c++17 main.cpp -o myapp `pkg-config gtkmm-3.0 --cflags --libs`

When I click on the Open help... menu item, I get the following:

Not found

If I change my program's first line to: constexpr char HELP_URI[] = "help:gedit";, then the gedit help pops out fine. Using a build system such as Meson (like gedit) or Autotools (like this Gnome documentation suggests) would be overkill for me right now. I would like to be able to show my help file without the need to handle those (i.e. I want to keep using my Makefile).

*Question: How could I show my own help file without needing to use Meson or Autotools?


Solution

  • The help files have to be somewhere Yelp can find them. Usually you would install them to /usr/share/help/LANG/APPID/, where LANG is a language identifier (or just C for the untranslated docs), and APPID is some identifier for your app (matching the basename of your .desktop file is a good best practice).

    If you use autotools, yelp.m4 takes care of all of this for you: http://yelp.io/tools/yelp.m4

    If you use meson, the gnome.yelp function takes care of this for you: https://mesonbuild.com/Gnome-module.html#gnomeyelp

    But if you don't want to use a build system, you'll have to manage getting the files somewhere yourself. They don't have to be in /usr/. Yelp looks in the help subdirectory of the directories in $XDG_DATA_HOME and $XDG_DATA_DIRS from the XDG base directory specification. By default, this means it looks at ~/.local/share/help/:/usr/local/share/help/:/usr/share/help/. So you can install the files locally under ~/.local/share/ for testing purposes.

    Or, if you really don't want to install files anywhere, you can arrange your source tree in a specific way and set an environment variable. For example, if your source tree is rooted at ~/myapp/, then put your page files under ~/myapp/help/C/myapp/, and call your app with XDG_DATA_HOME=~/myapp/ myapp.

    But really, if you want users to use your app, you're going to have to figure out how to install files in the right places. And you're better off using an existing build system for that, even if you think your app is too small.