/*
* Copyright 2014 Canonical Ltd.
* Copyright 2022-2023 Robert Tari
*
* This program is free software: you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 3, as published
* by the Free Software Foundation.
*
* This program is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranties of
* MERCHANTABILITY, SATISFACTORY QUALITY, 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 .
*
* Authors:
* Charles Kerr
* Robert Tari
*/
#include
class Exporter::Impl
{
public:
explicit Impl(const std::shared_ptr& indicator):
m_indicator(indicator)
{
auto bus_name = g_strdup_printf("org.ayatana.indicator.%s", indicator->name());
m_own_id = g_bus_own_name(G_BUS_TYPE_SESSION,
bus_name,
G_BUS_NAME_OWNER_FLAGS_NONE,
on_bus_acquired,
nullptr,
on_name_lost,
this,
nullptr);
g_free(bus_name);
}
~Impl()
{
if (m_bus != nullptr)
{
for(const auto& id : m_exported_menu_ids)
g_dbus_connection_unexport_menu_model(m_bus, id);
if (m_exported_actions_id)
g_dbus_connection_unexport_action_group(m_bus, m_exported_actions_id);
}
if (m_own_id)
g_bus_unown_name(m_own_id);
g_clear_object(&m_bus);
}
core::Signal& name_lost()
{
return m_name_lost;
}
private:
void emit_name_lost(const char* bus_name)
{
m_name_lost(bus_name);
}
static void on_bus_acquired(GDBusConnection * connection,
const gchar * name,
gpointer gself)
{
static_cast(gself)->on_bus_acquired(connection, name);
}
void on_bus_acquired(GDBusConnection* connection, const gchar* /*name*/)
{
m_bus = G_DBUS_CONNECTION(g_object_ref(G_OBJECT(connection)));
export_actions(m_indicator);
for (const auto& profile : m_indicator->profiles())
export_profile(m_indicator, profile);
}
void export_actions(const std::shared_ptr& indicator)
{
GError* error;
char* object_path;
guint id;
// export the actions
error = nullptr;
object_path = g_strdup_printf("/org/ayatana/indicator/%s", indicator->name());
id = g_dbus_connection_export_action_group(m_bus,
object_path,
G_ACTION_GROUP(indicator->action_group()),
&error);
if (id)
m_exported_actions_id = id;
else
g_warning("Can't export action group to '%s': %s", object_path, error->message);
g_clear_error(&error);
g_free(object_path);
}
static GVariant* create_header_state(const Header& h)
{
GVariantBuilder b;
g_variant_builder_init(&b, G_VARIANT_TYPE_VARDICT);
g_variant_builder_add(&b, "{sv}", "visible", g_variant_new_boolean(h.is_visible));
if (!h.title.empty())
g_variant_builder_add(&b, "{sv}", "title", g_variant_new_string(h.title.c_str()));
if (!h.label.empty())
g_variant_builder_add(&b, "{sv}", "label", g_variant_new_string(h.label.c_str()));
if (!h.tooltip.empty())
g_variant_builder_add(&b, "{sv}", "tooltip", g_variant_new_string(h.tooltip.c_str()));
if (!h.title.empty() || !h.label.empty())
g_variant_builder_add(&b, "{sv}", "accessible-desc", g_variant_new_string(!h.label.empty() ? h.label.c_str() : h.title.c_str()));
if (h.icon)
g_variant_builder_add(&b, "{sv}", "icon", g_icon_serialize(h.icon.get()));
return g_variant_builder_end (&b);
}
void export_profile(const std::shared_ptr& indicator,
const std::shared_ptr& profile)
{
// build the header action
auto action_group = indicator->action_group();
std::string action_name = profile->name() + "-header";
auto a = g_simple_action_new_stateful(action_name.c_str(), nullptr, create_header_state(profile->header()));
g_action_map_add_action(G_ACTION_MAP(action_group), G_ACTION(a));
profile->header().changed().connect([action_group,action_name](const Header& header){
auto state = create_header_state(header);
char* tmp = g_variant_print(state, true);
g_debug("header changed; updating action state to '%s'", tmp);
g_action_group_change_action_state(G_ACTION_GROUP(action_group),
action_name.c_str(),
create_header_state(header));
g_free(tmp);
});
// build the header menu
auto detailed_action = g_strdup_printf("indicator.%s", action_name.c_str());
GMenuItem* header = g_menu_item_new(nullptr, detailed_action);
g_menu_item_set_attribute(header, "x-ayatana-type", "s", "org.ayatana.indicator.root");
g_menu_item_set_submenu(header, profile->menu_model().get());
g_free(detailed_action);
// build the menu
auto menu = g_menu_new();
g_menu_append_item(menu, header);
g_object_unref(header);
// export the menu
auto object_path = g_strdup_printf("/org/ayatana/indicator/%s/%s",
indicator->name(),
profile->name().c_str());
GError* error = nullptr;
auto id = g_dbus_connection_export_menu_model(m_bus, object_path, G_MENU_MODEL(menu), &error);
if (id)
m_exported_menu_ids.insert(id);
else if (error != nullptr)
g_warning("cannot export '%s': %s", object_path, error->message);
g_free(object_path);
g_clear_error(&error);
//g_object_unref(menu);
}
static void on_name_lost(GDBusConnection * /*connection*/,
const gchar * name,
gpointer gthis)
{
static_cast(gthis)->emit_name_lost(name);
}
core::Signal m_name_lost;
std::shared_ptr m_indicator;
std::set m_exported_menu_ids;
guint m_own_id = 0;
guint m_exported_actions_id = 0;
GDBusConnection * m_bus = nullptr;
};
/***
****
***/
Exporter::Exporter(const std::shared_ptr& indicator):
impl(new Impl(indicator))
{
}
Exporter::~Exporter()
{
}
core::Signal&
Exporter::name_lost()
{
return impl->name_lost();
}
/***
****
***/