[tepl] amtk: create amtk/ directory for Actions, Menus and Toolbars Kit
- From: Sébastien Wilmet <swilmet src gnome org>
- To: commits-list gnome org
- Cc:
- Subject: [tepl] amtk: create amtk/ directory for Actions, Menus and Toolbars Kit
- Date: Fri, 14 Jul 2017 14:00:52 +0000 (UTC)
commit b4faabeb8d55d592a6bc702191a55be0c2e99eee
Author: Sébastien Wilmet <swilmet gnome org>
Date: Thu Jul 13 11:57:09 2017 +0200
amtk: create amtk/ directory for Actions, Menus and Toolbars Kit
Those classes are not really related to text editors, they are more
general.
As a first step the files are simply copied, with the following files
adapted:
- amtk/tepl-types.h
- amtk/tepl.h
amtk/tepl-action-info-central-store.c | 156 +++++++++++
amtk/tepl-action-info-central-store.h | 71 +++++
amtk/tepl-action-info-store.c | 475 +++++++++++++++++++++++++++++++++
amtk/tepl-action-info-store.h | 80 ++++++
amtk/tepl-action-info.c | 405 ++++++++++++++++++++++++++++
amtk/tepl-action-info.h | 110 ++++++++
amtk/tepl-action-map.c | 105 ++++++++
amtk/tepl-action-map.h | 38 +++
amtk/tepl-menu-item.c | 174 ++++++++++++
amtk/tepl-menu-item.h | 41 +++
amtk/tepl-menu-shell.c | 413 ++++++++++++++++++++++++++++
amtk/tepl-menu-shell.h | 74 +++++
amtk/tepl-types.h | 39 +++
amtk/tepl.h | 36 +++
14 files changed, 2217 insertions(+), 0 deletions(-)
---
diff --git a/amtk/tepl-action-info-central-store.c b/amtk/tepl-action-info-central-store.c
new file mode 100644
index 0000000..ba607aa
--- /dev/null
+++ b/amtk/tepl-action-info-central-store.c
@@ -0,0 +1,156 @@
+/*
+ * This file is part of Tepl, a text editor library.
+ *
+ * Copyright 2017 - Sébastien Wilmet <swilmet gnome org>
+ *
+ * Tepl 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.
+ *
+ * Tepl 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 library; if not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include "tepl-action-info-central-store.h"
+#include "tepl-action-info.h"
+
+/**
+ * SECTION:action-info-central-store
+ * @Short_description: Aggregation of all TeplActionInfoStore's
+ * @Title: TeplActionInfoCentralStore
+ * @See_also: #TeplActionInfoStore
+ *
+ * #TeplActionInfoCentralStore is a singleton class containing the aggregation
+ * of all #TeplActionInfoStore's. Each time a #TeplActionInfo is added to a
+ * #TeplActionInfoStore, it is also added to the #TeplActionInfoCentralStore.
+ */
+
+/* API design:
+ *
+ * Why both TeplActionInfoStore and TeplActionInfoCentralStore are needed?
+ *
+ * Advantages of TeplActionInfoStore:
+ * - tepl_action_info_store_new() takes an optional GtkApplication parameter. It
+ * doesn't rely on g_application_get_default() (calling
+ * g_application_get_default() in a library is not really a good practice I
+ * think. In theory an app can have several GApplication instances).
+ * - tepl_action_info_store_check_all_used()
+ *
+ * Advantages of TeplActionInfoCentralStore:
+ * - The central store checks if there are no duplicated action names
+ * (globally).
+ * - [For the menu bar, easy to retrieve the tooltip to show it in the
+ * statusbar.] No longer relevant with tepl_menu_item_get_long_description().
+ *
+ * If there was only one of the two classes, hacks would be needed to achieve
+ * the above items. So by having the two classes, we have the best of both
+ * worlds. We should not be afraid to create a lot of classes, and see things in
+ * big.
+ */
+
+struct _TeplActionInfoCentralStorePrivate
+{
+ /* Key: owned gchar*: action name.
+ * Value: owned TeplActionInfo.
+ */
+ GHashTable *hash_table;
+};
+
+G_DEFINE_TYPE_WITH_PRIVATE (TeplActionInfoCentralStore, tepl_action_info_central_store, G_TYPE_OBJECT)
+
+static void
+tepl_action_info_central_store_finalize (GObject *object)
+{
+ TeplActionInfoCentralStore *central_store = TEPL_ACTION_INFO_CENTRAL_STORE (object);
+
+ g_hash_table_unref (central_store->priv->hash_table);
+
+ G_OBJECT_CLASS (tepl_action_info_central_store_parent_class)->finalize (object);
+}
+
+static void
+tepl_action_info_central_store_class_init (TeplActionInfoCentralStoreClass *klass)
+{
+ GObjectClass *object_class = G_OBJECT_CLASS (klass);
+
+ object_class->finalize = tepl_action_info_central_store_finalize;
+}
+
+static void
+tepl_action_info_central_store_init (TeplActionInfoCentralStore *central_store)
+{
+ central_store->priv = tepl_action_info_central_store_get_instance_private (central_store);
+
+ central_store->priv->hash_table = g_hash_table_new_full (g_str_hash,
+ g_str_equal,
+ g_free,
+ (GDestroyNotify) tepl_action_info_unref);
+}
+
+/**
+ * tepl_action_info_central_store_get_instance:
+ *
+ * Returns: (transfer none): the #TeplActionInfoCentralStore singleton instance.
+ * Since: 2.0
+ */
+TeplActionInfoCentralStore *
+tepl_action_info_central_store_get_instance (void)
+{
+ static TeplActionInfoCentralStore *instance = NULL;
+
+ if (G_UNLIKELY (instance == NULL))
+ {
+ instance = g_object_new (TEPL_TYPE_ACTION_INFO_CENTRAL_STORE, NULL);
+ }
+
+ return instance;
+}
+
+void
+_tepl_action_info_central_store_add (TeplActionInfoCentralStore *central_store,
+ TeplActionInfo *info)
+{
+ const gchar *action_name;
+
+ g_return_if_fail (TEPL_IS_ACTION_INFO_CENTRAL_STORE (central_store));
+ g_return_if_fail (info != NULL);
+
+ action_name = tepl_action_info_get_action_name (info);
+ g_return_if_fail (action_name != NULL);
+
+ if (g_hash_table_lookup (central_store->priv->hash_table, action_name) != NULL)
+ {
+ g_warning ("The TeplActionInfoCentralStore already contains a TeplActionInfo "
+ "with the action name “%s”. Libraries must namespace their action names.",
+ action_name);
+ return;
+ }
+
+ g_hash_table_insert (central_store->priv->hash_table,
+ g_strdup (action_name),
+ tepl_action_info_ref (info));
+}
+
+/**
+ * tepl_action_info_central_store_lookup:
+ * @central_store: a #TeplActionInfoCentralStore.
+ * @action_name: an action name.
+ *
+ * Returns: (transfer none): the found #TeplActionInfo, or %NULL.
+ * Since: 2.0
+ */
+const TeplActionInfo *
+tepl_action_info_central_store_lookup (TeplActionInfoCentralStore *central_store,
+ const gchar *action_name)
+{
+ g_return_val_if_fail (TEPL_IS_ACTION_INFO_CENTRAL_STORE (central_store), NULL);
+ g_return_val_if_fail (action_name != NULL, NULL);
+
+ return g_hash_table_lookup (central_store->priv->hash_table, action_name);
+}
diff --git a/amtk/tepl-action-info-central-store.h b/amtk/tepl-action-info-central-store.h
new file mode 100644
index 0000000..a6806a8
--- /dev/null
+++ b/amtk/tepl-action-info-central-store.h
@@ -0,0 +1,71 @@
+/*
+ * This file is part of Tepl, a text editor library.
+ *
+ * Copyright 2017 - Sébastien Wilmet <swilmet gnome org>
+ *
+ * Tepl 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.
+ *
+ * Tepl 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 library; if not, see <http://www.gnu.org/licenses/>.
+ */
+
+#ifndef TEPL_ACTION_INFO_CENTRAL_STORE_H
+#define TEPL_ACTION_INFO_CENTRAL_STORE_H
+
+#if !defined (TEPL_H_INSIDE) && !defined (TEPL_COMPILATION)
+#error "Only <tepl/tepl.h> can be included directly."
+#endif
+
+#include <glib-object.h>
+#include <tepl/tepl-types.h>
+
+G_BEGIN_DECLS
+
+#define TEPL_TYPE_ACTION_INFO_CENTRAL_STORE (tepl_action_info_central_store_get_type ())
+#define TEPL_ACTION_INFO_CENTRAL_STORE(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj),
TEPL_TYPE_ACTION_INFO_CENTRAL_STORE, TeplActionInfoCentralStore))
+#define TEPL_ACTION_INFO_CENTRAL_STORE_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass),
TEPL_TYPE_ACTION_INFO_CENTRAL_STORE, TeplActionInfoCentralStoreClass))
+#define TEPL_IS_ACTION_INFO_CENTRAL_STORE(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj),
TEPL_TYPE_ACTION_INFO_CENTRAL_STORE))
+#define TEPL_IS_ACTION_INFO_CENTRAL_STORE_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass),
TEPL_TYPE_ACTION_INFO_CENTRAL_STORE))
+#define TEPL_ACTION_INFO_CENTRAL_STORE_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj),
TEPL_TYPE_ACTION_INFO_CENTRAL_STORE, TeplActionInfoCentralStoreClass))
+
+typedef struct _TeplActionInfoCentralStoreClass TeplActionInfoCentralStoreClass;
+typedef struct _TeplActionInfoCentralStorePrivate TeplActionInfoCentralStorePrivate;
+
+struct _TeplActionInfoCentralStore
+{
+ GObject parent;
+
+ TeplActionInfoCentralStorePrivate *priv;
+};
+
+struct _TeplActionInfoCentralStoreClass
+{
+ GObjectClass parent_class;
+
+ gpointer padding[12];
+};
+
+GType tepl_action_info_central_store_get_type (void) G_GNUC_CONST;
+
+TeplActionInfoCentralStore *
+ tepl_action_info_central_store_get_instance (void);
+
+const TeplActionInfo *
+ tepl_action_info_central_store_lookup (TeplActionInfoCentralStore *central_store,
+ const gchar *action_name);
+
+G_GNUC_INTERNAL
+void _tepl_action_info_central_store_add (TeplActionInfoCentralStore *central_store,
+ TeplActionInfo *info);
+
+G_END_DECLS
+
+#endif /* TEPL_ACTION_INFO_CENTRAL_STORE_H */
diff --git a/amtk/tepl-action-info-store.c b/amtk/tepl-action-info-store.c
new file mode 100644
index 0000000..e88baf7
--- /dev/null
+++ b/amtk/tepl-action-info-store.c
@@ -0,0 +1,475 @@
+/*
+ * This file is part of Tepl, a text editor library.
+ *
+ * Copyright 2017 - Sébastien Wilmet <swilmet gnome org>
+ *
+ * Tepl 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.
+ *
+ * Tepl 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 library; if not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include "tepl-action-info-store.h"
+#include "tepl-action-info.h"
+#include "tepl-action-info-central-store.h"
+#include "tepl-menu-item.h"
+
+/**
+ * SECTION:action-info-store
+ * @Short_description: A store of TeplActionInfo's
+ * @Title: TeplActionInfoStore
+ * @See_also: #TeplActionInfo, #TeplActionInfoCentralStore
+ *
+ * #TeplActionInfoStore contains a set of #TeplActionInfo's.
+ *
+ * #TeplActionInfoStore is add-only, a #TeplActionInfo cannot be removed. If
+ * needed, the remove operation will be added in the future.
+ *
+ * A #GtkApplication can be associated so that when a widget is created,
+ * gtk_application_set_accels_for_action() is called. See
+ * tepl_action_info_store_create_menu_item() for more details. Note that this
+ * happens on widget creation, not when adding a #TeplActionInfo to the store,
+ * so that the accelerator is bound to the application only if the
+ * #TeplActionInfo is actually used.
+ *
+ * #TeplActionInfoStore is designed so that libraries can provide their own
+ * store, to share action information (with translations) and possibly the
+ * #GAction implementations as well. Application-specific #TeplActionInfo's can
+ * be added to the store returned by
+ * tepl_application_get_app_action_info_store().
+ *
+ * A library #TeplActionInfoStore must namespace the action names to not have
+ * conflicts when a #TeplActionInfo is added to the #TeplActionInfoCentralStore.
+ * Examples of namespaced action names: `"win.tepl-save"` or `"app.tepl-quit"`.
+ */
+
+struct _TeplActionInfoStorePrivate
+{
+ /* Weak ref, because usually GtkApplication owns (indirectly) a
+ * TeplActionInfoStore.
+ */
+ GtkApplication *app;
+
+ /* Key: owned gchar*: action name.
+ * Value: owned TeplActionInfo.
+ */
+ GHashTable *hash_table;
+};
+
+enum
+{
+ PROP_0,
+ PROP_APPLICATION,
+ N_PROPERTIES
+};
+
+static GParamSpec *properties[N_PROPERTIES];
+
+G_DEFINE_TYPE_WITH_PRIVATE (TeplActionInfoStore, tepl_action_info_store, G_TYPE_OBJECT)
+
+static void
+set_application (TeplActionInfoStore *store,
+ GtkApplication *app)
+{
+ g_return_if_fail (app == NULL || GTK_IS_APPLICATION (app));
+
+ g_assert (store->priv->app == NULL);
+
+ if (app == NULL)
+ {
+ return;
+ }
+
+ store->priv->app = app;
+ g_object_add_weak_pointer (G_OBJECT (store->priv->app),
+ (gpointer *) &store->priv->app);
+
+ g_object_notify_by_pspec (G_OBJECT (store), properties[PROP_APPLICATION]);
+}
+
+static void
+tepl_action_info_store_get_property (GObject *object,
+ guint prop_id,
+ GValue *value,
+ GParamSpec *pspec)
+{
+ TeplActionInfoStore *store = TEPL_ACTION_INFO_STORE (object);
+
+ switch (prop_id)
+ {
+ case PROP_APPLICATION:
+ g_value_set_object (value, tepl_action_info_store_get_application (store));
+ break;
+
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+ break;
+ }
+}
+
+static void
+tepl_action_info_store_set_property (GObject *object,
+ guint prop_id,
+ const GValue *value,
+ GParamSpec *pspec)
+{
+ TeplActionInfoStore *store = TEPL_ACTION_INFO_STORE (object);
+
+ switch (prop_id)
+ {
+ case PROP_APPLICATION:
+ set_application (store, g_value_get_object (value));
+ break;
+
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+ break;
+ }
+}
+
+static void
+tepl_action_info_store_dispose (GObject *object)
+{
+ TeplActionInfoStore *store = TEPL_ACTION_INFO_STORE (object);
+
+ if (store->priv->app != NULL)
+ {
+ g_object_remove_weak_pointer (G_OBJECT (store->priv->app),
+ (gpointer *) &store->priv->app);
+ store->priv->app = NULL;
+ }
+
+ G_OBJECT_CLASS (tepl_action_info_store_parent_class)->dispose (object);
+}
+
+static void
+tepl_action_info_store_finalize (GObject *object)
+{
+ TeplActionInfoStore *store = TEPL_ACTION_INFO_STORE (object);
+
+ g_hash_table_unref (store->priv->hash_table);
+
+ G_OBJECT_CLASS (tepl_action_info_store_parent_class)->finalize (object);
+}
+
+static void
+tepl_action_info_store_class_init (TeplActionInfoStoreClass *klass)
+{
+ GObjectClass *object_class = G_OBJECT_CLASS (klass);
+
+ object_class->get_property = tepl_action_info_store_get_property;
+ object_class->set_property = tepl_action_info_store_set_property;
+ object_class->dispose = tepl_action_info_store_dispose;
+ object_class->finalize = tepl_action_info_store_finalize;
+
+ /**
+ * TeplActionInfoStore:application:
+ *
+ * The associated #GtkApplication. #TeplActionInfoStore has a weak
+ * reference to the #GtkApplication.
+ *
+ * Since: 2.0
+ */
+ properties[PROP_APPLICATION] =
+ g_param_spec_object ("application",
+ "GtkApplication",
+ "",
+ GTK_TYPE_APPLICATION,
+ G_PARAM_READWRITE |
+ G_PARAM_CONSTRUCT_ONLY |
+ G_PARAM_STATIC_STRINGS);
+
+ g_object_class_install_properties (object_class, N_PROPERTIES, properties);
+}
+
+static void
+tepl_action_info_store_init (TeplActionInfoStore *store)
+{
+ store->priv = tepl_action_info_store_get_instance_private (store);
+
+ store->priv->hash_table = g_hash_table_new_full (g_str_hash,
+ g_str_equal,
+ g_free,
+ (GDestroyNotify) tepl_action_info_unref);
+}
+
+/**
+ * tepl_action_info_store_new:
+ * @application: (nullable): a #GtkApplication, or %NULL.
+ *
+ * Creates a new #TeplActionInfoStore object. Associating a #GtkApplication is
+ * optional.
+ *
+ * Returns: a new #TeplActionInfoStore.
+ * Since: 2.0
+ */
+TeplActionInfoStore *
+tepl_action_info_store_new (GtkApplication *application)
+{
+ g_return_val_if_fail (application == NULL || GTK_IS_APPLICATION (application), NULL);
+
+ return g_object_new (TEPL_TYPE_ACTION_INFO_STORE,
+ "application", application,
+ NULL);
+}
+
+/**
+ * tepl_action_info_store_get_application:
+ * @store: a #TeplActionInfoStore.
+ *
+ * Returns: (transfer none) (nullable): the associated #GtkApplication, or
+ * %NULL.
+ */
+GtkApplication *
+tepl_action_info_store_get_application (TeplActionInfoStore *store)
+{
+ g_return_val_if_fail (TEPL_IS_ACTION_INFO_STORE (store), NULL);
+
+ return store->priv->app;
+}
+
+/**
+ * tepl_action_info_store_add:
+ * @store: a #TeplActionInfoStore.
+ * @info: a #TeplActionInfo.
+ *
+ * Inserts @info into @store and into the #TeplActionInfoCentralStore. Both the
+ * @store and central store must <emphasis>not</emphasis> already contain a
+ * #TeplActionInfo with the same action name. The stores take their own
+ * reference on @info.
+ *
+ * Since: 2.0
+ */
+void
+tepl_action_info_store_add (TeplActionInfoStore *store,
+ TeplActionInfo *info)
+{
+ const gchar *action_name;
+ TeplActionInfoCentralStore *central_store;
+
+ g_return_if_fail (TEPL_IS_ACTION_INFO_STORE (store));
+ g_return_if_fail (info != NULL);
+
+ action_name = tepl_action_info_get_action_name (info);
+ g_return_if_fail (action_name != NULL);
+
+ if (g_hash_table_lookup (store->priv->hash_table, action_name) != NULL)
+ {
+ g_warning ("%s(): the TeplActionInfoStore already contains a TeplActionInfo "
+ "with the action name “%s”.",
+ G_STRFUNC,
+ action_name);
+ return;
+ }
+
+ g_hash_table_insert (store->priv->hash_table,
+ g_strdup (action_name),
+ tepl_action_info_ref (info));
+
+ central_store = tepl_action_info_central_store_get_instance ();
+ _tepl_action_info_central_store_add (central_store, info);
+}
+
+/**
+ * tepl_action_info_store_add_entries:
+ * @store: a #TeplActionInfoStore.
+ * @entries: (array length=n_entries) (element-type TeplActionInfoEntry): a
+ * pointer to the first item in an array of #TeplActionInfoEntry structs.
+ * @n_entries: the length of @entries, or -1 if @entries is %NULL-terminated.
+ * @translation_domain: (nullable): a gettext domain, or %NULL.
+ *
+ * Calls tepl_action_info_store_add() for each entry.
+ *
+ * If @translation_domain is not %NULL, g_dgettext() is used to translate the
+ * @label and @tooltip of each entry before setting them to the #TeplActionInfo.
+ *
+ * An API similar to g_action_map_add_action_entries().
+ *
+ * Since: 2.0
+ */
+void
+tepl_action_info_store_add_entries (TeplActionInfoStore *store,
+ const TeplActionInfoEntry *entries,
+ gint n_entries,
+ const gchar *translation_domain)
+{
+ gint i;
+
+ g_return_if_fail (TEPL_IS_ACTION_INFO_STORE (store));
+ g_return_if_fail (n_entries >= -1);
+ g_return_if_fail (entries != NULL || n_entries == 0);
+
+ for (i = 0; n_entries == -1 ? entries[i].action_name != NULL : i < n_entries; i++)
+ {
+ TeplActionInfo *info;
+
+ info = tepl_action_info_new_from_entry (&entries[i], translation_domain);
+ tepl_action_info_store_add (store, info);
+ tepl_action_info_unref (info);
+ }
+}
+
+/**
+ * tepl_action_info_store_lookup:
+ * @store: a #TeplActionInfoStore.
+ * @action_name: an action name.
+ *
+ * Returns: (transfer none): the found #TeplActionInfo, or %NULL.
+ * Since: 2.0
+ */
+const TeplActionInfo *
+tepl_action_info_store_lookup (TeplActionInfoStore *store,
+ const gchar *action_name)
+{
+ g_return_val_if_fail (TEPL_IS_ACTION_INFO_STORE (store), NULL);
+ g_return_val_if_fail (action_name != NULL, NULL);
+
+ return g_hash_table_lookup (store->priv->hash_table, action_name);
+}
+
+/**
+ * tepl_action_info_store_create_menu_item:
+ * @store: a #TeplActionInfoStore.
+ * @action_name: an action name.
+ *
+ * Creates a new #GtkMenuItem for @action_name. The @store must contain a
+ * #TeplActionInfo for @action_name.
+ *
+ * gtk_actionable_set_action_name() is called on the menu item with
+ * @action_name. The label is set with the #GtkMenuItem:use-underline property
+ * enabled. The first accelerator is set to the #GtkAccelLabel of the menu item.
+ * The icon is set. And the tooltip is set with
+ * tepl_menu_item_set_long_description().
+ *
+ * If #TeplActionInfoStore:application is non-%NULL, this function also calls
+ * gtk_application_set_accels_for_action() with the accelerators returned by
+ * tepl_action_info_get_accels() (this will erase previously set accelerators
+ * for that action, if any).
+ *
+ * Returns: (transfer floating): a new #GtkMenuItem for @action_name.
+ * Since: 2.0
+ */
+GtkWidget *
+tepl_action_info_store_create_menu_item (TeplActionInfoStore *store,
+ const gchar *action_name)
+{
+ GtkMenuItem *menu_item;
+ TeplActionInfo *action_info;
+ const gchar * const *accels;
+ const gchar *icon_name;
+ const gchar *tooltip;
+
+ g_return_val_if_fail (TEPL_IS_ACTION_INFO_STORE (store), NULL);
+ g_return_val_if_fail (action_name != NULL, NULL);
+
+ action_info = g_hash_table_lookup (store->priv->hash_table, action_name);
+
+ if (action_info == NULL)
+ {
+ g_warning ("%s(): action name '%s' not found.",
+ G_STRFUNC,
+ action_name);
+
+ return NULL;
+ }
+
+ menu_item = GTK_MENU_ITEM (gtk_menu_item_new ());
+
+ gtk_actionable_set_action_name (GTK_ACTIONABLE (menu_item), action_name);
+
+ gtk_menu_item_set_use_underline (menu_item, TRUE);
+ gtk_menu_item_set_label (menu_item, tepl_action_info_get_label (action_info));
+
+ /* Set accel before setting icon, because
+ * tepl_menu_item_set_icon_name() adds a GtkBox.
+ */
+ accels = tepl_action_info_get_accels (action_info);
+ if (accels != NULL && accels[0] != NULL)
+ {
+ guint accel_key;
+ GdkModifierType accel_mods;
+
+ gtk_accelerator_parse (accels[0], &accel_key, &accel_mods);
+
+ if (accel_key != 0 || accel_mods != 0)
+ {
+ GtkWidget *child;
+
+ child = gtk_bin_get_child (GTK_BIN (menu_item));
+
+ gtk_accel_label_set_accel (GTK_ACCEL_LABEL (child),
+ accel_key,
+ accel_mods);
+ }
+ }
+
+ icon_name = tepl_action_info_get_icon_name (action_info);
+ if (icon_name != NULL)
+ {
+ tepl_menu_item_set_icon_name (menu_item, icon_name);
+ }
+
+ tooltip = tepl_action_info_get_tooltip (action_info);
+ if (tooltip != NULL)
+ {
+ tepl_menu_item_set_long_description (menu_item, tooltip);
+ }
+
+ if (store->priv->app != NULL)
+ {
+ gtk_application_set_accels_for_action (store->priv->app,
+ action_name,
+ accels);
+ }
+
+ _tepl_action_info_set_used (action_info);
+
+ return GTK_WIDGET (menu_item);
+}
+
+static void
+check_used_cb (gpointer key,
+ gpointer value,
+ gpointer user_data)
+{
+ const gchar *action_name = key;
+ const TeplActionInfo *action_info = value;
+
+ if (!_tepl_action_info_get_used (action_info))
+ {
+ g_warning ("TeplActionInfo with action_name='%s' has not been used.",
+ action_name);
+ }
+}
+
+/**
+ * tepl_action_info_store_check_all_used:
+ * @store: a #TeplActionInfoStore.
+ *
+ * Checks that all #TeplActionInfo's of @store have been used by
+ * tepl_action_info_store_create_menu_item(). If not, a warning is printed and
+ * might indicate dead code.
+ *
+ * You probably want to call this function on the store returned by
+ * tepl_application_get_app_action_info_store(). But it can also be useful for a
+ * store provided by a library, to easily see which actions you don't use.
+ *
+ * Since: 2.0
+ */
+void
+tepl_action_info_store_check_all_used (TeplActionInfoStore *store)
+{
+ g_return_if_fail (TEPL_IS_ACTION_INFO_STORE (store));
+
+ g_hash_table_foreach (store->priv->hash_table,
+ check_used_cb,
+ NULL);
+}
diff --git a/amtk/tepl-action-info-store.h b/amtk/tepl-action-info-store.h
new file mode 100644
index 0000000..437019e
--- /dev/null
+++ b/amtk/tepl-action-info-store.h
@@ -0,0 +1,80 @@
+/*
+ * This file is part of Tepl, a text editor library.
+ *
+ * Copyright 2017 - Sébastien Wilmet <swilmet gnome org>
+ *
+ * Tepl 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.
+ *
+ * Tepl 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 library; if not, see <http://www.gnu.org/licenses/>.
+ */
+
+#ifndef TEPL_ACTION_INFO_STORE_H
+#define TEPL_ACTION_INFO_STORE_H
+
+#if !defined (TEPL_H_INSIDE) && !defined (TEPL_COMPILATION)
+#error "Only <tepl/tepl.h> can be included directly."
+#endif
+
+#include <gtk/gtk.h>
+#include <tepl/tepl-types.h>
+
+G_BEGIN_DECLS
+
+#define TEPL_TYPE_ACTION_INFO_STORE (tepl_action_info_store_get_type ())
+#define TEPL_ACTION_INFO_STORE(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj),
TEPL_TYPE_ACTION_INFO_STORE, TeplActionInfoStore))
+#define TEPL_ACTION_INFO_STORE_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass),
TEPL_TYPE_ACTION_INFO_STORE, TeplActionInfoStoreClass))
+#define TEPL_IS_ACTION_INFO_STORE(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj),
TEPL_TYPE_ACTION_INFO_STORE))
+#define TEPL_IS_ACTION_INFO_STORE_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass),
TEPL_TYPE_ACTION_INFO_STORE))
+#define TEPL_ACTION_INFO_STORE_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj),
TEPL_TYPE_ACTION_INFO_STORE, TeplActionInfoStoreClass))
+
+typedef struct _TeplActionInfoStoreClass TeplActionInfoStoreClass;
+typedef struct _TeplActionInfoStorePrivate TeplActionInfoStorePrivate;
+
+struct _TeplActionInfoStore
+{
+ GObject parent;
+
+ TeplActionInfoStorePrivate *priv;
+};
+
+struct _TeplActionInfoStoreClass
+{
+ GObjectClass parent_class;
+
+ gpointer padding[12];
+};
+
+GType tepl_action_info_store_get_type (void) G_GNUC_CONST;
+
+TeplActionInfoStore * tepl_action_info_store_new (GtkApplication *application);
+
+GtkApplication * tepl_action_info_store_get_application (TeplActionInfoStore *store);
+
+void tepl_action_info_store_add (TeplActionInfoStore *store,
+ TeplActionInfo *info);
+
+void tepl_action_info_store_add_entries (TeplActionInfoStore *store,
+ const TeplActionInfoEntry *entries,
+ gint n_entries,
+ const gchar
*translation_domain);
+
+const TeplActionInfo * tepl_action_info_store_lookup (TeplActionInfoStore *store,
+ const gchar *action_name);
+
+GtkWidget * tepl_action_info_store_create_menu_item (TeplActionInfoStore *store,
+ const gchar *action_name);
+
+void tepl_action_info_store_check_all_used (TeplActionInfoStore *store);
+
+G_END_DECLS
+
+#endif /* TEPL_ACTION_INFO_STORE_H */
diff --git a/amtk/tepl-action-info.c b/amtk/tepl-action-info.c
new file mode 100644
index 0000000..af9d7bd
--- /dev/null
+++ b/amtk/tepl-action-info.c
@@ -0,0 +1,405 @@
+/*
+ * This file is part of Tepl, a text editor library.
+ *
+ * Copyright 2017 - Sébastien Wilmet <swilmet gnome org>
+ *
+ * Tepl 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.
+ *
+ * Tepl 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 library; if not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include "config.h"
+#include "tepl-action-info.h"
+#include <glib/gi18n-lib.h>
+#include "tepl-utils.h"
+
+/**
+ * SECTION:action-info
+ * @Short_description: GAction information
+ * @Title: TeplActionInfo
+ * @See_also: #TeplActionInfoStore
+ *
+ * A #TeplActionInfo instance contains a set of information about a #GAction.
+ * Those pieces of information are useful to create UI elements that trigger the
+ * #GAction, for example a menu item or a toolbar item.
+ *
+ * When writing an XML file to create a #GMenu, with the format understood by
+ * #GtkBuilder (see the class description of #GtkApplicationWindow), the
+ * information in the XML file can be used only to create a #GMenu. The initial
+ * goal with #TeplActionInfo and its related classes is to encode the
+ * information just once, and be able to create both a menu and a toolbar easily
+ * (to have a traditional user interface).
+ */
+
+struct _TeplActionInfo
+{
+ gchar *action_name;
+ gchar *icon_name;
+ gchar *label;
+ gchar *tooltip;
+
+ /* Must never be NULL, must be a NULL-terminated array. This way, it
+ * can be used directly as an argument to
+ * gtk_application_set_accels_for_action().
+ */
+ gchar **accels;
+
+ gint ref_count;
+
+ guint used : 1;
+};
+
+static void _tepl_action_info_free (TeplActionInfo *info);
+
+G_DEFINE_BOXED_TYPE (TeplActionInfo, tepl_action_info,
+ tepl_action_info_copy,
+ _tepl_action_info_free)
+
+static void
+_tepl_action_info_free (TeplActionInfo *info)
+{
+ if (info != NULL)
+ {
+ g_free (info->action_name);
+ g_free (info->icon_name);
+ g_free (info->label);
+ g_free (info->tooltip);
+ g_strfreev (info->accels);
+
+ g_free (info);
+ }
+}
+
+/**
+ * tepl_action_info_new:
+ *
+ * Returns: a new #TeplActionInfo.
+ * Since: 2.0
+ */
+TeplActionInfo *
+tepl_action_info_new (void)
+{
+ TeplActionInfo *info;
+
+ info = g_new0 (TeplActionInfo, 1);
+ info->accels = g_malloc0 (sizeof (gchar *));
+ info->ref_count = 1;
+
+ return info;
+}
+
+/**
+ * tepl_action_info_new_from_entry:
+ * @info_entry: a #TeplActionInfoEntry.
+ * @translation_domain: (nullable): a gettext domain, or %NULL.
+ *
+ * Creates a new #TeplActionInfo from a #TeplActionInfoEntry.
+ *
+ * If @translation_domain is not %NULL, g_dgettext() is used to translate the
+ * @label and @tooltip before setting them to the #TeplActionInfo.
+ *
+ * Returns: a new #TeplActionInfo.
+ * Since: 2.0
+ */
+TeplActionInfo *
+tepl_action_info_new_from_entry (const TeplActionInfoEntry *info_entry,
+ const gchar *translation_domain)
+{
+ TeplActionInfo *info;
+
+ info = tepl_action_info_new ();
+ info->action_name = g_strdup (info_entry->action_name);
+ info->icon_name = g_strdup (info_entry->icon_name);
+
+ if (translation_domain != NULL)
+ {
+ info->label = g_strdup (g_dgettext (translation_domain, info_entry->label));
+ info->tooltip = g_strdup (g_dgettext (translation_domain, info_entry->tooltip));
+ }
+ else
+ {
+ info->label = g_strdup (info_entry->label);
+ info->tooltip = g_strdup (info_entry->tooltip);
+ }
+
+ if (info_entry->accel != NULL)
+ {
+ g_strfreev (info->accels);
+
+ info->accels = g_malloc (2 * sizeof (gchar *));
+ info->accels[0] = g_strdup (info_entry->accel);
+ info->accels[1] = NULL;
+ }
+
+ return info;
+}
+
+/**
+ * tepl_action_info_ref:
+ * @info: a #TeplActionInfo.
+ *
+ * Increments the reference count of @info by one.
+ *
+ * Returns: the passed in @info.
+ * Since: 2.0
+ */
+TeplActionInfo *
+tepl_action_info_ref (TeplActionInfo *info)
+{
+ g_return_val_if_fail (info != NULL, NULL);
+
+ info->ref_count++;
+
+ return info;
+}
+
+/**
+ * tepl_action_info_unref:
+ * @info: a #TeplActionInfo.
+ *
+ * Decrements the reference count of @info by one. If the reference count drops
+ * to 0, @info is freed.
+ *
+ * Since: 2.0
+ */
+void
+tepl_action_info_unref (TeplActionInfo *info)
+{
+ g_return_if_fail (info != NULL);
+
+ info->ref_count--;
+
+ if (info->ref_count == 0)
+ {
+ _tepl_action_info_free (info);
+ }
+}
+
+/**
+ * tepl_action_info_copy:
+ * @info: a #TeplActionInfo.
+ *
+ * Returns: (transfer full): a copy of @info. The copy will have a reference
+ * count of one.
+ * Since: 2.0
+ */
+TeplActionInfo *
+tepl_action_info_copy (const TeplActionInfo *info)
+{
+ TeplActionInfo *new_info;
+
+ g_return_val_if_fail (info != NULL, NULL);
+
+ new_info = tepl_action_info_new ();
+
+ new_info->action_name = g_strdup (info->action_name);
+ new_info->icon_name = g_strdup (info->icon_name);
+ new_info->label = g_strdup (info->label);
+ new_info->tooltip = g_strdup (info->tooltip);
+
+ tepl_action_info_set_accels (new_info, (const gchar * const *)info->accels);
+
+ return new_info;
+}
+
+/**
+ * tepl_action_info_get_action_name:
+ * @info: a #TeplActionInfo.
+ *
+ * Returns: (nullable): the action name, or %NULL. Example: `"win.save"`.
+ * Since: 2.0
+ */
+const gchar *
+tepl_action_info_get_action_name (const TeplActionInfo *info)
+{
+ g_return_val_if_fail (info != NULL, NULL);
+
+ return info->action_name;
+}
+
+/**
+ * tepl_action_info_set_action_name:
+ * @info: a #TeplActionInfo.
+ * @action_name: the action name.
+ *
+ * Sets the action name, for example `"win.save"`.
+ *
+ * Since: 2.0
+ */
+void
+tepl_action_info_set_action_name (TeplActionInfo *info,
+ const gchar *action_name)
+{
+ g_return_if_fail (info != NULL);
+ g_return_if_fail (action_name != NULL);
+
+ g_free (info->action_name);
+ info->action_name = g_strdup (action_name);
+}
+
+/**
+ * tepl_action_info_get_icon_name:
+ * @info: a #TeplActionInfo.
+ *
+ * Returns: (nullable): the icon name, or %NULL.
+ * Since: 2.0
+ */
+const gchar *
+tepl_action_info_get_icon_name (const TeplActionInfo *info)
+{
+ g_return_val_if_fail (info != NULL, NULL);
+
+ return info->icon_name;
+}
+
+/**
+ * tepl_action_info_set_icon_name:
+ * @info: a #TeplActionInfo.
+ * @icon_name: (nullable): the icon name, or %NULL.
+ *
+ * Since: 2.0
+ */
+void
+tepl_action_info_set_icon_name (TeplActionInfo *info,
+ const gchar *icon_name)
+{
+ g_return_if_fail (info != NULL);
+
+ g_free (info->icon_name);
+ info->icon_name = g_strdup (icon_name);
+}
+
+/**
+ * tepl_action_info_get_label:
+ * @info: a #TeplActionInfo.
+ *
+ * Returns: (nullable): the label (i.e. a short description), or %NULL.
+ * Since: 2.0
+ */
+const gchar *
+tepl_action_info_get_label (const TeplActionInfo *info)
+{
+ g_return_val_if_fail (info != NULL, NULL);
+
+ return info->label;
+}
+
+/**
+ * tepl_action_info_set_label:
+ * @info: a #TeplActionInfo.
+ * @label: (nullable): the label (i.e. a short description), or %NULL.
+ *
+ * Since: 2.0
+ */
+void
+tepl_action_info_set_label (TeplActionInfo *info,
+ const gchar *label)
+{
+ g_return_if_fail (info != NULL);
+
+ g_free (info->label);
+ info->label = g_strdup (label);
+}
+
+/**
+ * tepl_action_info_get_tooltip:
+ * @info: a #TeplActionInfo.
+ *
+ * Returns: (nullable): the tooltip (i.e. a long description), or %NULL.
+ * Since: 2.0
+ */
+const gchar *
+tepl_action_info_get_tooltip (const TeplActionInfo *info)
+{
+ g_return_val_if_fail (info != NULL, NULL);
+
+ return info->tooltip;
+}
+
+/**
+ * tepl_action_info_set_tooltip:
+ * @info: a #TeplActionInfo.
+ * @tooltip: (nullable): the tooltip (i.e. a long description), or %NULL.
+ *
+ * Since: 2.0
+ */
+void
+tepl_action_info_set_tooltip (TeplActionInfo *info,
+ const gchar *tooltip)
+{
+ g_return_if_fail (info != NULL);
+
+ g_free (info->tooltip);
+ info->tooltip = g_strdup (tooltip);
+}
+
+/**
+ * tepl_action_info_get_accels:
+ * @info: a #TeplActionInfo.
+ *
+ * Returns the accelerators. This function never returns %NULL, it always
+ * returns a %NULL-terminated array, to be suitable for
+ * gtk_application_set_accels_for_action().
+ *
+ * Returns: (transfer none) (array zero-terminated=1): a %NULL-terminated array
+ * of accelerators in the format understood by gtk_accelerator_parse().
+ * Since: 2.0
+ */
+const gchar * const *
+tepl_action_info_get_accels (const TeplActionInfo *info)
+{
+ g_return_val_if_fail (info != NULL, NULL);
+
+ g_assert (info->accels != NULL);
+
+ return (const gchar * const *)info->accels;
+}
+
+/**
+ * tepl_action_info_set_accels:
+ * @info: a #TeplActionInfo.
+ * @accels: (array zero-terminated=1): a %NULL-terminated array of accelerators
+ * in the format understood by gtk_accelerator_parse().
+ *
+ * A function similar to gtk_application_set_accels_for_action().
+ *
+ * @accels must not be %NULL, it must be a %NULL-terminated array, to be
+ * consistent with gtk_application_set_accels_for_action().
+ *
+ * Since: 2.0
+ */
+void
+tepl_action_info_set_accels (TeplActionInfo *info,
+ const gchar * const *accels)
+{
+ g_return_if_fail (info != NULL);
+ g_return_if_fail (accels != NULL);
+
+ g_strfreev (info->accels);
+ info->accels = _tepl_utils_strv_copy (accels);
+}
+
+gboolean
+_tepl_action_info_get_used (const TeplActionInfo *info)
+{
+ g_return_val_if_fail (info != NULL, FALSE);
+
+ return info->used;
+}
+
+void
+_tepl_action_info_set_used (TeplActionInfo *info)
+{
+ g_return_if_fail (info != NULL);
+
+ info->used = TRUE;
+}
diff --git a/amtk/tepl-action-info.h b/amtk/tepl-action-info.h
new file mode 100644
index 0000000..6f46d63
--- /dev/null
+++ b/amtk/tepl-action-info.h
@@ -0,0 +1,110 @@
+/*
+ * This file is part of Tepl, a text editor library.
+ *
+ * Copyright 2017 - Sébastien Wilmet <swilmet gnome org>
+ *
+ * Tepl 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.
+ *
+ * Tepl 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 library; if not, see <http://www.gnu.org/licenses/>.
+ */
+
+#ifndef TEPL_ACTION_INFO_H
+#define TEPL_ACTION_INFO_H
+
+#if !defined (TEPL_H_INSIDE) && !defined (TEPL_COMPILATION)
+#error "Only <tepl/tepl.h> can be included directly."
+#endif
+
+#include <glib-object.h>
+#include <tepl/tepl-types.h>
+
+G_BEGIN_DECLS
+
+#define TEPL_TYPE_ACTION_INFO (tepl_action_info_get_type ())
+
+/**
+ * TeplActionInfoEntry:
+ * @action_name: the action name.
+ * @icon_name: the icon name, or %NULL.
+ * @label: the label (i.e. a short description), or %NULL.
+ * @accel: the accelerator, in the format understood by gtk_accelerator_parse().
+ * Or %NULL.
+ * @tooltip: the tooltip (i.e. a long description), or %NULL.
+ *
+ * This struct defines a set of information for a single action. It is for use
+ * with tepl_action_info_store_add_entries().
+ *
+ * Like #GActionEntry, it is permissible to use an incomplete initialiser in
+ * order to leave some of the later values as %NULL. Additional optional fields
+ * may be added in the future.
+ *
+ * Since: 2.0
+ */
+struct _TeplActionInfoEntry
+{
+ const gchar *action_name;
+ const gchar *icon_name;
+ const gchar *label;
+ const gchar *accel;
+ const gchar *tooltip;
+
+ /*< private >*/
+ gpointer padding[3];
+};
+
+GType tepl_action_info_get_type (void) G_GNUC_CONST;
+
+TeplActionInfo * tepl_action_info_new (void);
+
+TeplActionInfo * tepl_action_info_new_from_entry (const TeplActionInfoEntry *info_entry,
+ const gchar
*translation_domain);
+
+TeplActionInfo * tepl_action_info_ref (TeplActionInfo *info);
+
+void tepl_action_info_unref (TeplActionInfo *info);
+
+TeplActionInfo * tepl_action_info_copy (const TeplActionInfo *info);
+
+const gchar * tepl_action_info_get_action_name (const TeplActionInfo *info);
+
+void tepl_action_info_set_action_name (TeplActionInfo *info,
+ const gchar *action_name);
+
+const gchar * tepl_action_info_get_icon_name (const TeplActionInfo *info);
+
+void tepl_action_info_set_icon_name (TeplActionInfo *info,
+ const gchar *icon_name);
+
+const gchar * tepl_action_info_get_label (const TeplActionInfo *info);
+
+void tepl_action_info_set_label (TeplActionInfo *info,
+ const gchar *label);
+
+const gchar * tepl_action_info_get_tooltip (const TeplActionInfo *info);
+
+void tepl_action_info_set_tooltip (TeplActionInfo *info,
+ const gchar *tooltip);
+
+const gchar * const * tepl_action_info_get_accels (const TeplActionInfo *info);
+
+void tepl_action_info_set_accels (TeplActionInfo *info,
+ const gchar * const *accels);
+
+G_GNUC_INTERNAL
+gboolean _tepl_action_info_get_used (const TeplActionInfo *info);
+
+G_GNUC_INTERNAL
+void _tepl_action_info_set_used (TeplActionInfo *info);
+
+G_END_DECLS
+
+#endif /* TEPL_ACTION_INFO_H */
diff --git a/amtk/tepl-action-map.c b/amtk/tepl-action-map.c
new file mode 100644
index 0000000..7ffc046
--- /dev/null
+++ b/amtk/tepl-action-map.c
@@ -0,0 +1,105 @@
+/*
+ * This file is part of Tepl, a text editor library.
+ *
+ * Copyright 2017 - Sébastien Wilmet <swilmet gnome org>
+ *
+ * Tepl 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.
+ *
+ * Tepl 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 library; if not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include "tepl-action-map.h"
+
+/**
+ * SECTION:action-map
+ * @Short_description: GActionMap wrapper functions
+ * @Title: TeplActionMap
+ *
+ * #GActionMap wrapper functions.
+ */
+
+static void
+check_dups_in_array (const GActionEntry *entries,
+ const gchar *action_name,
+ gint action_num)
+{
+ gint i;
+
+ for (i = 0; i < action_num; i++)
+ {
+ const GActionEntry *entry = &entries[i];
+
+ if (g_strcmp0 (action_name, entry->name) == 0)
+ {
+ g_warning ("tepl_action_map_add_action_entries_check_dups(): "
+ "the GActionEntry array contains duplicated entries for the action name
'%s'. "
+ "The first one will be dropped from the GActionMap.",
+ action_name);
+ return;
+ }
+ }
+}
+
+/**
+ * tepl_action_map_add_action_entries_check_dups:
+ * @action_map: a #GActionMap.
+ * @entries: (array length=n_entries) (element-type GActionEntry): a pointer to
+ * the first item in an array of #GActionEntry structs.
+ * @n_entries: the length of @entries, or -1 if @entries is %NULL-terminated.
+ * @user_data: the user data for signal connections.
+ *
+ * A wrapper function for g_action_map_add_action_entries() that checks
+ * duplicates.
+ *
+ * This function first checks - for each entry - that the @action_map doesn't
+ * already contain a #GAction with the same name. A warning is printed if an old
+ * action will be dropped. In any case, it then calls
+ * g_action_map_add_action_entries() with the same arguments as passed to this
+ * function.
+ *
+ * This function also checks if there are duplicates in the @entries array
+ * itself.
+ *
+ * Since: 2.0
+ */
+void
+tepl_action_map_add_action_entries_check_dups (GActionMap *action_map,
+ const GActionEntry *entries,
+ gint n_entries,
+ gpointer user_data)
+{
+ gint i;
+
+ g_return_if_fail (G_IS_ACTION_MAP (action_map));
+ g_return_if_fail (n_entries >= -1);
+ g_return_if_fail (entries != NULL || n_entries == 0);
+
+ for (i = 0; n_entries == -1 ? entries[i].name != NULL : i < n_entries; i++)
+ {
+ const GActionEntry *entry = &entries[i];
+
+ if (g_action_map_lookup_action (action_map, entry->name) != NULL)
+ {
+ g_warning ("%s(): the GActionMap already contains a GAction with the name '%s'. "
+ "The old GAction will be dropped from the GActionMap.",
+ G_STRFUNC,
+ entry->name);
+ }
+
+ check_dups_in_array (entries, entry->name, i);
+ }
+
+ g_action_map_add_action_entries (action_map,
+ entries,
+ n_entries,
+ user_data);
+}
diff --git a/amtk/tepl-action-map.h b/amtk/tepl-action-map.h
new file mode 100644
index 0000000..6252ca1
--- /dev/null
+++ b/amtk/tepl-action-map.h
@@ -0,0 +1,38 @@
+/*
+ * This file is part of Tepl, a text editor library.
+ *
+ * Copyright 2017 - Sébastien Wilmet <swilmet gnome org>
+ *
+ * Tepl 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.
+ *
+ * Tepl 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 library; if not, see <http://www.gnu.org/licenses/>.
+ */
+
+#ifndef TEPL_ACTION_MAP_H
+#define TEPL_ACTION_MAP_H
+
+#if !defined (TEPL_H_INSIDE) && !defined (TEPL_COMPILATION)
+#error "Only <tepl/tepl.h> can be included directly."
+#endif
+
+#include <gio/gio.h>
+
+G_BEGIN_DECLS
+
+void tepl_action_map_add_action_entries_check_dups (GActionMap *action_map,
+ const GActionEntry *entries,
+ gint n_entries,
+ gpointer user_data);
+
+G_END_DECLS
+
+#endif /* TEPL_ACTION_MAP_H */
diff --git a/amtk/tepl-menu-item.c b/amtk/tepl-menu-item.c
new file mode 100644
index 0000000..d095ceb
--- /dev/null
+++ b/amtk/tepl-menu-item.c
@@ -0,0 +1,174 @@
+/*
+ * This file is part of Tepl, a text editor library.
+ *
+ * Copyright 2017 - Sébastien Wilmet <swilmet gnome org>
+ *
+ * Tepl 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.
+ *
+ * Tepl 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 library; if not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include "tepl-menu-item.h"
+
+/**
+ * SECTION:menu-item
+ * @Short_description: GtkMenuItem functions
+ * @Title: TeplMenuItem
+ *
+ * #GtkMenuItem functions.
+ */
+
+#define LONG_DESCRIPTION_KEY "tepl-menu-item-long-description-key"
+
+/**
+ * tepl_menu_item_get_long_description:
+ * @menu_item: a #GtkMenuItem.
+ *
+ * Returns: (nullable): the long description of @menu_item, previously set with
+ * tepl_menu_item_set_long_description().
+ * Since: 2.0
+ */
+const gchar *
+tepl_menu_item_get_long_description (GtkMenuItem *menu_item)
+{
+ g_return_val_if_fail (GTK_IS_MENU_ITEM (menu_item), NULL);
+
+ return g_object_get_data (G_OBJECT (menu_item), LONG_DESCRIPTION_KEY);
+}
+
+/**
+ * tepl_menu_item_set_long_description:
+ * @menu_item: a #GtkMenuItem.
+ * @long_description: (nullable): the long description, or %NULL to unset it.
+ *
+ * Sets the long description of @menu_item. A possible use-case is to display it
+ * in a #GtkStatusbar, or as a tooltip.
+ *
+ * Since: 2.0
+ */
+void
+tepl_menu_item_set_long_description (GtkMenuItem *menu_item,
+ const gchar *long_description)
+{
+ g_return_if_fail (GTK_IS_MENU_ITEM (menu_item));
+
+ g_object_set_data_full (G_OBJECT (menu_item),
+ LONG_DESCRIPTION_KEY,
+ g_strdup (long_description),
+ g_free);
+}
+
+/**
+ * tepl_menu_item_set_icon_name:
+ * @item: a #GtkMenuItem.
+ * @icon_name: an icon name.
+ *
+ * Sets an icon to a #GtkMenuItem.
+ *
+ * If the child widget of @item is already a #GtkBox, all #GtkImage widgets
+ * inside that box are first destroyed. A #GtkImage for @icon_name is then
+ * inserted to the box.
+ *
+ * If the child widget of @item is not a #GtkBox (it's usually the
+ * #GtkAccelLabel), it is replaced by a new #GtkBox and the initial child widget
+ * is inserted to the #GtkBox, alongside the icon.
+ *
+ * As a consequence, if you want to call functions on the #GtkAccelLabel, it's
+ * easier to do it before calling this function.
+ *
+ * Since: 2.0
+ */
+/* Based on gtk_model_menu_item_set_icon() from gtkmodelmenuitem.c (private
+ * GTK+ class).
+ * Copyright 2011, 2013 Canonical Limited
+ */
+void
+tepl_menu_item_set_icon_name (GtkMenuItem *item,
+ const gchar *icon_name)
+{
+ GtkWidget *child;
+
+ g_return_if_fail (GTK_IS_MENU_ITEM (item));
+
+ child = gtk_bin_get_child (GTK_BIN (item));
+
+ /* There are only three possibilities here:
+ *
+ * - no child
+ * - accel label child
+ * - already a box
+ *
+ * Handle the no-child case by having GtkMenuItem create the accel
+ * label, then we will only have two possible cases.
+ */
+ if (child == NULL)
+ {
+ gtk_menu_item_get_label (item);
+ child = gtk_bin_get_child (GTK_BIN (item));
+ g_return_if_fail (GTK_IS_LABEL (child));
+ }
+
+ /* If it is a box, make sure there are no images inside of it already. */
+ if (GTK_IS_BOX (child))
+ {
+ GList *children;
+
+ children = gtk_container_get_children (GTK_CONTAINER (child));
+ while (children != NULL)
+ {
+ if (GTK_IS_IMAGE (children->data))
+ {
+ gtk_widget_destroy (children->data);
+ }
+
+ children = g_list_delete_link (children, children);
+ }
+ }
+ else
+ {
+ GtkWidget *box;
+
+ if (icon_name == NULL)
+ {
+ return;
+ }
+
+ box = gtk_box_new (GTK_ORIENTATION_HORIZONTAL, 6);
+
+ /* Reparent the child without destroying it */
+ g_object_ref (child);
+ gtk_container_remove (GTK_CONTAINER (item), child);
+ gtk_box_pack_end (GTK_BOX (box), child, TRUE, TRUE, 0);
+ g_object_unref (child);
+
+ gtk_container_add (GTK_CONTAINER (item), box);
+ gtk_widget_show (box);
+
+ /* Now we have a box */
+ child = box;
+ }
+
+ g_assert (GTK_IS_BOX (child));
+
+ /* child is now a box containing a label and no image. Add the icon,
+ * if appropriate.
+ */
+ if (icon_name != NULL)
+ {
+ GtkWidget *image;
+
+ image = gtk_image_new_from_icon_name (icon_name, GTK_ICON_SIZE_MENU);
+ gtk_image_set_pixel_size (GTK_IMAGE (image), 16);
+ gtk_box_pack_start (GTK_BOX (child), image, FALSE, FALSE, 0);
+ gtk_widget_show (image);
+ }
+}
diff --git a/amtk/tepl-menu-item.h b/amtk/tepl-menu-item.h
new file mode 100644
index 0000000..ebb371a
--- /dev/null
+++ b/amtk/tepl-menu-item.h
@@ -0,0 +1,41 @@
+/*
+ * This file is part of Tepl, a text editor library.
+ *
+ * Copyright 2017 - Sébastien Wilmet <swilmet gnome org>
+ *
+ * Tepl 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.
+ *
+ * Tepl 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 library; if not, see <http://www.gnu.org/licenses/>.
+ */
+
+#ifndef TEPL_MENU_ITEM_H
+#define TEPL_MENU_ITEM_H
+
+#if !defined (TEPL_H_INSIDE) && !defined (TEPL_COMPILATION)
+#error "Only <tepl/tepl.h> can be included directly."
+#endif
+
+#include <gtk/gtk.h>
+
+G_BEGIN_DECLS
+
+const gchar * tepl_menu_item_get_long_description (GtkMenuItem *menu_item);
+
+void tepl_menu_item_set_long_description (GtkMenuItem *menu_item,
+ const gchar *long_description);
+
+void tepl_menu_item_set_icon_name (GtkMenuItem *item,
+ const gchar *icon_name);
+
+G_END_DECLS
+
+#endif /* TEPL_MENU_ITEM_H */
diff --git a/amtk/tepl-menu-shell.c b/amtk/tepl-menu-shell.c
new file mode 100644
index 0000000..ddf641f
--- /dev/null
+++ b/amtk/tepl-menu-shell.c
@@ -0,0 +1,413 @@
+/*
+ * This file is part of Tepl, a text editor library.
+ *
+ * Copyright 2016, 2017 - Sébastien Wilmet <swilmet gnome org>
+ *
+ * Tepl 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.
+ *
+ * Tepl 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 library; if not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include "tepl-menu-shell.h"
+
+/**
+ * SECTION:menu-shell
+ * @Short_description: An extension of GtkMenuShell
+ * @Title: TeplMenuShell
+ *
+ * #TeplMenuShell extends the #GtkMenuShell abstract class with the
+ * #TeplMenuShell::menu-item-selected and #TeplMenuShell::menu-item-deselected
+ * convenience signals.
+ *
+ * One possible use-case is to push/pop longer descriptions of menu items to a
+ * #GtkStatusbar, exactly like
+ * tepl_application_window_connect_menu_to_statusbar() does.
+ */
+
+struct _TeplMenuShellPrivate
+{
+ GtkMenuShell *gtk_menu_shell;
+};
+
+enum
+{
+ PROP_0,
+ PROP_MENU_SHELL,
+ N_PROPERTIES
+};
+
+enum
+{
+ SIGNAL_MENU_ITEM_SELECTED,
+ SIGNAL_MENU_ITEM_DESELECTED,
+ N_SIGNALS
+};
+
+#define TEPL_MENU_SHELL_KEY "tepl-menu-shell-key"
+
+static GParamSpec *properties[N_PROPERTIES];
+static guint signals[N_SIGNALS];
+
+G_DEFINE_TYPE_WITH_PRIVATE (TeplMenuShell, tepl_menu_shell, G_TYPE_OBJECT)
+
+/* Prototypes */
+static void connect_menu_shell (TeplMenuShell *tepl_menu_shell,
+ GtkMenuShell *gtk_menu_shell);
+
+static void disconnect_menu_shell (TeplMenuShell *tepl_menu_shell,
+ GtkMenuShell *gtk_menu_shell);
+
+static void
+menu_item_select_cb (GtkMenuItem *menu_item,
+ gpointer user_data)
+{
+ TeplMenuShell *tepl_menu_shell = TEPL_MENU_SHELL (user_data);
+ GtkWidget *submenu;
+
+ submenu = gtk_menu_item_get_submenu (menu_item);
+
+ if (GTK_IS_MENU_SHELL (submenu))
+ {
+ connect_menu_shell (tepl_menu_shell, GTK_MENU_SHELL (submenu));
+ }
+
+ g_signal_emit (tepl_menu_shell,
+ signals[SIGNAL_MENU_ITEM_SELECTED], 0,
+ menu_item);
+}
+
+static void
+menu_item_deselect_cb (GtkMenuItem *menu_item,
+ gpointer user_data)
+{
+ TeplMenuShell *tepl_menu_shell = TEPL_MENU_SHELL (user_data);
+ GtkWidget *submenu;
+
+ submenu = gtk_menu_item_get_submenu (menu_item);
+
+ if (GTK_IS_MENU_SHELL (submenu))
+ {
+ disconnect_menu_shell (tepl_menu_shell, GTK_MENU_SHELL (submenu));
+ }
+
+ g_signal_emit (tepl_menu_shell,
+ signals[SIGNAL_MENU_ITEM_DESELECTED], 0,
+ menu_item);
+}
+
+static void
+connect_menu_item (TeplMenuShell *tepl_menu_shell,
+ GtkMenuItem *menu_item)
+{
+ g_signal_connect_object (menu_item,
+ "select",
+ G_CALLBACK (menu_item_select_cb),
+ tepl_menu_shell,
+ 0);
+
+ g_signal_connect_object (menu_item,
+ "deselect",
+ G_CALLBACK (menu_item_deselect_cb),
+ tepl_menu_shell,
+ 0);
+}
+
+static void
+disconnect_menu_item (TeplMenuShell *tepl_menu_shell,
+ GtkMenuItem *menu_item)
+{
+ g_signal_handlers_disconnect_by_func (menu_item,
+ menu_item_select_cb,
+ tepl_menu_shell);
+
+ g_signal_handlers_disconnect_by_func (menu_item,
+ menu_item_deselect_cb,
+ tepl_menu_shell);
+}
+
+static void
+insert_cb (GtkMenuShell *gtk_menu_shell,
+ GtkWidget *child,
+ gint position,
+ gpointer user_data)
+{
+ TeplMenuShell *tepl_menu_shell = TEPL_MENU_SHELL (user_data);
+
+ if (GTK_IS_MENU_ITEM (child))
+ {
+ connect_menu_item (tepl_menu_shell, GTK_MENU_ITEM (child));
+ }
+}
+
+static void
+remove_cb (GtkContainer *container,
+ GtkWidget *child,
+ gpointer user_data)
+{
+ TeplMenuShell *tepl_menu_shell = TEPL_MENU_SHELL (user_data);
+
+ if (GTK_IS_MENU_ITEM (child))
+ {
+ disconnect_menu_item (tepl_menu_shell, GTK_MENU_ITEM (child));
+ }
+}
+
+static void
+connect_menu_shell (TeplMenuShell *tepl_menu_shell,
+ GtkMenuShell *gtk_menu_shell)
+{
+ GList *children;
+ GList *l;
+
+ children = gtk_container_get_children (GTK_CONTAINER (gtk_menu_shell));
+
+ for (l = children; l != NULL; l = l->next)
+ {
+ GtkMenuItem *menu_item = l->data;
+
+ if (GTK_IS_MENU_ITEM (menu_item))
+ {
+ connect_menu_item (tepl_menu_shell, menu_item);
+ }
+ }
+
+ g_list_free (children);
+
+ g_signal_connect_object (gtk_menu_shell,
+ "insert",
+ G_CALLBACK (insert_cb),
+ tepl_menu_shell,
+ 0);
+
+ g_signal_connect_object (gtk_menu_shell,
+ "remove",
+ G_CALLBACK (remove_cb),
+ tepl_menu_shell,
+ 0);
+}
+
+static void
+disconnect_menu_shell (TeplMenuShell *tepl_menu_shell,
+ GtkMenuShell *gtk_menu_shell)
+{
+ GList *children;
+ GList *l;
+
+ children = gtk_container_get_children (GTK_CONTAINER (gtk_menu_shell));
+
+ for (l = children; l != NULL; l = l->next)
+ {
+ GtkMenuItem *menu_item = l->data;
+
+ if (GTK_IS_MENU_ITEM (menu_item))
+ {
+ disconnect_menu_item (tepl_menu_shell, menu_item);
+ }
+ }
+
+ g_list_free (children);
+
+ g_signal_handlers_disconnect_by_func (gtk_menu_shell,
+ insert_cb,
+ tepl_menu_shell);
+
+ g_signal_handlers_disconnect_by_func (gtk_menu_shell,
+ remove_cb,
+ tepl_menu_shell);
+}
+
+static void
+set_menu_shell (TeplMenuShell *tepl_menu_shell,
+ GtkMenuShell *gtk_menu_shell)
+{
+ g_assert (tepl_menu_shell->priv->gtk_menu_shell == NULL);
+ g_return_if_fail (GTK_IS_MENU_SHELL (gtk_menu_shell));
+
+ tepl_menu_shell->priv->gtk_menu_shell = gtk_menu_shell;
+ connect_menu_shell (tepl_menu_shell, gtk_menu_shell);
+}
+
+static void
+tepl_menu_shell_get_property (GObject *object,
+ guint prop_id,
+ GValue *value,
+ GParamSpec *pspec)
+{
+ TeplMenuShell *tepl_menu_shell = TEPL_MENU_SHELL (object);
+
+ switch (prop_id)
+ {
+ case PROP_MENU_SHELL:
+ g_value_set_object (value, tepl_menu_shell_get_menu_shell (tepl_menu_shell));
+ break;
+
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+ break;
+ }
+}
+
+static void
+tepl_menu_shell_set_property (GObject *object,
+ guint prop_id,
+ const GValue *value,
+ GParamSpec *pspec)
+{
+ TeplMenuShell *tepl_menu_shell = TEPL_MENU_SHELL (object);
+
+ switch (prop_id)
+ {
+ case PROP_MENU_SHELL:
+ set_menu_shell (tepl_menu_shell, g_value_get_object (value));
+ break;
+
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+ break;
+ }
+}
+
+static void
+tepl_menu_shell_dispose (GObject *object)
+{
+ TeplMenuShell *tepl_menu_shell = TEPL_MENU_SHELL (object);
+
+ tepl_menu_shell->priv->gtk_menu_shell = NULL;
+
+ G_OBJECT_CLASS (tepl_menu_shell_parent_class)->dispose (object);
+}
+
+static void
+tepl_menu_shell_class_init (TeplMenuShellClass *klass)
+{
+ GObjectClass *object_class = G_OBJECT_CLASS (klass);
+
+ object_class->get_property = tepl_menu_shell_get_property;
+ object_class->set_property = tepl_menu_shell_set_property;
+ object_class->dispose = tepl_menu_shell_dispose;
+
+ /**
+ * TeplMenuShell:menu-shell:
+ *
+ * The #GtkMenuShell.
+ *
+ * Since: 2.0
+ */
+ properties[PROP_MENU_SHELL] =
+ g_param_spec_object ("menu-shell",
+ "GtkMenuShell",
+ "",
+ GTK_TYPE_MENU_SHELL,
+ G_PARAM_READWRITE |
+ G_PARAM_CONSTRUCT_ONLY |
+ G_PARAM_STATIC_STRINGS);
+
+ g_object_class_install_properties (object_class, N_PROPERTIES, properties);
+
+ /**
+ * TeplMenuShell::menu-item-selected:
+ * @tepl_menu_shell: the #TeplMenuShell emitting the signal.
+ * @menu_item: the #GtkMenuItem that has been selected.
+ *
+ * The ::menu-item-selected signal is emitted when the
+ * #GtkMenuItem::select signal is emitted on a #GtkMenuItem belonging
+ * (directly or indirectly through submenus) to @tepl_menu_shell.
+ *
+ * Since: 2.0
+ */
+ signals[SIGNAL_MENU_ITEM_SELECTED] =
+ g_signal_new ("menu-item-selected",
+ G_TYPE_FROM_CLASS (klass),
+ G_SIGNAL_RUN_FIRST,
+ G_STRUCT_OFFSET (TeplMenuShellClass, menu_item_selected),
+ NULL, NULL, NULL,
+ G_TYPE_NONE,
+ 1, GTK_TYPE_MENU_ITEM);
+
+ /**
+ * TeplMenuShell::menu-item-deselected:
+ * @tepl_menu_shell: the #TeplMenuShell emitting the signal.
+ * @menu_item: the #GtkMenuItem that has been deselected.
+ *
+ * The ::menu-item-deselected signal is emitted when the
+ * #GtkMenuItem::deselect signal is emitted on a #GtkMenuItem belonging
+ * (directly or indirectly through submenus) to @tepl_menu_shell.
+ *
+ * Since: 2.0
+ */
+ signals[SIGNAL_MENU_ITEM_DESELECTED] =
+ g_signal_new ("menu-item-deselected",
+ G_TYPE_FROM_CLASS (klass),
+ G_SIGNAL_RUN_FIRST,
+ G_STRUCT_OFFSET (TeplMenuShellClass, menu_item_deselected),
+ NULL, NULL, NULL,
+ G_TYPE_NONE,
+ 1, GTK_TYPE_MENU_ITEM);
+}
+
+static void
+tepl_menu_shell_init (TeplMenuShell *tepl_menu_shell)
+{
+ tepl_menu_shell->priv = tepl_menu_shell_get_instance_private (tepl_menu_shell);
+}
+
+/**
+ * tepl_menu_shell_get_from_gtk_menu_shell:
+ * @gtk_menu_shell: a #GtkMenuShell.
+ *
+ * Returns the #TeplMenuShell of @gtk_menu_shell. The returned object is
+ * guaranteed to be the same for the lifetime of @gtk_menu_shell.
+ *
+ * Returns: (transfer none): the #TeplMenuShell of @gtk_menu_shell.
+ * Since: 2.0
+ */
+TeplMenuShell *
+tepl_menu_shell_get_from_gtk_menu_shell (GtkMenuShell *gtk_menu_shell)
+{
+ TeplMenuShell *tepl_menu_shell;
+
+ g_return_val_if_fail (GTK_IS_MENU_SHELL (gtk_menu_shell), NULL);
+
+ tepl_menu_shell = g_object_get_data (G_OBJECT (gtk_menu_shell), TEPL_MENU_SHELL_KEY);
+
+ if (tepl_menu_shell == NULL)
+ {
+ tepl_menu_shell = g_object_new (TEPL_TYPE_MENU_SHELL,
+ "menu-shell", gtk_menu_shell,
+ NULL);
+
+ g_object_set_data_full (G_OBJECT (gtk_menu_shell),
+ TEPL_MENU_SHELL_KEY,
+ tepl_menu_shell,
+ g_object_unref);
+ }
+
+ g_return_val_if_fail (TEPL_IS_MENU_SHELL (tepl_menu_shell), NULL);
+ return tepl_menu_shell;
+}
+
+/**
+ * tepl_menu_shell_get_menu_shell:
+ * @tepl_menu_shell: a #TeplMenuShell.
+ *
+ * Returns: (transfer none): the #GtkMenuShell of @tepl_menu_shell.
+ * Since: 2.0
+ */
+GtkMenuShell *
+tepl_menu_shell_get_menu_shell (TeplMenuShell *tepl_menu_shell)
+{
+ g_return_val_if_fail (TEPL_IS_MENU_SHELL (tepl_menu_shell), NULL);
+
+ return tepl_menu_shell->priv->gtk_menu_shell;
+}
+
+/* ex:set ts=8 noet: */
diff --git a/amtk/tepl-menu-shell.h b/amtk/tepl-menu-shell.h
new file mode 100644
index 0000000..6059c9e
--- /dev/null
+++ b/amtk/tepl-menu-shell.h
@@ -0,0 +1,74 @@
+/*
+ * This file is part of Tepl, a text editor library.
+ *
+ * Copyright 2017 - Sébastien Wilmet <swilmet gnome org>
+ *
+ * Tepl 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.
+ *
+ * Tepl 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 library; if not, see <http://www.gnu.org/licenses/>.
+ */
+
+#ifndef TEPL_MENU_SHELL_H
+#define TEPL_MENU_SHELL_H
+
+#if !defined (TEPL_H_INSIDE) && !defined (TEPL_COMPILATION)
+#error "Only <tepl/tepl.h> can be included directly."
+#endif
+
+#include <gtk/gtk.h>
+#include <tepl/tepl-types.h>
+
+G_BEGIN_DECLS
+
+#define TEPL_TYPE_MENU_SHELL (tepl_menu_shell_get_type ())
+#define TEPL_MENU_SHELL(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), TEPL_TYPE_MENU_SHELL,
TeplMenuShell))
+#define TEPL_MENU_SHELL_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), TEPL_TYPE_MENU_SHELL,
TeplMenuShellClass))
+#define TEPL_IS_MENU_SHELL(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), TEPL_TYPE_MENU_SHELL))
+#define TEPL_IS_MENU_SHELL_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), TEPL_TYPE_MENU_SHELL))
+#define TEPL_MENU_SHELL_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), TEPL_TYPE_MENU_SHELL,
TeplMenuShellClass))
+
+typedef struct _TeplMenuShellClass TeplMenuShellClass;
+typedef struct _TeplMenuShellPrivate TeplMenuShellPrivate;
+
+struct _TeplMenuShell
+{
+ GObject parent;
+
+ TeplMenuShellPrivate *priv;
+};
+
+struct _TeplMenuShellClass
+{
+ GObjectClass parent_class;
+
+ /* Signals */
+
+ void (* menu_item_selected) (TeplMenuShell *tepl_menu_shell,
+ GtkMenuItem *menu_item);
+
+ void (* menu_item_deselected) (TeplMenuShell *tepl_menu_shell,
+ GtkMenuItem *menu_item);
+
+ gpointer padding[12];
+};
+
+GType tepl_menu_shell_get_type (void) G_GNUC_CONST;
+
+TeplMenuShell * tepl_menu_shell_get_from_gtk_menu_shell (GtkMenuShell *gtk_menu_shell);
+
+GtkMenuShell * tepl_menu_shell_get_menu_shell (TeplMenuShell *tepl_menu_shell);
+
+G_END_DECLS
+
+#endif /* TEPL_MENU_SHELL_H */
+
+/* ex:set ts=8 noet: */
diff --git a/amtk/tepl-types.h b/amtk/tepl-types.h
new file mode 100644
index 0000000..e9ce253
--- /dev/null
+++ b/amtk/tepl-types.h
@@ -0,0 +1,39 @@
+/*
+ * This file is part of Tepl, a text editor library.
+ *
+ * Copyright 2017 - Sébastien Wilmet <swilmet gnome org>
+ *
+ * Tepl 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.
+ *
+ * Tepl 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 library; if not, see <http://www.gnu.org/licenses/>.
+ */
+
+#ifndef TEPL_TYPES_H
+#define TEPL_TYPES_H
+
+#if !defined (TEPL_H_INSIDE) && !defined (TEPL_COMPILATION)
+#error "Only <tepl/tepl.h> can be included directly."
+#endif
+
+#include <glib.h>
+
+G_BEGIN_DECLS
+
+typedef struct _TeplActionInfo TeplActionInfo;
+typedef struct _TeplActionInfoEntry TeplActionInfoEntry;
+typedef struct _TeplActionInfoStore TeplActionInfoStore;
+typedef struct _TeplActionInfoCentralStore TeplActionInfoCentralStore;
+typedef struct _TeplMenuShell TeplMenuShell;
+
+G_END_DECLS
+
+#endif /* TEPL_TYPES_H */
diff --git a/amtk/tepl.h b/amtk/tepl.h
new file mode 100644
index 0000000..0f0f0d7
--- /dev/null
+++ b/amtk/tepl.h
@@ -0,0 +1,36 @@
+/*
+ * This file is part of Tepl, a text editor library.
+ *
+ * Copyright 2017 - Sébastien Wilmet <swilmet gnome org>
+ *
+ * Tepl 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.
+ *
+ * Tepl 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 library; if not, see <http://www.gnu.org/licenses/>.
+ */
+
+#ifndef TEPL_H
+#define TEPL_H
+
+#define TEPL_H_INSIDE
+
+#include <tepl/tepl-types.h>
+
+#include <tepl/tepl-action-info.h>
+#include <tepl/tepl-action-info-store.h>
+#include <tepl/tepl-action-info-central-store.h>
+#include <tepl/tepl-action-map.h>
+#include <tepl/tepl-menu-item.h>
+#include <tepl/tepl-menu-shell.h>
+
+#undef TEPL_H_INSIDE
+
+#endif /* TEPL_H */
[
Date Prev][
Date Next] [
Thread Prev][
Thread Next]
[
Thread Index]
[
Date Index]
[
Author Index]