How Context Help works in the Program Code

All class methods related to context help start with the common pattern context _help in their names. It is easy to get all occurences in the code with the following command (example):

        grep -F context_help `find . -name '*.[Ch]' -print`

The base functionality is defined in several BC_WindowBase class methods in
guicast/bcwindowbase.C (search on context_help). All BC_Window's and BC_SubWindow's inherit these methods.

For the simplest case of context help definition, it is sufficient to add the call to context_help_set_keyword() in the most inclusive widget constructor. If Alt/h is pressed with the mouse over this widget's window, its keypress_event() method (inherited from BC_WindowBase) will catch this hotkey, fetch the keyphrase defined by context_help_set_keyword() and call the doc/ContextManual.pl script with this keyphrase. Then ContextManual.pl script does the whole processing of the keyphrase given and calls web browser to display the found HTML manual page. The browser is called in the background to prevent from blocking the calling CINELERRA-GG thread.

An example from cinelerra/zoombar.C:

        ZoomBar::ZoomBar(MWindow *mwindow, MWindowGUI *gui)
        : BC_SubWindow(...)
        {
                this->gui = gui;
                this->mwindow = mwindow;
		context_help_set_keyword("Zoom Panel");
	}

If Alt/h is pressed with the mouse over some subwindow (arbitrary depth) of the context_help_set_keyword() caller, the keypress_event() method of that subwindow catches the hotkey. Its own context help keyword, probably, will be empty. In this case the whole widget hierarchy is traced up to top_level widget, their context help keywords are checked, and the first nonempty keyword is used for context help.

This approach allows us to define the help keyword common to the whole dialog window with a bunch of diverse buttons with a single call to context_help_set _keyword(), without placing such a call into each button constructor. And at the same time, this approach allows to assign different help keywords to GUI elements belonging to the same window but documented in different manual pages.


An example with several different help keywords from cinelerra/mwindowgui.C:

        MWindowGUI::MWindowGUI(MWindow *mwindow)
        : BC_Window(_(PROGRAM_NAME ": Program"), ...)
        {
                this->mwindow = mwindow;
                ...
                context_help_set_keyword("Program Window");
        }
        ...
        FFMpegToggle::FFMpegToggle(MWindow *mwindow, MButtons *mbuttons, int x, int y)
        : BC_Toggle(...)
        {
                this->mwindow = mwindow;
                this->mbuttons = mbuttons;
                set_tooltip(get_value() ? FFMPEG_EARLY_TIP : FFMPEG_LATE_TIP);
                context_help_set_keyword("FFmpeg Early Probe Explanation");
        }
        ...
        StackButton::StackButton(MWindow *mwindow, int x, int y)
        : BC_GenericButton(x, y, mwindow->theme->stack_button_w, "0")
        {
                this->mwindow = mwindow;
                set_tooltip(_("Close EDL"));
                context_help_set_keyword("OpenEDL");
        }
        ...
        ProxyToggle::ProxyToggle(MWindow *mwindow, MButtons *mbuttons, int x, int y)
        : BC_Toggle(...)
        {
                this->mwindow = mwindow;
                this->mbuttons = mbuttons;
                ...
                context_help_set_keyword("Proxy");
        }
If the widget we wish to add context help to, overloads keypress_event() of the base class with its own method, then it is necessary to introduce a small addition to its keypress_event() handler: the call to context_help_check_and_show() has to be added in a suitable place in the handler.

An example with context_help_check_and_show() from cinelerra/mbuttons.C:
	int MButtons::keypress_event()
	{
	    int result = 0;
	    if(!result) {
	        result = transport->keypress_event();
	    }
	    if(!result) {
        	result = context_help_check_and_show();
            }
	    return result;
	}

All the keypress handlers are not equal, therefore we provide different variants of the methods context_help_check_and_show() and context_help_show() (the latter for explicit checking on hotkey).

An example with context_help_show() from cinelerra/editpanel.C:
        int EditPanelTcInt::keypress_event()
        {
                if( get_keypress() == 'h' && alt_down() ) {
                        context_help_show("Align Timecodes");
                        return 1;
                }
                ...further processing...
                return 1;
        }

A single example of much more sophisticated keypress_event() handler can be found in cinelerra/trackcanvas.C (search on context_help). The problem here was to track the particular object drawn on the canvas to figure out whose context help is to be requested. Another rare example of difficulty may appear when context help is desired for some GUI object which is not subclassed from BC_WindowBase.

ContextManual.pl looks for occurence of keyphrases in the following order:

  1. In Contents.html
  2. In Index.html
  3. In all the CinelerraGG_Manual/*.html files via grep

Keyphrase matching is tried first exact and case sensitive, then partially and case insensitive. The special keyword TOC shows Contents, IDX shows Index. If the keyword starts with FILE:, the filename after FILE: is extracted and that file is shown. You can look in the ContextManual.pl script text.

For debugging purposes the script can be called from command line. For this to work, the environment variable $CIN_DAT has to be set to the parent directory of doc.


Providing context help for plugins

In the simplest case, nothing has to be done at all. The plugin context help functionality introduced in cinelerra/pluginclient.C automatically assigns context help keyword from the plugin's title for all plugins subclassed from PluginClient. For this to work, the manual page documenting the plugin must be entitled identically to the programmatic title of the plugin. A special treatment can be necessary in the following cases:

  1. Rendered effects are not subclasses of PluginClient and require an explicit context help definition via context_help_set_keyword().
  2. Only the main plugin dialog inherits context help functionality from the parent class. If a plugin opens additional dialog windows, they probably require explicit context help definitions.
  3. If the plugin title differs from its subsection title in the documentation, either its help keyword or the corresponding title in the manual should be renamed. Another possibility can be an addition to the %rewrite table in doc/ContextManual.pl.
  4. If the plugin title contains some characters special for HTML and/or Perl regular expression syntax, the special characters have to be escaped. For example, the keyphrase corresponding to the title Crop & Position (X/Y) should be converted to:

                    Crop & Position \\(X\\/Y\\)
    

    Such a rewriting can be done either in the C++ code or in ContextManual.pl.

One additional explanation about previous user information when plugin tooltips are off. Help for the selected plugin is shown because in this case it would be difficult to figure out, without a visual feedback, which particular item is currently under the mouse.



Subsections
The CINELERRA-GG Community, 2021
https://www.cinelerra-gg.org