diff --git a/src/plugin/applet.c b/src/plugin/applet.c index e600ba1..a2c92fe 100644 --- a/src/plugin/applet.c +++ b/src/plugin/applet.c @@ -1,476 +1,476 @@ /* * This file is part of budgie-desktop * * Copyright (C) 2015-2016 Ikey Doherty * * This library 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. */ #include "applet.h" #include "budgie-enums.h" /** * SECTION:applet * @Short_description: Budgie Panel GTK+ Widget * @Title: BudgieApplet * * The BudgieApplet is the main event when it comes to providing a Budgie * Panel extension. This is the widget that is visible to the user, and is * provided by, and instaniated by, your #BudgiePlugin. * * Those implementing applets have a specific API available to them in order * to achieve better integration with the overall desktop. At the bare minimum * you should at least ensure that your applet respects the sizes exposed by * the managing panel, via the #BudgieApplet::panel-size-changed signal. * * BudgieApplet extends #GtkEventBox to leave you free to make your own choices * on internal applet layout and configuration. Do note, however, that the * panel implementation will not call #gtk_widget_show_all, it is solely * your responsibility to ensure all of your contents are displayed. This * is to enable applet's to contextually hide part of their user interface * when required. * */ enum { PROP_PREFIX = 1, PROP_SCHEMA, PROP_ACTIONS, N_PROPS }; enum { POSITION_CHANGED = 0, N_SIGNALS }; struct _BudgieAppletPrivate { char *prefix; char *schema; BudgiePanelAction actions; }; G_DEFINE_TYPE_WITH_PRIVATE(BudgieApplet, budgie_applet, GTK_TYPE_EVENT_BOX) static GParamSpec *obj_properties[N_PROPS] = { NULL, }; static guint applet_signals[N_SIGNALS] = { 0 }; #define FREE_IF_SET(x) \ { \ if (self->priv->x) { \ g_free(self->priv->x); \ self->priv->x = NULL; \ } \ } static void budgie_applet_set_property(GObject *object, guint id, const GValue *value, GParamSpec *spec) { BudgieApplet *self = BUDGIE_APPLET(object); switch (id) { case PROP_PREFIX: budgie_applet_set_settings_prefix(self, g_value_get_string(value)); break; case PROP_SCHEMA: budgie_applet_set_settings_schema(self, g_value_get_string(value)); break; case PROP_ACTIONS: self->priv->actions = g_value_get_flags(value); break; default: G_OBJECT_WARN_INVALID_PROPERTY_ID(object, id, spec); break; } } static void budgie_applet_get_property(GObject *object, guint id, GValue *value, GParamSpec *spec) { BudgieApplet *self = BUDGIE_APPLET(object); switch (id) { case PROP_PREFIX: g_value_set_string(value, budgie_applet_get_settings_prefix(self)); break; case PROP_SCHEMA: g_value_set_string(value, budgie_applet_get_settings_schema(self)); break; case PROP_ACTIONS: g_value_set_flags(value, self->priv->actions); break; default: G_OBJECT_WARN_INVALID_PROPERTY_ID(object, id, spec); break; } } /** * budgie_applet_invoke_action: * @action: Action to invoke * * Invoke the given action on this applet. This action will only be one * that has been declared in supported actions bitmask. * * To allow better integration between the Budgie Desktop, and the applets * that live within it, the panel will relay actions to applets that have * set their #BudgieApplet:supported-actions to a matching bitmask. * * For example, if we wish to listen for Menu Key events, we can simply do * the following in C: * * |[ * * static void my_applet_invoke_action(BudgieApplet *applet, BudgiePanelAction action) * { * if (action == BUDGIE_PANEL_ACTION_MENU) { * my_applet_do_predict_the_lottery(MY_APPLET(applet)); * } * } * * static void my_class_init(GObjectClass *class) * { * MyClass *mc = MY_CLASS(klass); * .. * mc->invoke_action = my_applet_invoke_action; * } * ]| * * Likewise, a Vala implementation might look like the following: * |[ * * public override void invoke_action(Budgie.PanelAction action) * { * if (action == Budgie.PanelAction.MENU) { * this.predict_the_lottery(); * } * } * ]| */ void budgie_applet_invoke_action(BudgieApplet *self, BudgiePanelAction action) { BudgieAppletClass *klazz = NULL; if (!BUDGIE_IS_APPLET(self)) { return; } klazz = BUDGIE_APPLET_GET_CLASS(self); if (klazz->invoke_action) { klazz->invoke_action(self, action); } } /** * budgie_applet_supports_settings: * * Implementations should override this to return TRUE if they support * a settings UI * * Returns: true if this implementation supports a Settings UI */ gboolean budgie_applet_supports_settings(BudgieApplet *self) { BudgieAppletClass *klazz = NULL; if (!BUDGIE_IS_APPLET(self)) { return FALSE; } klazz = BUDGIE_APPLET_GET_CLASS(self); if (!klazz->supports_settings) { return FALSE; } return klazz->supports_settings(self); } /** * budgie_applet_get_settings_ui: * * For applets that need to expose settings, they should both override the * #BudgieApplet::supports_settings method and return a new widget instance * whenever this function is invoked. * * This UI will live in the Raven sidebar within the Budgie Desktop, and * will be destroyed as soon as it's not being used. It's advisable to keep * this widget implementation light, and to prefer vertical space. * * Returns: (transfer full) (nullable): A GTK Settings UI */ GtkWidget *budgie_applet_get_settings_ui(BudgieApplet *self) { BudgieAppletClass *klazz = NULL; if (!BUDGIE_IS_APPLET(self)) { return NULL; } klazz = BUDGIE_APPLET_GET_CLASS(self); if (!klazz->get_settings_ui) { return NULL; } return klazz->get_settings_ui(self); } /** * budgie_applet_get_applet_settings: * @uuid: UUID for this instance * * If your #BudgiePlugin implementation passes the UUID to your BudgieApplet * implementation on construction, you can take advantage of per-instance * settings. * * For most applets, global GSettings keys are more than suffice. However, * in some situations, it may be beneficial to enable multiple unique instances * of your applet, each with their own configuration. * * To facilitate this, use this function to create a new relocatable settings * instance using your UUID. Make sure you set the #BudgieApplet:settings-schema * and #BudgieApplet:settings-prefix properties first. * * Returns: (transfer full): A newly created #GSettings for this applet instance */ GSettings *budgie_applet_get_applet_settings(BudgieApplet *self, gchar *uuid) { GSettings *settings = NULL; gchar *path = NULL; - if (!self->priv->schema || !self->priv->prefix) { + if (!self || !self->priv || !self->priv->schema || !self->priv->prefix) { return NULL; } path = g_strdup_printf("%s/{%s}/", self->priv->prefix, uuid); if (!path) { return NULL; } settings = g_settings_new_with_path(self->priv->schema, path); g_free(path); return settings; } static void budgie_applet_dispose(GObject *g_object) { BudgieApplet *self = BUDGIE_APPLET(g_object); FREE_IF_SET(prefix); FREE_IF_SET(schema); G_OBJECT_CLASS(budgie_applet_parent_class)->dispose(g_object); } static void budgie_applet_class_init(BudgieAppletClass *klazz) { GObjectClass *obj_class = G_OBJECT_CLASS(klazz); obj_class->get_property = budgie_applet_get_property; obj_class->set_property = budgie_applet_set_property; obj_class->dispose = budgie_applet_dispose; klazz->update_popovers = NULL; /* Todo, make the PREFIX/SCHEMA G_PARAM_CONSTRUCT_ONLY */ /** * BudgieApplet:settings-prefix: * * The GSettings schema path prefix for this applet * * For applets that require unique instance configuration, the * panel management must know where to initialise the settings * within the tree. The path takes the form: * * `$SETTINGS_PREFIX/{$UUID}` * * As an example, the Budgie Menu Applet set's the `settings-prefix` * to: * `/com/solus-project/budgie-panel/instance/budgie-menu`. * * This results in relocatable schemas being created at: * * `/com/solus-project/budgie-panel/instance/budgie-menu/{$UUID}` */ obj_properties[PROP_PREFIX] = g_param_spec_string("settings-prefix", "GSettings schema prefix", "Set the GSettings schema prefix", NULL, G_PARAM_READWRITE); /** * BudgieApplet:settings-schema: * * The ID of the GSettings schema used by this applet * * This only takes effect when you've also set #BudgieApplet:settings-prefix, * and is used by the panel managemen to both initialise and delete your per-instance * settings, respectively. * * As an example, the Budgie Menu Applet uses the schema: * * `com.solus-project.budgie-menu` * * as defined by the accompanying gschema XML file. Providing an incorrect * schema ID is considered programmer error. */ obj_properties[PROP_SCHEMA] = g_param_spec_string("settings-schema", "GSettings relocatable schema ID", "Set the GSettings relocatable schema ID", NULL, G_PARAM_READWRITE); /** * BudgieApplet:supported-actions: * * The actions supported by this applet instance */ obj_properties[PROP_ACTIONS] = g_param_spec_flags("supported-actions", "Supported panel actions", "Get/set the supported panel actions", BUDGIE_TYPE_PANEL_ACTION, BUDGIE_PANEL_ACTION_NONE, G_PARAM_READWRITE); /** * BudgieApplet::panel-size-changed: * @applet: The applet receiving the signal * @panel_size: The new panel size * @icon_size: Larget possible icon size for the panel * @small_icon_size: Smaller icon that will still fit on the panel * * Used to notify this applet of a change in the panel size */ applet_signals[POSITION_CHANGED] = g_signal_new("panel-size-changed", BUDGIE_TYPE_APPLET, G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION, G_STRUCT_OFFSET(BudgieAppletClass, panel_size_changed), NULL, NULL, NULL, G_TYPE_NONE, 3, G_TYPE_INT, G_TYPE_INT, G_TYPE_INT); g_object_class_install_properties(obj_class, N_PROPS, obj_properties); } /** * budgie_applet_set_settings_prefix: * * Utility function for Python usage. See: #BudgieApplet:settings-prefix */ void budgie_applet_set_settings_prefix(BudgieApplet *self, const gchar *prefix) { if (!self || !prefix) { return; } BudgieAppletPrivate *priv = self->priv; if (priv->prefix) { g_free(priv->prefix); } priv->prefix = g_strdup(prefix); } /** * budgie_applet_get_settings_prefix: * * Utility function for Python usage. See: #BudgieApplet:settings-prefix */ const gchar *budgie_applet_get_settings_prefix(BudgieApplet *self) { if (!self) { return NULL; } return (const gchar *)self->priv->prefix; } /** * budgie_applet_set_settings_schema: * * Utility function for Python usage. See #BudgieApplet:settings-schema */ void budgie_applet_set_settings_schema(BudgieApplet *self, const gchar *schema) { if (!self || !schema) { return; } BudgieAppletPrivate *priv = self->priv; ; if (priv->schema) { g_free(priv->schema); } priv->schema = g_strdup(schema); } /** * budgie_applet_get_settings_schema: * * Utility function for Python usage. See #BudgieApplet:settings-schema */ const gchar *budgie_applet_get_settings_schema(BudgieApplet *self) { if (!self) { return NULL; } return (const gchar *)self->priv->schema; } /** * budgie_applet_update_popovers: * @manager: (nullable) * * This virtual method should be implemented by panel applets that wish * to support #GtkPopover's natively. As each Budgie Panel may house multiple * GtkPopover widgets, each one must be registered with the @manager. * * During this call, it is safe to store a reference to the @manager. In * this call you should invoke #BudgiePopoverManager::register_popover to * register your popover with the panel manager. * * Each registered popover joins the global menu system of popovers in the * panel. It is a requirement to register, otherwise the panel will not * know when to expand and collapse the main panel harness to accommodate * the GtkPopover. * */ void budgie_applet_update_popovers(BudgieApplet *self, BudgiePopoverManager *manager) { if (!self) { return; } BudgieAppletClass *klazz = BUDGIE_APPLET_GET_CLASS(self); if (klazz->update_popovers) { klazz->update_popovers(self, manager); } } /** * budgie_applet_get_supported_actions: * * Utility function for Python bindings. See #BudgieApplet:supported-actions */ BudgiePanelAction budgie_applet_get_supported_actions(BudgieApplet *self) { if (!self) { return BUDGIE_PANEL_ACTION_NONE; } return self->priv->actions; } static void budgie_applet_init(BudgieApplet *self) { self->priv = budgie_applet_get_instance_private(self); gtk_widget_set_can_focus(GTK_WIDGET(self), FALSE); } /** * budgie_applet_new: * * Returns: (transfer full): A new BudgieApplet */ BudgieApplet *budgie_applet_new() { return g_object_new(BUDGIE_TYPE_APPLET, NULL); }