[PATCH 1/2] Add a help button to every Anaconda screen

Martin Kolman mkolman at redhat.com
Thu Sep 25 17:37:13 UTC 2014


This makes context aware help possible for every Anaconda screen by
providing a help button in the upper right corner of the screen.
Clicking the button opens the yelp help viewer with either a help page
tailored for the given screen, the main page of the installation guide
(if no help is available for the screen) or a "no help found" placeholder
page.
The new helpFile GUIObject attribute is used to specify which help
file to show for the given screen. If no help file is specified
either the main page of the Installation Guide will be shown (if
available) or the placeholder page will be shown.

Signed-off-by: Martin Kolman <mkolman at redhat.com>
---
 anaconda.spec.in                                |   1 +
 data/help/Makefile.am                           |  21 ++++
 data/help/en-US/FedoraPlaceholder.html          |   5 +
 data/help/en-US/FedoraPlaceholderWithLinks.html |  13 +++
 data/help/en-US/Makefile.am                     |  23 ++++
 data/help/en-US/RHEL7Placeholder.html           |   5 +
 data/help/en-US/RHEL7PlaceholderWithLinks.html  |  11 ++
 pyanaconda/ihelp.py                             | 143 ++++++++++++++++++++++++
 pyanaconda/installclass.py                      |   6 +
 pyanaconda/installclasses/fedora.py             |   3 +
 pyanaconda/installclasses/rhel.py               |   3 +
 pyanaconda/ui/gui/__init__.py                   |  12 ++
 pyanaconda/ui/gui/hubs/__init__.py              |   4 +-
 pyanaconda/ui/gui/spokes/__init__.py            |  13 ++-
 scripts/makeupdates                             |   3 +
 widgets/configure.ac                            |   4 +-
 widgets/glade/AnacondaWidgets.xml               |   7 +-
 widgets/src/BaseWindow.c                        |  48 ++++++++
 widgets/src/BaseWindow.h                        |   3 +
 19 files changed, 323 insertions(+), 5 deletions(-)
 create mode 100644 data/help/Makefile.am
 create mode 100644 data/help/en-US/FedoraPlaceholder.html
 create mode 100644 data/help/en-US/FedoraPlaceholderWithLinks.html
 create mode 100644 data/help/en-US/Makefile.am
 create mode 100644 data/help/en-US/RHEL7Placeholder.html
 create mode 100644 data/help/en-US/RHEL7PlaceholderWithLinks.html
 create mode 100644 pyanaconda/ihelp.py

diff --git a/anaconda.spec.in b/anaconda.spec.in
index 0bdd16f..d36893d 100644
--- a/anaconda.spec.in
+++ b/anaconda.spec.in
@@ -167,6 +167,7 @@ Requires: nm-connection-editor
 %ifarch %livearches
 Requires: zenity
 %endif
+Requires: yelp
 
 %description gui
 This package contains graphical user interface for the Anaconda installer.
diff --git a/data/help/Makefile.am b/data/help/Makefile.am
new file mode 100644
index 0000000..8be7029
--- /dev/null
+++ b/data/help/Makefile.am
@@ -0,0 +1,21 @@
+# data/Makefile.am for anaconda
+#
+# Copyright (C) 2014  Red Hat, Inc.
+#
+# This program is free software; you can redistribute it and/or modify
+# it under the terms of the GNU Lesser General Public License as published
+# by the Free Software Foundation; either version 2.1 of the License, or
+# (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+# GNU Lesser General Public License for more details.
+#
+# You should have received a copy of the GNU Lesser General Public License
+# along with this program.  If not, see <http://www.gnu.org/licenses/>.
+#
+# Author: Martin Kolman <mkolman at redhat.com>
+
+MAINTAINERCLEANFILES = Makefile.in
+SUBDIRS = en-US
diff --git a/data/help/en-US/FedoraPlaceholder.html b/data/help/en-US/FedoraPlaceholder.html
new file mode 100644
index 0000000..4d1781a
--- /dev/null
+++ b/data/help/en-US/FedoraPlaceholder.html
@@ -0,0 +1,5 @@
+<body>
+<h1>The Anaconda built-in help</h1>
+<p>...is not yet available for this screen.</p>
+<p>You can check the Anaconda wiki page, the Fedora Installation Guide or other online help resources instead.</p>
+</body>
diff --git a/data/help/en-US/FedoraPlaceholderWithLinks.html b/data/help/en-US/FedoraPlaceholderWithLinks.html
new file mode 100644
index 0000000..5fe0485
--- /dev/null
+++ b/data/help/en-US/FedoraPlaceholderWithLinks.html
@@ -0,0 +1,13 @@
+<body>
+<h1>The Anaconda built-in help</h1>
+<p>...is not yet available for this screen.</p>
+<p>You can check the Anaconda wiki page, the Fedora Installation Guide or other online help resources instead:</p>
+<p>
+<ul>
+    <li><a href="http://docs.fedoraproject.org/en-US/Fedora/20/html/Installation_Guide/">Fedora Installation Guide</a></li>
+    <li><a href="https://fedoraproject.org/wiki/Anaconda">Anaconda Wiki</a></li>
+    <li><a href="http://fedoraproject.org/wiki/Anaconda_Boot_Options">Anaconda Boot Options</a></li>
+    <li><a href="http://fedoraproject.org/wiki/Anaconda/Kickstart">Anaconda Kickstart</a></li>
+</ul>
+</p>
+</body>
diff --git a/data/help/en-US/Makefile.am b/data/help/en-US/Makefile.am
new file mode 100644
index 0000000..0cd10bc
--- /dev/null
+++ b/data/help/en-US/Makefile.am
@@ -0,0 +1,23 @@
+# data/Makefile.am for anaconda
+#
+# Copyright (C) 2014  Red Hat, Inc.
+#
+# This program is free software; you can redistribute it and/or modify
+# it under the terms of the GNU Lesser General Public License as published
+# by the Free Software Foundation; either version 2.1 of the License, or
+# (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+# GNU Lesser General Public License for more details.
+#
+# You should have received a copy of the GNU Lesser General Public License
+# along with this program.  If not, see <http://www.gnu.org/licenses/>.
+#
+# Author: Martin Kolman <mkolman at redhat.com>
+
+enplacholderdir               = $(docdir)/en-US
+dist_enplaceholder_DATA       = FedoraPlaceholder.html FedoraPlaceholderWithLinks.html RHEL7Placeholder.html RHEL7PlaceholderWithLinks.html
+
+MAINTAINERCLEANFILES = Makefile.in
diff --git a/data/help/en-US/RHEL7Placeholder.html b/data/help/en-US/RHEL7Placeholder.html
new file mode 100644
index 0000000..9f3c446
--- /dev/null
+++ b/data/help/en-US/RHEL7Placeholder.html
@@ -0,0 +1,5 @@
+<body>
+<h1>The Anaconda built-in help</h1>
+<p>...is not yet available for this screen.</p>
+<p>You can check out the Red Hat Enterprise Linux 7 Installation Guide or other online help resources instead.</p>
+</body>
diff --git a/data/help/en-US/RHEL7PlaceholderWithLinks.html b/data/help/en-US/RHEL7PlaceholderWithLinks.html
new file mode 100644
index 0000000..c2da61b
--- /dev/null
+++ b/data/help/en-US/RHEL7PlaceholderWithLinks.html
@@ -0,0 +1,11 @@
+<body>
+<h1>The Anaconda built-in help</h1>
+<p>...is not yet available for this screen.</p>
+<p>You can check the Red Hat Enterprise Linux 7 Installation Guide or Red Hat Customer Portal resources instead:</p>
+<p>
+<ul>
+    <li><a href="https://access.redhat.com/documentation/en-US/Red_Hat_Enterprise_Linux/7/html/Installation_Guide/index.html">Red Hat Enterprise Linux 7 Installation Guide</a></li>
+    <li><a href="https://access.redhat.com">Red Hat Customer Portal</a></li>
+</ul>
+</p>
+</body>
diff --git a/pyanaconda/ihelp.py b/pyanaconda/ihelp.py
new file mode 100644
index 0000000..3e7446a
--- /dev/null
+++ b/pyanaconda/ihelp.py
@@ -0,0 +1,143 @@
+#
+# Copyright (C) 2014  Red Hat, Inc.
+#
+# This copyrighted material is made available to anyone wishing to use,
+# modify, copy, or redistribute it subject to the terms and conditions of
+# the GNU General Public License v.2, or (at your option) any later version.
+# This program is distributed in the hope that it will be useful, but WITHOUT
+# ANY WARRANTY expressed or implied, including the implied warranties of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General
+# Public License for more details.  You should have received a copy of the
+# GNU General Public License along with this program; if not, write to the
+# Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+# 02110-1301, USA.  Any Red Hat trademarks that are incorporated in the
+# source code or documentation are not subject to the GNU General Public
+# License and may only be used or replicated with the express permission of
+# Red Hat, Inc.
+#
+# Red Hat Author(s): Martin Kolman <mkolman at redhat.com>
+#
+"""
+Anaconda built-in help module
+"""
+import os
+import subprocess
+
+from pyanaconda.flags import flags
+from pyanaconda.localization import find_best_locale_match
+from pyanaconda.constants import DEFAULT_LANG
+
+import logging
+log = logging.getLogger("anaconda")
+
+yelp_process = None
+
+def _get_best_help_file(help_folder, help_file):
+    """Return the path to the best help file for the current language and available
+    help content
+
+    :param str help_folder: a path to folder where we should look for the help files
+    :param str help_file: name of the requested help file
+    :return: path to the best help file or None is no match is found
+    :rtype: str or NoneType
+
+    """
+    current_lang = os.environ["LANG"]
+    # list all available languages for the Anaconda help
+    # * content is stored in folders named after the given language code
+    #   (en-US, cs-CZ, jp-JP, etc.)
+    # * we check if the given folder contains the currently needed help file
+    #   and only consider it fit to use if it does have the file
+    if not os.path.exists(help_folder):
+        log.warning("help folder %s for help file %s does not exist", help_folder, help_file)
+        return None
+
+    help_langs = [l for l in os.listdir(help_folder) if os.path.isfile(os.path.join(help_folder, l, help_file))]
+
+    best_lang = find_best_locale_match(current_lang, help_langs)
+    if not best_lang and current_lang != DEFAULT_LANG:
+        # nothing found for current language, fallback to the default language,
+        # if available & different from current language
+        log.warning("help file %s not found in lang %s, falling back to default lang (%s)",
+                    help_file, current_lang, DEFAULT_LANG)
+        best_lang = find_best_locale_match(DEFAULT_LANG, help_langs)
+
+    # did we get something usable ?
+    if best_lang:
+        # we already checked that the full path exists when enumerating suitable
+        # help content above, so we can just return the part here without
+        # checking it again
+        return os.path.join(help_folder, best_lang, help_file)
+    else:
+        log.warning("no help content found for file %s", help_file)
+        return None
+
+def get_help_path(help_file, instclass):
+    """Return the full path for the given help file name,
+    if the help file path does not exist a fallback path is returned.
+    There are actually two possible fallback paths that might be returned:
+    * first we try to return path to the main page of the installation guide
+      (if it exists)
+    * if we can't find the main page of the installation page, path to a
+      "no help found" placeholder bundled with Anaconda is returned
+
+    Regarding help l10n, we try to respect the current locale as defined by the
+    "LANG" environmental variable, but fallback to English if localized content
+    is not available.
+
+    :param help_file: help file name
+    :type help_file: str or NoneType
+    :param instclass: current install class instance
+    :return str: full path to the help file requested or to a placeholder
+    """
+    # help l10n handling
+
+    if help_file:
+        help_path = _get_best_help_file(instclass.help_folder, help_file)
+        if help_path is not None:
+            return help_path
+
+    # the screen did not have a helpFile defined or the defined help file
+    # does not exist, so next try to check if we can find the main page
+    # of the installation guide and use it instead
+    help_path = _get_best_help_file(instclass.help_folder, instclass.help_main_page)
+    if help_path is not None:
+        return help_path
+
+    # looks like the installation guide is not available, so just return
+    # a placeholder page, which should be always present
+    if flags.livecdInstall:
+        return _get_best_help_file(instclass.help_folder, instclass.help_placeholder_with_links)
+    else:
+        return _get_best_help_file(instclass.help_folder, instclass.help_placeholder)
+
+def start_yelp(help_path):
+    """Start a new yelp process and make sure to kill any existing ones
+
+    :param help_path: path to the help file yelp should load
+    :type help_path: str or NoneType
+    """
+
+    kill_yelp()
+    log.debug("starting yelp")
+    global yelp_process
+    # under some extreme circumstances (placeholders missing)
+    # the help path can be None and we need to prevent Popen
+    # receiving None as an argument instead of a string
+    try:
+        yelp_process = subprocess.Popen(["yelp", help_path or ""])
+    except OSError:
+        log.exception("starting the yelp help viewer failed")
+
+def kill_yelp():
+    """Try to kill any existing yelp processes"""
+
+    global yelp_process
+    if not yelp_process:
+        return False
+
+    log.debug("killing yelp")
+    yelp_process.kill()
+    yelp_process = None
+    return True
+
diff --git a/pyanaconda/installclass.py b/pyanaconda/installclass.py
index 3b8811b..b89b215 100644
--- a/pyanaconda/installclass.py
+++ b/pyanaconda/installclass.py
@@ -63,6 +63,12 @@ class BaseInstallClass(object):
     # don't select this class by default
     default = 0
 
+    # help
+    help_folder = "/usr/share/doc/anaconda"
+    help_main_page = "Installation_Guide.xml"
+    help_placeholder = None
+    help_placeholder_with_links = None
+
     @property
     def l10n_domain(self):
         if self._l10n_domain is None:
diff --git a/pyanaconda/installclasses/fedora.py b/pyanaconda/installclasses/fedora.py
index db20ed5..87e91fa 100644
--- a/pyanaconda/installclasses/fedora.py
+++ b/pyanaconda/installclasses/fedora.py
@@ -38,6 +38,9 @@ class InstallClass(BaseInstallClass):
 
     efi_dir = "fedora"
 
+    help_placeholder = "FedoraPlaceholder.html"
+    help_placeholder_with_links = "FedoraPlaceholderWithLinks.html"
+
     def configure(self, anaconda):
         BaseInstallClass.configure(self, anaconda)
         BaseInstallClass.setDefaultPartitioning(self, anaconda.storage)
diff --git a/pyanaconda/installclasses/rhel.py b/pyanaconda/installclasses/rhel.py
index d7cb7c0..b1e4375 100644
--- a/pyanaconda/installclasses/rhel.py
+++ b/pyanaconda/installclasses/rhel.py
@@ -44,6 +44,9 @@ class InstallClass(BaseInstallClass):
 
     efi_dir = "redhat"
 
+    help_placeholder = "RHEL7Placeholder.html"
+    help_placeholder_with_links = "RHEL7PlaceholderWithLinks.html"
+
     def configure(self, anaconda):
         BaseInstallClass.configure(self, anaconda)
         BaseInstallClass.setDefaultPartitioning(self, anaconda.storage)
diff --git a/pyanaconda/ui/gui/__init__.py b/pyanaconda/ui/gui/__init__.py
index b64d983..9aeae7e 100644
--- a/pyanaconda/ui/gui/__init__.py
+++ b/pyanaconda/ui/gui/__init__.py
@@ -29,6 +29,7 @@ from pyanaconda import product, iutil
 
 from pyanaconda.ui import UserInterface, common
 from pyanaconda.ui.gui.utils import enlightbox, gtk_action_wait, busyCursor, unbusyCursor
+from pyanaconda import ihelp
 import os.path
 
 import logging
@@ -78,10 +79,15 @@ class GUIObject(common.UIObject):
                            that use GUI elements (Hubs & Spokes) from Anaconda
                            can override the translation domain with their own,
                            so that their subclasses are properly translated.
+       helpFile         -- The location of the yelp-compatible help file for the
+                           given GUI object. The default value of "" indicates
+                           that the object has not specific help file assigned
+                           and the default help file should be used.
     """
     builderObjects = []
     mainWidgetName = None
     uiFile = ""
+    helpFile = None
     translationDomain = "anaconda"
 
     screenshots_directory = "/tmp/anaconda-screenshots"
@@ -351,6 +357,7 @@ class GraphicalUserInterface(UserInterface):
 
         obj.register_event_cb("continue", self._on_continue_clicked)
         obj.register_event_cb("quit", self._on_quit_clicked)
+        obj.register_event_cb("help-button", self._on_help_clicked, obj)
 
         return obj
 
@@ -502,6 +509,11 @@ class GraphicalUserInterface(UserInterface):
         self._currentAction = nextAction
         self._actions.pop(0)
 
+    def _on_help_clicked(self, window, obj):
+        # the help button has been clicked, start the yelp viewer with
+        # content for the current screen
+        ihelp.start_yelp(ihelp.get_help_path(obj.helpFile, self.instclass))
+
     def _on_quit_clicked(self):
         dialog = self._quitDialog(None)
         with enlightbox(self._currentAction.window, dialog.window):
diff --git a/pyanaconda/ui/gui/hubs/__init__.py b/pyanaconda/ui/gui/hubs/__init__.py
index ce71929..19d9179 100644
--- a/pyanaconda/ui/gui/hubs/__init__.py
+++ b/pyanaconda/ui/gui/hubs/__init__.py
@@ -396,11 +396,13 @@ class Hub(GUIObject, common.Hub):
 
     ### SIGNAL HANDLERS
 
-    def register_event_cb(self, event, cb):
+    def register_event_cb(self, event, cb, *args):
         if event == "continue" and self.continueButton:
             self.continueButton.connect("clicked", lambda *args: cb())
         elif event == "quit" and self.quitButton:
             self.quitButton.connect("clicked", lambda *args: cb())
+        elif event == "help-button":
+            self.window.connect("help-button-clicked", cb, *args)
 
     def _on_spoke_clicked(self, selector, event, spoke):
         from gi.repository import Gdk
diff --git a/pyanaconda/ui/gui/spokes/__init__.py b/pyanaconda/ui/gui/spokes/__init__.py
index 32a1469..30e4d67 100644
--- a/pyanaconda/ui/gui/spokes/__init__.py
+++ b/pyanaconda/ui/gui/spokes/__init__.py
@@ -23,6 +23,7 @@ from pyanaconda.ui import common
 from pyanaconda.ui.common import collect
 from pyanaconda.ui.gui import GUIObject
 import os.path
+from pyanaconda import ihelp
 
 __all__ = ["StandaloneSpoke", "NormalSpoke", "PersonalizationSpoke",
            "collect_spokes"]
@@ -73,11 +74,13 @@ class StandaloneSpoke(Spoke, common.StandaloneSpoke):
         self.apply()
         cb()
 
-    def register_event_cb(self, event, cb):
+    def register_event_cb(self, event, cb, *args):
         if event == "continue":
             self.window.connect("continue-clicked", lambda *args: self._on_continue_clicked(cb))
         elif event == "quit":
             self.window.connect("quit-clicked", lambda *args: cb())
+        elif event == "help-button":
+            self.window.connect("help-button-clicked", cb, *args)
 
 # Inherit abstract methods from common.NormalSpoke
 # pylint: disable-msg=W0223
@@ -86,6 +89,14 @@ class NormalSpoke(Spoke, common.NormalSpoke):
         Spoke.__init__(self, data)
         common.NormalSpoke.__init__(self, data, storage, payload, instclass)
 
+        # Add a help handler
+        self.window.connect_after("help-button-clicked", self._on_help_clicked)
+
+    def _on_help_clicked(self, window):
+        # the help button has been clicked, start the yelp viewer with
+        # content for the current spoke
+        ihelp.start_yelp(ihelp.get_help_path(self.helpFile, self.instclass))
+
     def on_back_clicked(self, window):
         from gi.repository import Gtk
 
diff --git a/scripts/makeupdates b/scripts/makeupdates
index 8981c8f..e1c8d22 100755
--- a/scripts/makeupdates
+++ b/scripts/makeupdates
@@ -257,6 +257,9 @@ def copyUpdatedWidgets(updates, cwd):
     for f in files:
         path = os.path.normpath(cwd + "/widgets/src/.libs/" + f)
         if os.path.islink(path) and not os.path.exists(updates + libdir + os.path.basename(path)):
+            print("ASDASDASDASD")
+            print(path)
+            print(os.readlink(path), updates + libdir + os.path.basename(path))
             os.symlink(os.readlink(path), updates + libdir + os.path.basename(path))
         elif os.path.isfile(path):
             shutil.copy2(path, updates + libdir)
diff --git a/widgets/configure.ac b/widgets/configure.ac
index 5b9f066..833a152 100644
--- a/widgets/configure.ac
+++ b/widgets/configure.ac
@@ -20,8 +20,8 @@
 #
 
 AC_PREREQ([2.63])
-AC_INIT([AnacondaWidgets], [1.0], [clumens at redhat.com])
-AM_INIT_AUTOMAKE(AnacondaWidgets, 1.0)
+AC_INIT([AnacondaWidgets], [1.1], [clumens at redhat.com])
+AM_INIT_AUTOMAKE(AnacondaWidgets, 1.1)
 AM_SILENT_RULES([yes])
 
 AM_PATH_PYTHON
diff --git a/widgets/glade/AnacondaWidgets.xml b/widgets/glade/AnacondaWidgets.xml
index 56da94c..35cbf12 100644
--- a/widgets/glade/AnacondaWidgets.xml
+++ b/widgets/glade/AnacondaWidgets.xml
@@ -1,5 +1,5 @@
 <glade-catalog name="AnacondaWidgets"
-               version="1.0"
+               version="1.1"
                library="AnacondaWidgets"
                domain="glade-3"
                depends="gtk+">
@@ -25,6 +25,11 @@
                 <property id="distribution" translatable="True" />
                 <property id="window-name" translatable="True" />
             </properties>
+
+            <signals>
+              <signal since="1.1" id="help-button-clicked" />
+            </signals>
+
         </glade-widget-class>
 
         <glade-widget-class title="Standalone Spoke Window"
diff --git a/widgets/src/BaseWindow.c b/widgets/src/BaseWindow.c
index 8e9eef9..025960b 100644
--- a/widgets/src/BaseWindow.c
+++ b/widgets/src/BaseWindow.c
@@ -25,6 +25,8 @@
 #include "BaseWindow.h"
 #include "intl.h"
 
+ #include <atk/atk.h>
+
 /**
  * SECTION: BaseWindow
  * @title: AnacondaBaseWindow
@@ -73,6 +75,7 @@
 
 enum {
     SIGNAL_INFO_BAR_CLICKED,
+    SIGNAL_HELP_BUTTON_CLICKED,
     LAST_SIGNAL
 };
 
@@ -87,6 +90,7 @@ enum {
 #define DEFAULT_WINDOW_NAME   N_("SPOKE NAME")
 #define DEFAULT_BETA          N_("PRE-RELEASE / TESTING")
 #define LAYOUT_INDICATOR_LABEL_WIDTH 10
+#define HELP_BUTTON_LABEL N_("Help!")
 
 struct _AnacondaBaseWindowPrivate {
     gboolean    is_beta, info_shown;
@@ -95,6 +99,7 @@ struct _AnacondaBaseWindowPrivate {
     GtkWidget  *nav_box, *nav_area, *action_area;
     GtkWidget  *name_label, *distro_label, *beta_label;
     GtkWidget  *layout_indicator;
+    GtkWidget  *help_button;
 
     /* Untranslated versions of various things. */
     gchar *orig_name, *orig_distro, *orig_beta;
@@ -106,6 +111,7 @@ static void anaconda_base_window_buildable_init(GtkBuildableIface *iface);
 static void format_beta_label(AnacondaBaseWindow *window, const char *markup);
 
 static gboolean anaconda_base_window_info_bar_clicked(GtkWidget *widget, GdkEvent *event, AnacondaBaseWindow *win);
+static void anaconda_base_window_help_button_clicked(GtkButton *button, AnacondaBaseWindow *win);
 
 G_DEFINE_TYPE_WITH_CODE(AnacondaBaseWindow, anaconda_base_window, GTK_TYPE_WINDOW,
                         G_IMPLEMENT_INTERFACE(GTK_TYPE_BUILDABLE, anaconda_base_window_buildable_init))
@@ -151,6 +157,7 @@ static void anaconda_base_window_class_init(AnacondaBaseWindowClass *klass) {
                                                         G_PARAM_READWRITE));
 
     klass->info_bar_clicked = NULL;
+    klass->help_button_clicked = NULL;
 
     /**
      * AnacondaBaseWindow::info-bar-clicked:
@@ -170,6 +177,24 @@ static void anaconda_base_window_class_init(AnacondaBaseWindowClass *klass) {
                                                            g_cclosure_marshal_VOID__VOID,
                                                            G_TYPE_NONE, 0);
 
+    /**
+     * AnacondaBaseWindow::help-button-clicked:
+     * @window: the window that received the signal
+     *
+     * Emitted when the help button in the right corner has been activated
+     * (pressed and released). This is commonly used to open the help view with
+     * help content for the given spoke or hub
+     *
+     * Since: 1.1
+     */
+    window_signals[SIGNAL_HELP_BUTTON_CLICKED] = g_signal_new("help-button-clicked",
+                                                              G_TYPE_FROM_CLASS(object_class),
+                                                              G_SIGNAL_RUN_FIRST | G_SIGNAL_ACTION,
+                                                              G_STRUCT_OFFSET(AnacondaBaseWindowClass, help_button_clicked),
+                                                              NULL, NULL,
+                                                              g_cclosure_marshal_VOID__VOID,
+                                                              G_TYPE_NONE, 0);
+
     g_type_class_add_private(object_class, sizeof(AnacondaBaseWindowPrivate));
 }
 
@@ -188,6 +213,7 @@ GtkWidget *anaconda_base_window_new() {
 
 static void anaconda_base_window_init(AnacondaBaseWindow *win) {
     char *markup;
+    AtkObject *atk;
 
     win->priv = G_TYPE_INSTANCE_GET_PRIVATE(win,
                                             ANACONDA_TYPE_BASE_WINDOW,
@@ -288,11 +314,28 @@ static void anaconda_base_window_init(AnacondaBaseWindow *win) {
     gtk_widget_set_margin_top(win->priv->layout_indicator, 6);
     gtk_widget_set_margin_bottom(win->priv->layout_indicator, 6);
 
+    /* Create the help button. */
+    win->priv->help_button = gtk_button_new_with_label(HELP_BUTTON_LABEL);
+    gtk_widget_set_halign(win->priv->help_button, GTK_ALIGN_START);
+    gtk_widget_set_vexpand(win->priv->help_button, FALSE);
+    gtk_widget_set_valign(win->priv->help_button, GTK_ALIGN_END);
+    gtk_widget_set_margin_bottom(win->priv->help_button, 6);
+
+    atk = gtk_widget_get_accessible(win->priv->help_button);
+    atk_object_set_name(atk, HELP_BUTTON_LABEL);
+
+    /* Hook up some signals for that button.  The signal handlers here will
+     * just raise our own custom signals for the whole window.
+     */
+    g_signal_connect(win->priv->help_button, "clicked",
+                     G_CALLBACK(anaconda_base_window_help_button_clicked), win);
+
     /* Add everything to the nav area. */
     gtk_grid_attach(GTK_GRID(win->priv->nav_area), win->priv->name_label, 0, 0, 1, 1);
     gtk_grid_attach(GTK_GRID(win->priv->nav_area), win->priv->distro_label, 1, 0, 1, 1);
     gtk_grid_attach(GTK_GRID(win->priv->nav_area), win->priv->beta_label, 1, 1, 1, 1);
     gtk_grid_attach(GTK_GRID(win->priv->nav_area), win->priv->layout_indicator, 1, 2, 1, 1);
+    gtk_grid_attach(GTK_GRID(win->priv->nav_area), win->priv->help_button, 2, 1, 1, 2);
 }
 
 static void anaconda_base_window_get_property(GObject *object, guint prop_id, GValue *value, GParamSpec *pspec) {
@@ -550,6 +593,11 @@ static gboolean anaconda_base_window_info_bar_clicked(GtkWidget *wiget, GdkEvent
     return FALSE;
 }
 
+static void anaconda_base_window_help_button_clicked(GtkButton *button,
+                                                     AnacondaBaseWindow *win) {
+        g_signal_emit(win, window_signals[SIGNAL_HELP_BUTTON_CLICKED], 0);
+}
+
 /**
  * anaconda_base_window_clear_info:
  * @win: a #AnacondaBaseWindow
diff --git a/widgets/src/BaseWindow.h b/widgets/src/BaseWindow.h
index 6c08ee8..928743f 100644
--- a/widgets/src/BaseWindow.h
+++ b/widgets/src/BaseWindow.h
@@ -56,11 +56,14 @@ struct _AnacondaBaseWindow {
  *                pointer to be cast to a #GtkWindow pointer.
  * @info_bar_clicked : Function pointer called when the #AnacondaBaseWindow::info-bar-clicked
  *                     signal is emitted.
+ * @help_button_clicked: Function pointer called when the #AnacondaSpokeWindow::help-button-clicked
+ *                       signal is emitted.
  */
 struct _AnacondaBaseWindowClass {
     GtkWindowClass parent_class;
 
     void (* info_bar_clicked) (AnacondaBaseWindow *window);
+    void (* help_button_clicked) (AnacondaBaseWindow *window);
 };
 
 GType       anaconda_base_window_get_type (void);
-- 
1.9.3



More information about the anaconda-patches mailing list