/* dzl-action-group.h * * Copyright (C) 2017 Christian Hergert * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 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 General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ #ifndef DZL_ACTION_GROUP_H #define DZL_ACTION_GROUP_H #include G_BEGIN_DECLS #define DZL_DEFINE_ACTION_GROUP(Type, prefix, ...) \ struct _##Type##ActionEntry { \ const gchar *name; \ void (*activate) (Type *self, GVariant *param); \ const gchar *parameter_type; \ const gchar *state; \ void (*change_state) (Type *self, GVariant *state); \ } prefix##_actions[] = __VA_ARGS__; \ \ typedef struct { \ GVariant *state; \ GVariant *state_hint; \ guint enabled : 1; \ } Type##ActionInfo; \ \ static gboolean \ _##prefix##_has_action (GActionGroup *group, \ const gchar *name) \ { \ for (guint i = 0; i < G_N_ELEMENTS(prefix##_actions); i++) \ { \ if (g_strcmp0 (name, prefix##_actions[i].name) == 0) \ return TRUE; \ } \ return FALSE; \ } \ \ static gchar ** \ _##prefix##_list_actions (GActionGroup *group) \ { \ GPtrArray *ar = g_ptr_array_new (); \ \ for (guint i = 0; i < G_N_ELEMENTS(prefix##_actions); i++) \ g_ptr_array_add (ar, g_strdup (prefix##_actions[i].name)); \ g_ptr_array_add (ar, NULL); \ \ return (gchar **)g_ptr_array_free (ar, FALSE); \ } \ \ static void \ _##prefix##_action_info_free (gpointer data) \ { \ Type##ActionInfo *info = data; \ g_clear_pointer (&info->state, g_variant_unref); \ g_clear_pointer (&info->state_hint, g_variant_unref); \ g_slice_free (Type##ActionInfo, info); \ } \ \ static Type##ActionInfo * \ _##prefix##_get_action_info (GActionGroup *group, \ const gchar *name) \ { \ g_autofree gchar *fullname = g_strdup_printf ("ACTION-INFO:%s", name); \ Type##ActionInfo *info = g_object_get_data (G_OBJECT (group), fullname); \ if (info == NULL) \ { \ info = g_slice_new0 (Type##ActionInfo); \ info->enabled = TRUE; \ for (guint i = 0; i < G_N_ELEMENTS(prefix##_actions); i++) \ { \ if (g_strcmp0 (prefix##_actions[i].name, name) == 0) \ { \ if (prefix##_actions[i].state != NULL) \ info->state = g_variant_parse ( \ NULL, prefix##_actions[i].state, NULL, NULL, NULL); \ break; \ } \ } \ g_object_set_data_full (G_OBJECT (group), fullname, info, \ _##prefix##_action_info_free); \ } \ return info; \ } \ \ static inline void \ prefix##_set_action_state (Type *self, \ const gchar *name, \ GVariant *state) \ { \ Type##ActionInfo *info = _##prefix##_get_action_info (G_ACTION_GROUP (self), \ name); \ if (state != info->state) \ { \ g_clear_pointer (&info->state, g_variant_unref); \ info->state = state ? g_variant_ref_sink (state) : NULL; \ g_action_group_action_state_changed (G_ACTION_GROUP (self), name, state); \ } \ } \ \ static inline void \ prefix##_set_action_enabled (Type *self, \ const gchar *name, \ gboolean enabled) \ { \ Type##ActionInfo *info = _##prefix##_get_action_info (G_ACTION_GROUP (self), \ name); \ if (enabled != info->enabled) \ { \ info->enabled = !!enabled; \ g_action_group_action_enabled_changed (G_ACTION_GROUP (self), \ name, enabled); \ } \ } \ \ static void \ _##prefix##_change_action_state (GActionGroup *group, \ const gchar *name, \ GVariant *state) \ { \ for (guint i = 0; i < G_N_ELEMENTS(prefix##_actions); i++) \ { \ if (g_strcmp0 (name, prefix##_actions[i].name) == 0) \ { \ if (prefix##_actions[i].change_state) \ prefix##_actions[i].change_state ((Type*)group, state); \ return; \ } \ } \ } \ \ static void \ _##prefix##_activate_action (GActionGroup *group, \ const gchar *name, \ GVariant *param) \ { \ for (guint i = 0; i < G_N_ELEMENTS(prefix##_actions); i++) \ { \ if (g_strcmp0 (name, prefix##_actions[i].name) == 0) \ { \ if (prefix##_actions[i].activate) \ prefix##_actions[i].activate ((Type*)group, param); \ return; \ } \ } \ } \ \ static gboolean \ _##prefix##_query_action (GActionGroup *group, \ const gchar *name, \ gboolean *enabled, \ const GVariantType **parameter_type, \ const GVariantType **state_type, \ GVariant **state_hint, \ GVariant **state) \ { \ for (guint i = 0; i < G_N_ELEMENTS(prefix##_actions); i++) \ { \ if (g_strcmp0 (name, prefix##_actions[i].name) == 0) \ { \ Type##ActionInfo *info = _##prefix##_get_action_info(group, name); \ if (prefix##_actions[i].change_state && state_type) \ *state_type = prefix##_actions[i].parameter_type ? \ G_VARIANT_TYPE(prefix##_actions[i].parameter_type) : \ NULL; \ else if (prefix##_actions[i].activate && parameter_type) \ *parameter_type = prefix##_actions[i].parameter_type ? \ G_VARIANT_TYPE(prefix##_actions[i].parameter_type) :\ NULL; \ if (state_hint) \ *state_hint = info->state_hint != NULL ? \ g_variant_ref (info->state_hint) : NULL; \ if (state) \ *state = info->state != NULL ? \ g_variant_ref (info->state) : NULL; \ if (enabled) \ *enabled = info->enabled; \ return TRUE; \ } \ } \ return FALSE; \ } \ \ static void \ prefix##_init_action_group (GActionGroupInterface *iface) \ { \ iface->has_action = _##prefix##_has_action; \ iface->list_actions = _##prefix##_list_actions; \ iface->change_action_state = _##prefix##_change_action_state; \ iface->activate_action = _##prefix##_activate_action; \ iface->query_action = _##prefix##_query_action; \ } G_END_DECLS #endif /* DZL_ACTION_GROUP_H */