[Xfce4-commits] [apps/xfdashboard] 04/05: Implement pop-up menu
noreply at xfce.org
noreply at xfce.org
Tue Aug 2 09:03:10 CEST 2016
This is an automated email from the git hooks/post-receive script.
nomad pushed a commit to branch master
in repository apps/xfdashboard.
commit 8390f0676e43f5ecae94a100f0e381389d9ca9d7
Author: Stephan Haller <nomad at froevel.de>
Date: Tue Aug 2 09:02:05 2016 +0200
Implement pop-up menu
---
data/themes/xfdashboard/xfdashboard.css | 43 +
libxfdashboard/Makefile.am | 4 +
libxfdashboard/popup-menu-item-meta.c | 591 +++++++++
libxfdashboard/popup-menu-item-meta.h | 95 ++
libxfdashboard/popup-menu.c | 2005 +++++++++++++++++++++++++++++++
libxfdashboard/popup-menu.h | 151 +++
6 files changed, 2889 insertions(+)
diff --git a/data/themes/xfdashboard/xfdashboard.css b/data/themes/xfdashboard/xfdashboard.css
index 48746e3..294a3d8 100644
--- a/data/themes/xfdashboard/xfdashboard.css
+++ b/data/themes/xfdashboard/xfdashboard.css
@@ -537,3 +537,46 @@ XfdashboardSearchView XfdashboardSearchResultContainer XfdashboardButton:selecte
corners: all;
corner-radius: 4.0;
}
+
+/* Popup menus */
+.popup-menu
+{
+ background-type: fill outline rounded-corners;
+ background-fill-color: @alpha(@background-base-color, 0.7);
+ outline-color: #5792e2ff;
+ outline-width: 1.0;
+ corners: all;
+ corner-radius: 4.0;
+}
+
+.popup-menu-title
+{
+ font: bold;
+ background-type: outline;
+ outline-borders: bottom;
+ outline-width: 1.0;
+ outline-color: white;
+}
+
+.popup-menu-item
+{
+ margin-top: 2.0;
+ margin-bottom: 2.0;
+}
+
+.popup-menu-item:selected,
+.popup-menu-item:hover
+{
+ background-type: fill;
+ background-fill-color: #93c1ffff;
+}
+
+.popup-menu-separator
+{
+ background-type: outline;
+ outline-borders: all;
+ outline-width: 1.0;
+ outline-color: white;
+ margin-top: 2.0;
+ margin-bottom: 2.0;
+}
diff --git a/libxfdashboard/Makefile.am b/libxfdashboard/Makefile.am
index f61f9ca..51ad25d 100644
--- a/libxfdashboard/Makefile.am
+++ b/libxfdashboard/Makefile.am
@@ -55,6 +55,8 @@ libxfdashboard_la_headers = \
outline-effect.h \
plugin.h \
plugins-manager.h \
+ popup-menu.h \
+ popup-menu-item-meta.h \
quicklaunch.h \
scaled-table-layout.h \
scrollbar.h \
@@ -122,6 +124,8 @@ libxfdashboard_la_SOURCES = \
outline-effect.c \
plugin.c \
plugins-manager.c \
+ popup-menu.c \
+ popup-menu-item-meta.c \
quicklaunch.c \
scaled-table-layout.c \
scrollbar.c \
diff --git a/libxfdashboard/popup-menu-item-meta.c b/libxfdashboard/popup-menu-item-meta.c
new file mode 100644
index 0000000..eda6e45
--- /dev/null
+++ b/libxfdashboard/popup-menu-item-meta.c
@@ -0,0 +1,591 @@
+/*
+ * popup-menu-item-meta: A meta class for menu items in a pop-up menu
+ *
+ * Copyright 2012-2016 Stephan Haller <nomad at froevel.de>
+ *
+ * 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 2 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, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
+ * MA 02110-1301, USA.
+ *
+ *
+ */
+
+/**
+ * SECTION:popup-menu-item-meta
+ * @short_description: A meta class for menu items in a pop-up menu
+ * @include: xfdashboard/popup-menu-item-meta.h
+ *
+ * A #XfdashboardPopupMenuItemMetaItemMeta will be created to set to each menu item
+ * added to the items container of a #XfdashboardPopupMenu. This meta class handles
+ * the activation of a menu item and calls the callback function with the associated
+ * user data.
+ *
+ * This class should not be used directly.
+ */
+
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+
+#include <libxfdashboard/popup-menu-item-meta.h>
+
+#include <glib/gi18n-lib.h>
+
+#include <libxfdashboard/stylable.h>
+#include <libxfdashboard/click-action.h>
+#include <libxfdashboard/compat.h>
+
+
+/* Define this class in GObject system */
+G_DEFINE_TYPE(XfdashboardPopupMenuItemMeta,
+ xfdashboard_popup_menu_item_meta,
+ G_TYPE_OBJECT);
+
+/* Private structure - access only by public API if needed */
+#define XFDASHBOARD_POPUP_MENU_ITEM_META_GET_PRIVATE(obj) \
+ (G_TYPE_INSTANCE_GET_PRIVATE((obj), XFDASHBOARD_TYPE_POPUP_MENU_ITEM_META, XfdashboardPopupMenuItemMetaPrivate))
+
+struct _XfdashboardPopupMenuItemMetaPrivate
+{
+ /* Properties related */
+ XfdashboardPopupMenu *popupMenu;
+ ClutterActor *menuItem;
+ XfdashboardPopupMenuItemActivateCallback callback;
+ gpointer userData;
+
+ /* Instance related */
+ ClutterAction *clickAction;
+};
+
+/* Properties */
+enum
+{
+ PROP_0,
+
+ PROP_POPUP_MENU,
+ PROP_MENU_ITEM,
+ PROP_CALLBACK,
+ PROP_USER_DATA,
+
+ PROP_LAST
+};
+
+static GParamSpec* XfdashboardPopupMenuItemMetaProperties[PROP_LAST]={ 0, };
+
+/* Signals */
+enum
+{
+ SIGNAL_ACTIVATED,
+
+ SIGNAL_LAST
+};
+
+static guint XfdashboardPopupMenuItemMetaSignals[SIGNAL_LAST]={ 0, };
+
+
+/* IMPLEMENTATION: Private variables and methods */
+
+/* A menu item was clicked */
+static void _xfdashboard_popup_menu_item_meta_clicked(XfdashboardPopupMenuItemMeta *self,
+ ClutterActor *inActor,
+ gpointer inUserData)
+{
+ g_return_if_fail(XFDASHBOARD_IS_POPUP_MENU_ITEM_META(self));
+ g_return_if_fail(CLUTTER_IS_ACTOR(inActor));
+ g_return_if_fail(XFDASHBOARD_IS_CLICK_ACTION(inUserData));
+
+ /* Activate menu item */
+ xfdashboard_popup_menu_item_meta_activate(self);
+}
+
+/* Set pop-up menu */
+static void _xfdashboard_popup_menu_item_meta_set_popup_menu(XfdashboardPopupMenuItemMeta *self,
+ XfdashboardPopupMenu *inPopupMenu)
+{
+ XfdashboardPopupMenuItemMetaPrivate *priv;
+
+ g_return_if_fail(XFDASHBOARD_IS_POPUP_MENU_ITEM_META(self));
+ g_return_if_fail(XFDASHBOARD_IS_POPUP_MENU(inPopupMenu));
+
+ priv=self->priv;
+
+ /* This function can only be called once so no pop-up must be set yet */
+ if(priv->popupMenu)
+ {
+ g_critical(_("Attempting to set pop-up menu %s at %s but it is already set."),
+ G_OBJECT_TYPE_NAME(inPopupMenu),
+ G_OBJECT_TYPE_NAME(self));
+ return;
+ }
+
+ /* Set value if changed */
+ if(priv->popupMenu!=inPopupMenu)
+ {
+ /* Set value */
+ priv->popupMenu=inPopupMenu;
+ g_object_add_weak_pointer(G_OBJECT(priv->popupMenu), (gpointer*)&priv->popupMenu);
+
+ /* Notify about property change */
+ g_object_notify_by_pspec(G_OBJECT(self), XfdashboardPopupMenuItemMetaProperties[PROP_POPUP_MENU]);
+ }
+}
+
+/* Set menu item actor */
+static void _xfdashboard_popup_menu_item_meta_set_menu_item(XfdashboardPopupMenuItemMeta *self,
+ ClutterActor *inMenuItem)
+{
+ XfdashboardPopupMenuItemMetaPrivate *priv;
+
+ g_return_if_fail(XFDASHBOARD_IS_POPUP_MENU_ITEM_META(self));
+ g_return_if_fail(CLUTTER_IS_ACTOR(inMenuItem));
+
+ priv=self->priv;
+
+ /* This function can only be called once so no menu item must be set yet */
+ if(priv->menuItem || priv->clickAction)
+ {
+ g_critical(_("Attempting to set menu item %s at %s but it is already set."),
+ G_OBJECT_TYPE_NAME(inMenuItem),
+ G_OBJECT_TYPE_NAME(self));
+ return;
+ }
+
+ /* Set value if changed */
+ if(priv->menuItem!=inMenuItem)
+ {
+ /* Set value */
+ priv->menuItem=inMenuItem;
+ g_object_add_weak_pointer(G_OBJECT(priv->menuItem), (gpointer*)&priv->menuItem);
+
+ /* Apply style for menu item if possible */
+ if(XFDASHBOARD_IS_STYLABLE(priv->menuItem))
+ {
+ xfdashboard_stylable_add_class(XFDASHBOARD_STYLABLE(priv->menuItem), "popup-menu-item");
+ }
+
+ /* Create click action and add it to menu item actor */
+ priv->clickAction=xfdashboard_click_action_new();
+ g_signal_connect_swapped(priv->clickAction,
+ "clicked",
+ G_CALLBACK(_xfdashboard_popup_menu_item_meta_clicked),
+ self);
+ clutter_actor_add_action(priv->menuItem, priv->clickAction);
+
+ /* Notify about property change */
+ g_object_notify_by_pspec(G_OBJECT(self), XfdashboardPopupMenuItemMetaProperties[PROP_MENU_ITEM]);
+ }
+}
+
+/* Set callback function */
+static void _xfdashboard_popup_menu_item_meta_set_callback(XfdashboardPopupMenuItemMeta *self,
+ XfdashboardPopupMenuItemActivateCallback inCallback)
+{
+ XfdashboardPopupMenuItemMetaPrivate *priv;
+
+ g_return_if_fail(XFDASHBOARD_IS_POPUP_MENU_ITEM_META(self));
+
+ priv=self->priv;
+
+ /* This function can only be called once so no callback function must be set yet */
+ if(priv->callback)
+ {
+ g_critical(_("Attempting to set callback function at %s but it is already set."),
+ G_OBJECT_TYPE_NAME(self));
+ return;
+ }
+
+ /* Set value if changed */
+ if(priv->callback!=inCallback)
+ {
+ /* Set value */
+ priv->callback=inCallback;
+
+ /* Notify about property change */
+ g_object_notify_by_pspec(G_OBJECT(self), XfdashboardPopupMenuItemMetaProperties[PROP_CALLBACK]);
+ }
+}
+
+/* Set user-data function */
+static void _xfdashboard_popup_menu_item_meta_set_user_data(XfdashboardPopupMenuItemMeta *self,
+ gpointer inUserData)
+{
+ XfdashboardPopupMenuItemMetaPrivate *priv;
+
+ g_return_if_fail(XFDASHBOARD_IS_POPUP_MENU_ITEM_META(self));
+
+ priv=self->priv;
+
+ /* This function can only be called once so no user-data must be set yet */
+ if(priv->userData)
+ {
+ g_critical(_("Attempting to set user-data at %s but it is already set."),
+ G_OBJECT_TYPE_NAME(self));
+ return;
+ }
+
+ /* Set value if changed */
+ if(priv->userData!=inUserData)
+ {
+ /* Set value */
+ priv->userData=inUserData;
+
+ /* Notify about property change */
+ g_object_notify_by_pspec(G_OBJECT(self), XfdashboardPopupMenuItemMetaProperties[PROP_USER_DATA]);
+ }
+}
+
+
+/* IMPLEMENTATION: GObject */
+
+/* Dispose this object */
+static void _xfdashboard_popup_menu_item_meta_dispose(GObject *inObject)
+{
+ XfdashboardPopupMenuItemMeta *self=XFDASHBOARD_POPUP_MENU_ITEM_META(inObject);
+ XfdashboardPopupMenuItemMetaPrivate *priv=self->priv;
+
+ /* Release our allocated variables */
+ if(priv->popupMenu)
+ {
+ g_object_remove_weak_pointer(G_OBJECT(priv->popupMenu), (gpointer*)&priv->popupMenu);
+ priv->popupMenu=NULL;
+ }
+
+ if(priv->menuItem)
+ {
+ /* Remove style from menu item if possible */
+ if(XFDASHBOARD_IS_STYLABLE(priv->menuItem))
+ {
+ xfdashboard_stylable_remove_class(XFDASHBOARD_STYLABLE(priv->menuItem), "popup-menu-item");
+ }
+
+ /* Remove click action from menu item actor */
+ if(priv->clickAction)
+ {
+ clutter_actor_remove_action(priv->menuItem, priv->clickAction);
+ priv->clickAction=NULL;
+ }
+
+ /* Release menu item actor */
+ g_object_remove_weak_pointer(G_OBJECT(priv->menuItem), (gpointer*)&priv->menuItem);
+ priv->menuItem=NULL;
+ }
+
+ if(priv->callback)
+ {
+ priv->callback=NULL;
+ }
+
+ if(priv->userData)
+ {
+ priv->userData=NULL;
+ }
+
+ /* Call parent's class dispose method */
+ G_OBJECT_CLASS(xfdashboard_popup_menu_item_meta_parent_class)->dispose(inObject);
+}
+
+/* Set/get properties */
+static void _xfdashboard_popup_menu_item_meta_set_property(GObject *inObject,
+ guint inPropID,
+ const GValue *inValue,
+ GParamSpec *inSpec)
+{
+ XfdashboardPopupMenuItemMeta *self=XFDASHBOARD_POPUP_MENU_ITEM_META(inObject);
+
+ switch(inPropID)
+ {
+ case PROP_POPUP_MENU:
+ _xfdashboard_popup_menu_item_meta_set_popup_menu(self, g_value_get_object(inValue));
+ break;
+
+ case PROP_MENU_ITEM:
+ _xfdashboard_popup_menu_item_meta_set_menu_item(self, g_value_get_object(inValue));
+ break;
+
+ case PROP_CALLBACK:
+ _xfdashboard_popup_menu_item_meta_set_callback(self, g_value_get_pointer(inValue));
+ break;
+
+ case PROP_USER_DATA:
+ _xfdashboard_popup_menu_item_meta_set_user_data(self, g_value_get_pointer(inValue));
+ break;
+
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID(inObject, inPropID, inSpec);
+ break;
+ }
+}
+
+static void _xfdashboard_popup_menu_item_meta_get_property(GObject *inObject,
+ guint inPropID,
+ GValue *outValue,
+ GParamSpec *inSpec)
+{
+ XfdashboardPopupMenuItemMeta *self=XFDASHBOARD_POPUP_MENU_ITEM_META(inObject);
+ XfdashboardPopupMenuItemMetaPrivate *priv=self->priv;
+
+ switch(inPropID)
+ {
+ case PROP_POPUP_MENU:
+ g_value_set_object(outValue, priv->popupMenu);
+ break;
+
+ case PROP_MENU_ITEM:
+ g_value_set_object(outValue, priv->menuItem);
+ break;
+
+ case PROP_CALLBACK:
+ g_value_set_pointer(outValue, priv->callback);
+ break;
+
+ case PROP_USER_DATA:
+ g_value_set_pointer(outValue, priv->userData);
+ break;
+
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID(inObject, inPropID, inSpec);
+ break;
+ }
+}
+
+/* Class initialization
+ * Override functions in parent classes and define properties
+ * and signals
+ */
+static void xfdashboard_popup_menu_item_meta_class_init(XfdashboardPopupMenuItemMetaClass *klass)
+{
+ GObjectClass *gobjectClass=G_OBJECT_CLASS(klass);
+
+ /* Override functions */
+ gobjectClass->dispose=_xfdashboard_popup_menu_item_meta_dispose;
+ gobjectClass->set_property=_xfdashboard_popup_menu_item_meta_set_property;
+ gobjectClass->get_property=_xfdashboard_popup_menu_item_meta_get_property;
+
+ /* Set up private structure */
+ g_type_class_add_private(klass, sizeof(XfdashboardPopupMenuItemMetaPrivate));
+
+ /* Define properties */
+ /**
+ * XfdashboardPopupMenuItemMeta:popup-menu:
+ *
+ * The pop-up menu which contains the menu item where this meta belongs to.
+ */
+ XfdashboardPopupMenuItemMetaProperties[PROP_POPUP_MENU]=
+ g_param_spec_object("popup-menu",
+ _("Pop-up menu"),
+ _("The pop-up menu containing the menu item where this meta belongs to"),
+ XFDASHBOARD_TYPE_POPUP_MENU,
+ G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS | G_PARAM_CONSTRUCT_ONLY);
+
+ /**
+ * XfdashboardPopupMenuItemMeta:menu-item:
+ *
+ * The menu item where this meta belongs to.
+ */
+ XfdashboardPopupMenuItemMetaProperties[PROP_MENU_ITEM]=
+ g_param_spec_object("menu-item",
+ _("Menu item"),
+ _("The menu item where this meta belongs to"),
+ CLUTTER_TYPE_ACTOR,
+ G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS | G_PARAM_CONSTRUCT_ONLY);
+
+ /**
+ * XfdashboardPopupMenuItemMeta:callback:
+ *
+ * The callback function to call when this meta for a menu item is activated.
+ */
+ XfdashboardPopupMenuItemMetaProperties[PROP_CALLBACK]=
+ g_param_spec_pointer("callback",
+ _("Callback"),
+ _("The callback function to call when this meta is activated"),
+ G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS | G_PARAM_CONSTRUCT_ONLY);
+
+ /**
+ * XfdashboardPopupMenuItemMeta:user-data:
+ *
+ * The user-data to pass to callback function which is called when this meta
+ * for a menu item is activated.
+ */
+ XfdashboardPopupMenuItemMetaProperties[PROP_USER_DATA]=
+ g_param_spec_pointer("user-data",
+ _("User data"),
+ _("The user data to pass to callback function"),
+ G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS | G_PARAM_CONSTRUCT_ONLY);
+
+ g_object_class_install_properties(gobjectClass, PROP_LAST, XfdashboardPopupMenuItemMetaProperties);
+
+ /* Define signals */
+ /**
+ * XfdashboardPopupMenuItemMeta::activated:
+ * @self: The pop-up menu which was activated
+ *
+ * The ::activated signal is emitted when the pop-up menu is shown and the
+ * user can perform an action by selecting an item.
+ */
+ XfdashboardPopupMenuItemMetaSignals[SIGNAL_ACTIVATED]=
+ g_signal_new("activated",
+ G_TYPE_FROM_CLASS(klass),
+ G_SIGNAL_RUN_LAST,
+ G_STRUCT_OFFSET(XfdashboardPopupMenuItemMetaClass, activated),
+ NULL,
+ NULL,
+ g_cclosure_marshal_VOID__VOID,
+ G_TYPE_NONE,
+ 0);
+}
+
+/* Object initialization
+ * Create private structure and set up default values
+ */
+static void xfdashboard_popup_menu_item_meta_init(XfdashboardPopupMenuItemMeta *self)
+{
+ XfdashboardPopupMenuItemMetaPrivate *priv;
+
+ priv=self->priv=XFDASHBOARD_POPUP_MENU_ITEM_META_GET_PRIVATE(self);
+
+ /* Set up default values */
+ priv->popupMenu=NULL;
+ priv->menuItem=NULL;
+ priv->callback=NULL;
+ priv->userData=NULL;
+ priv->clickAction=NULL;
+}
+
+/* IMPLEMENTATION: Public API */
+
+/**
+ * xfdashboard_popup_menu_item_meta_new:
+ * @inPopupMenu: The #XfdashboardPopupMenu where the menu item belongs to
+ * @inMenuItem: A #ClutterActor menu item of pop-up menu
+ * @inCallback: (type XfdashboardPopupMenuItemActivateCallback): The function to
+ * be called when the menu item is activated
+ * @inUserData: Data to be passed to @inCallback
+ *
+ * Creates a new #XfdashboardPopupMenuItemMeta meta object instance for the menu
+ * item actor @inMenuItem at pop-up menu @inPopupMenu. When the menu item is
+ * clicked or xfdashboard_popup_menu_item_meta_activate() is called for the menu
+ * item the callback function @inCallback is called and the data @inUserData is
+ * passed to that callback function.
+ *
+ * Return value: The newly created #XfdashboardPopupMenuItemMeta
+ */
+XfdashboardPopupMenuItemMeta* xfdashboard_popup_menu_item_meta_new(XfdashboardPopupMenu *inPopupMenu,
+ ClutterActor *inMenuItem,
+ XfdashboardPopupMenuItemActivateCallback inCallback,
+ gpointer inUserData)
+{
+ GObject *meta;
+
+ g_return_val_if_fail(XFDASHBOARD_IS_POPUP_MENU(inPopupMenu), NULL);
+ g_return_val_if_fail(CLUTTER_IS_ACTOR(inMenuItem), NULL);
+
+ /* Create meta object */
+ meta=g_object_new(XFDASHBOARD_TYPE_POPUP_MENU_ITEM_META,
+ "popup-menu", inPopupMenu,
+ "menu-item", inMenuItem,
+ "callback", inCallback,
+ "user-data", inUserData,
+ NULL);
+
+ /* Return newly created meta object */
+ return(XFDASHBOARD_POPUP_MENU_ITEM_META(meta));
+}
+
+/**
+ * xfdashboard_popup_menu_item_meta_new:
+ * @self: A #XfdashboardPopupMenuItemMeta
+ *
+ * Activates the menu item associated with this #XfdashboardPopupMenuItemMeta by
+ * calling the callback function and passing the user data to that function. Also
+ * the signal "activated" will be emitted after the callback function was called.
+ */
+void xfdashboard_popup_menu_item_meta_activate(XfdashboardPopupMenuItemMeta *self)
+{
+ XfdashboardPopupMenuItemMetaPrivate *priv;
+
+ g_return_if_fail(XFDASHBOARD_IS_POPUP_MENU_ITEM_META(self));
+
+ priv=self->priv;
+
+ /* Call the callback handler and pass data and associated user data to it */
+ if(priv->callback)
+ {
+ (priv->callback)(priv->popupMenu, priv->menuItem, priv->userData);
+ }
+
+ /* Emit signal for activation */
+ g_signal_emit(self, XfdashboardPopupMenuItemMetaSignals[SIGNAL_ACTIVATED], 0);
+}
+
+/**
+ * xfdashboard_popup_menu_item_meta_get_popup_menu:
+ * @self: A #XfdashboardPopupMenuItemMeta
+ *
+ * Retrieves the associated pop-up menu of @self.
+ *
+ * Return value: (transfer none): The associated pop-up menu of type #XfdashboardPopupMenu
+ */
+XfdashboardPopupMenu* xfdashboard_popup_menu_item_meta_get_popup_menu(XfdashboardPopupMenuItemMeta *self)
+{
+ g_return_val_if_fail(XFDASHBOARD_IS_POPUP_MENU_ITEM_META(self), NULL);
+
+ return(self->priv->popupMenu);
+}
+
+/**
+ * xfdashboard_popup_menu_item_meta_get_menu_item:
+ * @self: A #XfdashboardPopupMenuItemMeta
+ *
+ * Retrieves the associated menu item actor of @self.
+ *
+ * Return value: (transfer none): The associated menu item actor
+ */
+ClutterActor* xfdashboard_popup_menu_item_meta_get_menu_item(XfdashboardPopupMenuItemMeta *self)
+{
+ g_return_val_if_fail(XFDASHBOARD_IS_POPUP_MENU_ITEM_META(self), NULL);
+
+ return(self->priv->menuItem);
+}
+
+/**
+ * xfdashboard_popup_menu_item_meta_get_callback:
+ * @self: A #XfdashboardPopupMenuItemMeta
+ *
+ * Retrieves the associated callback function of @self which is called when the
+ * menu item is activated.
+ *
+ * Return value: The pointer to callback function
+ */
+gpointer xfdashboard_popup_menu_item_meta_get_callback(XfdashboardPopupMenuItemMeta *self)
+{
+ g_return_val_if_fail(XFDASHBOARD_IS_POPUP_MENU_ITEM_META(self), NULL);
+
+ return(self->priv->callback);
+}
+
+/**
+ * xfdashboard_popup_menu_item_meta_get_user_data:
+ * @self: A #XfdashboardPopupMenuItemMeta
+ *
+ * Retrieves the associated user data passed to callback function of @self which
+ * is called when the menu item is activated.
+ *
+ * Return value: The associated user data
+ */
+gpointer xfdashboard_popup_menu_item_meta_get_user_data(XfdashboardPopupMenuItemMeta *self)
+{
+ g_return_val_if_fail(XFDASHBOARD_IS_POPUP_MENU_ITEM_META(self), NULL);
+
+ return(self->priv->userData);
+}
diff --git a/libxfdashboard/popup-menu-item-meta.h b/libxfdashboard/popup-menu-item-meta.h
new file mode 100644
index 0000000..75a1f36
--- /dev/null
+++ b/libxfdashboard/popup-menu-item-meta.h
@@ -0,0 +1,95 @@
+/*
+ * popup-menu-item-meta: A meta class for menu items in a pop-up menu
+ *
+ * Copyright 2012-2016 Stephan Haller <nomad at froevel.de>
+ *
+ * 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 2 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, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
+ * MA 02110-1301, USA.
+ *
+ *
+ */
+
+#ifndef __LIBXFDASHBOARD_POPUP_MENU_ITEM_META__
+#define __LIBXFDASHBOARD_POPUP_MENU_ITEM_META__
+
+#if !defined(__LIBXFDASHBOARD_H_INSIDE__) && !defined(LIBXFDASHBOARD_COMPILATION)
+#error "Only <libxfdashboard/libxfdashboard.h> can be included directly."
+#endif
+
+#include <libxfdashboard/popup-menu.h>
+
+G_BEGIN_DECLS
+
+#define XFDASHBOARD_TYPE_POPUP_MENU_ITEM_META (xfdashboard_popup_menu_item_meta_get_type())
+#define XFDASHBOARD_POPUP_MENU_ITEM_META(obj) (G_TYPE_CHECK_INSTANCE_CAST((obj), XFDASHBOARD_TYPE_POPUP_MENU_ITEM_META, XfdashboardPopupMenuItemMeta))
+#define XFDASHBOARD_IS_POPUP_MENU_ITEM_META(obj) (G_TYPE_CHECK_INSTANCE_TYPE((obj), XFDASHBOARD_TYPE_POPUP_MENU_ITEM_META))
+#define XFDASHBOARD_POPUP_MENU_ITEM_META_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST((klass), XFDASHBOARD_TYPE_POPUP_MENU_ITEM_META, XfdashboardPopupMenuItemMetaClass))
+#define XFDASHBOARD_IS_POPUP_MENU_ITEM_META_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE((klass), XFDASHBOARD_TYPE_POPUP_MENU_ITEM_META))
+#define XFDASHBOARD_POPUP_MENU_ITEM_META_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS((obj), XFDASHBOARD_TYPE_POPUP_MENU_ITEM_META, XfdashboardPopupMenuItemMetaClass))
+
+typedef struct _XfdashboardPopupMenuItemMeta XfdashboardPopupMenuItemMeta;
+typedef struct _XfdashboardPopupMenuItemMetaClass XfdashboardPopupMenuItemMetaClass;
+typedef struct _XfdashboardPopupMenuItemMetaPrivate XfdashboardPopupMenuItemMetaPrivate;
+
+/**
+ * XfdashboardPopupMenuItemMeta:
+ *
+ * The #XfdashboardPopupMenuItemMeta structure contains only private data and
+ * should be accessed using the provided API
+ */
+struct _XfdashboardPopupMenuItemMeta
+{
+ /*< private >*/
+ /* Parent instance */
+ GObject parent_instance;
+
+ /* Private structure */
+ XfdashboardPopupMenuItemMetaPrivate *priv;
+};
+
+/**
+ * XfdashboardPopupMenuItemMetaClass:
+ *
+ * The #XfdashboardPopupMenuItemMetaClass structure contains only private data
+ */
+struct _XfdashboardPopupMenuItemMetaClass
+{
+ /*< private >*/
+ /* Parent class */
+ GObjectClass parent_class;
+
+ /*< public >*/
+ /* Virtual functions */
+ void (*activated)(XfdashboardPopupMenuItemMeta *self);
+};
+
+/* Public API */
+GType xfdashboard_popup_menu_item_meta_get_type(void) G_GNUC_CONST;
+
+XfdashboardPopupMenuItemMeta* xfdashboard_popup_menu_item_meta_new(XfdashboardPopupMenu *inPopupMenu,
+ ClutterActor *inMenuItem,
+ XfdashboardPopupMenuItemActivateCallback inCallback,
+ gpointer inUserData);
+
+XfdashboardPopupMenu* xfdashboard_popup_menu_item_meta_get_popup_menu(XfdashboardPopupMenuItemMeta *self);
+ClutterActor* xfdashboard_popup_menu_item_meta_get_menu_item(XfdashboardPopupMenuItemMeta *self);
+gpointer xfdashboard_popup_menu_item_meta_get_callback(XfdashboardPopupMenuItemMeta *self);
+gpointer xfdashboard_popup_menu_item_meta_get_user_data(XfdashboardPopupMenuItemMeta *self);
+
+void xfdashboard_popup_menu_item_meta_activate(XfdashboardPopupMenuItemMeta *self);
+
+G_END_DECLS
+
+#endif /* __LIBXFDASHBOARD_POPUP_MENU_ITEM_META__ */
diff --git a/libxfdashboard/popup-menu.c b/libxfdashboard/popup-menu.c
new file mode 100644
index 0000000..1cd27e5
--- /dev/null
+++ b/libxfdashboard/popup-menu.c
@@ -0,0 +1,2005 @@
+/*
+ * popup-menu: A pop-up menu with menu items performing an action when an menu
+ * item was clicked
+ *
+ * Copyright 2012-2016 Stephan Haller <nomad at froevel.de>
+ *
+ * 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 2 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, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
+ * MA 02110-1301, USA.
+ *
+ *
+ */
+
+/**
+ * SECTION:popup-menu
+ * @short_description: A pop-up menu showing items and perfoming an action
+ when an item was clicked
+ * @include: xfdashboard/popup-menu.h
+ *
+ * A #XfdashboardPopupMenu implements a drop down menu consisting of a list of
+ * #ClutterActor objects as menu items which can be navigated and activated by
+ * the user to perform the associated action of the selected menu item.
+ *
+ * The following example shows how create and activate a #XfdashboardPopupMenu
+ * when an actor was clicked.
+ * application when clicked:
+ *
+ * |[<!-- language="C" -->
+ * ]|
+ */
+
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+
+#include <libxfdashboard/popup-menu.h>
+
+#include <glib/gi18n-lib.h>
+#include <gdk/gdk.h>
+#include <math.h>
+
+#include <libxfdashboard/popup-menu-item-meta.h>
+#include <libxfdashboard/box-layout.h>
+#include <libxfdashboard/focusable.h>
+#include <libxfdashboard/focus-manager.h>
+#include <libxfdashboard/stylable.h>
+#include <libxfdashboard/window-tracker.h>
+#include <libxfdashboard/application.h>
+#include <libxfdashboard/click-action.h>
+#include <libxfdashboard/bindings-pool.h>
+#include <libxfdashboard/button.h>
+#include <libxfdashboard/enums.h>
+#include <libxfdashboard/utils.h>
+#include <libxfdashboard/compat.h>
+
+
+/* Define this class in GObject system */
+static void _xfdashboard_popup_menu_focusable_iface_init(XfdashboardFocusableInterface *iface);
+
+G_DEFINE_TYPE_WITH_CODE(XfdashboardPopupMenu,
+ xfdashboard_popup_menu,
+ XFDASHBOARD_TYPE_BACKGROUND,
+ G_IMPLEMENT_INTERFACE(XFDASHBOARD_TYPE_FOCUSABLE, _xfdashboard_popup_menu_focusable_iface_init));
+
+/* Private structure - access only by public API if needed */
+#define XFDASHBOARD_POPUP_MENU_GET_PRIVATE(obj) \
+ (G_TYPE_INSTANCE_GET_PRIVATE((obj), XFDASHBOARD_TYPE_POPUP_MENU, XfdashboardPopupMenuPrivate))
+
+struct _XfdashboardPopupMenuPrivate
+{
+ /* Properties related */
+ gboolean destroyOnCancel;
+
+ ClutterActor *source;
+
+ gboolean showTitle;
+ gboolean showTitleIcon;
+
+ /* Instance related */
+ gboolean isActive;
+
+ ClutterActor *title;
+ ClutterActor *itemsContainer;
+
+ XfdashboardWindowTracker *windowTracker;
+
+ XfdashboardFocusManager *focusManager;
+ gpointer oldFocusable;
+ gpointer selectedItem;
+
+ XfdashboardStage *stage;
+ guint capturedEventSignalID;
+
+ guint sourceDestroySignalID;
+};
+
+/* Properties */
+enum
+{
+ PROP_0,
+
+ PROP_DESTROY_ON_CANCEL,
+
+ PROP_SOURCE,
+
+ PROP_SHOW_TITLE,
+ PROP_TITLE,
+
+ PROP_SHOW_TITLE_ICON,
+ PROP_TITLE_ICON_NAME,
+ PROP_TITLE_GICON,
+
+ PROP_LAST
+};
+
+static GParamSpec* XfdashboardPopupMenuProperties[PROP_LAST]={ 0, };
+
+/* Signals */
+enum
+{
+ SIGNAL_ACTIVATED,
+ SIGNAL_CANCELLED,
+
+ SIGNAL_ITEM_ACTIVATED,
+
+ SIGNAL_ITEM_ADDED,
+ SIGNAL_ITEM_REMOVED,
+
+ SIGNAL_LAST
+};
+
+static guint XfdashboardPopupMenuSignals[SIGNAL_LAST]={ 0, };
+
+
+/* IMPLEMENTATION: Private variables and methods */
+
+static GQuark _xfdashboard_popup_menu_items_container_child_meta_quark=0;
+
+/* An event occured after a popup menu was activated so check if popup menu should
+ * be cancelled because a button was pressed and release outside the popup menu.
+ */
+static gboolean _xfdashboard_popup_menu_on_captured_event(XfdashboardPopupMenu *self,
+ ClutterEvent *inEvent,
+ gpointer inUserData)
+{
+ XfdashboardPopupMenuPrivate *priv;
+
+ g_return_val_if_fail(XFDASHBOARD_IS_POPUP_MENU(self), CLUTTER_EVENT_PROPAGATE);
+ g_return_val_if_fail(XFDASHBOARD_IS_STAGE(inUserData), CLUTTER_EVENT_PROPAGATE);
+
+ priv=self->priv;
+
+ /* Check if popup menu should be cancelled depending on event */
+ switch(clutter_event_type(inEvent))
+ {
+ case CLUTTER_BUTTON_RELEASE:
+ /* If button was released outside popup menu cancel this popup menu */
+ {
+ gfloat x, y, w, h;
+
+ clutter_actor_get_transformed_position(CLUTTER_ACTOR(self), &x, &y);
+ clutter_actor_get_size(CLUTTER_ACTOR(self), &w, &h);
+ if(inEvent->button.x<x ||
+ inEvent->button.x>=(x+w) ||
+ inEvent->button.y<y ||
+ inEvent->button.y>=(y+h))
+ {
+ /* Cancel popup menu */
+ xfdashboard_popup_menu_cancel(self);
+
+ /* Do not let this event be handled */
+ return(CLUTTER_EVENT_STOP);
+ }
+ }
+ break;
+
+ case CLUTTER_KEY_PRESS:
+ case CLUTTER_KEY_RELEASE:
+ /* If key press or key release is not a selection action for a focusable
+ * actor then cancel this popup menu.
+ */
+ {
+ GSList *targetFocusables;
+ const gchar *action;
+ gboolean cancelPopupMenu;
+
+ /* Lookup action for event and emit action if a binding was found
+ * for this event.
+ */
+ targetFocusables=NULL;
+ action=NULL;
+ cancelPopupMenu=FALSE;
+
+ if(xfdashboard_focus_manager_get_event_targets_and_action(priv->focusManager, inEvent, XFDASHBOARD_FOCUSABLE(self), &targetFocusables, &action))
+ {
+ if(!targetFocusables ||
+ !targetFocusables->data ||
+ !XFDASHBOARD_IS_POPUP_MENU(targetFocusables->data))
+ {
+ cancelPopupMenu=TRUE;
+ }
+
+ /* Release allocated resources */
+ g_slist_free_full(targetFocusables, g_object_unref);
+ }
+
+ /* 'ESC' is a special key as it cannot be determined by focus
+ * manager but it has to be intercepted as this key release
+ * should only cancel popup-menu but not quit application.
+ */
+ if(!cancelPopupMenu &&
+ clutter_event_type(inEvent)==CLUTTER_KEY_RELEASE &&
+ inEvent->key.keyval==CLUTTER_KEY_Escape)
+ {
+ cancelPopupMenu=TRUE;
+ }
+
+ /* Cancel popup-menu if requested */
+ if(cancelPopupMenu)
+ {
+ /* Cancel popup menu */
+ xfdashboard_popup_menu_cancel(self);
+
+ /* Do not let this event be handled */
+ return(CLUTTER_EVENT_STOP);
+ }
+ }
+ break;
+
+ default:
+ /* Let all other event pass through */
+ break;
+ }
+
+ /* If we get here then this event passed our filter and can be handled normally */
+ return(CLUTTER_EVENT_PROPAGATE);
+}
+
+/* Check if menu item is really part of this pop-up menu */
+static gboolean _xfdashboard_popup_menu_contains_menu_item(XfdashboardPopupMenu *self, ClutterActor *inMenuItem)
+{
+ XfdashboardPopupMenuItemMeta *meta;
+
+ g_return_val_if_fail(XFDASHBOARD_IS_POPUP_MENU(self), FALSE);
+ g_return_val_if_fail(CLUTTER_IS_ACTOR(inMenuItem), FALSE);
+
+ /* Get meta of requested menu item actor */
+ meta=g_object_get_qdata(G_OBJECT(inMenuItem), _xfdashboard_popup_menu_items_container_child_meta_quark);
+ if(!meta || !XFDASHBOARD_IS_POPUP_MENU_ITEM_META(meta)) return(FALSE);
+
+ /* Meta must really belong to the requested menu item actor */
+ if(xfdashboard_popup_menu_item_meta_get_menu_item(meta)!=inMenuItem)
+ {
+ g_critical(_("Menu item meta does not match menu item!"));
+ return(FALSE);
+ }
+
+ /* Meta must belong to this pop-up menu */
+ if(xfdashboard_popup_menu_item_meta_get_popup_menu(meta)!=self)
+ {
+ g_critical(_("Menu item meta does not match pop-up menu!"));
+ return(FALSE);
+ }
+
+ /* If we get here the "menu item" actor is a menu item of this pop-up menu */
+ return(TRUE);
+}
+
+/* Menu item was activated */
+static void _xfdashboard_popup_menu_on_meta_activated(XfdashboardPopupMenu *self,
+ gpointer inUserData)
+{
+ g_return_if_fail(XFDASHBOARD_IS_POPUP_MENU(self));
+ g_return_if_fail(XFDASHBOARD_IS_POPUP_MENU_ITEM_META(inUserData));
+
+ /* Cancel pop-up menu as menu item was activated and its callback function
+ * was called by its meta object.
+ */
+ xfdashboard_popup_menu_cancel(self);
+}
+
+/* Update visiblity of title actor depending on if title and/or icon of title
+ * should be shown or not.
+ */
+static void _xfdashboard_popup_menu_update_title_actors_visibility(XfdashboardPopupMenu *self)
+{
+ XfdashboardPopupMenuPrivate *priv;
+ XfdashboardButtonStyle oldStyle;
+ XfdashboardButtonStyle newStyle;
+ gboolean oldVisible;
+ gboolean newVisible;
+
+ g_return_if_fail(XFDASHBOARD_IS_POPUP_MENU(self));
+
+ priv=self->priv;
+
+ /* Get current visibility state */
+ oldVisible=clutter_actor_is_visible(priv->title);
+ oldStyle=xfdashboard_button_get_style(XFDASHBOARD_BUTTON(priv->title));
+
+ /* Determine new visibility state depending on if title and/or icon of title
+ * should be shown or not.
+ */
+ newStyle=0;
+ newVisible=TRUE;
+ if(priv->showTitle && priv->showTitleIcon) newStyle=XFDASHBOARD_BUTTON_STYLE_BOTH;
+ else if(priv->showTitle) newStyle=XFDASHBOARD_BUTTON_STYLE_TEXT;
+ else if(priv->showTitleIcon) newStyle=XFDASHBOARD_BUTTON_STYLE_ICON;
+ else newVisible=FALSE;
+
+ /* Set new visibility style if changed and re-layout title actor */
+ if(newStyle!=oldStyle)
+ {
+ xfdashboard_button_set_style(XFDASHBOARD_BUTTON(priv->title), newStyle);
+ clutter_actor_queue_relayout(priv->title);
+ }
+
+ /* Show or hide actor */
+ if(newVisible!=oldVisible)
+ {
+ if(newVisible) clutter_actor_show(priv->title);
+ else clutter_actor_hide(priv->title);
+ }
+}
+
+/* The source actor was destroyed so cancel this pop-up menu if active and
+ * destroy it if automatic destruction was turned on.
+ */
+static void _xfdashboard_popup_menu_on_source_destroy(XfdashboardPopupMenu *self,
+ gpointer inUserData)
+{
+ XfdashboardPopupMenuPrivate *priv;
+
+ g_return_if_fail(XFDASHBOARD_IS_POPUP_MENU(self));
+ g_return_if_fail(CLUTTER_IS_ACTOR(inUserData));
+
+ priv=self->priv;
+
+ /* Unset and clean-up source */
+ if(priv->source)
+ {
+ gchar *cssClass;
+
+ /* Disconnect signal handler */
+ if(priv->sourceDestroySignalID)
+ {
+ g_signal_handler_disconnect(priv->source, priv->sourceDestroySignalID);
+ priv->sourceDestroySignalID=0;
+ }
+
+ /* Remove style */
+ cssClass=g_strdup_printf("popup-menu-source-%s", G_OBJECT_TYPE_NAME(priv->source));
+ xfdashboard_stylable_remove_class(XFDASHBOARD_STYLABLE(self), cssClass);
+ g_free(cssClass);
+
+ /* Release source */
+ g_object_remove_weak_pointer(G_OBJECT(priv->source), (gpointer*)&priv->source);
+ priv->source=NULL;
+ }
+
+ /* Enforce that pop-up menu is cancelled either by calling the cancel function
+ * if it is active or by checking and destructing it if automatic destruction
+ * flag is set.
+ */
+ if(priv->isActive)
+ {
+ /* Pop-up menu is active so cancel it. The cancel function will also destroy
+ * it if destroy-on-cancel was enabled.
+ */
+ xfdashboard_popup_menu_cancel(self);
+ }
+ else
+ {
+ /* Destroy this pop-up menu actor when destroy-on-cancel was enabled */
+ if(priv->destroyOnCancel)
+ {
+ clutter_actor_destroy(CLUTTER_ACTOR(self));
+ }
+ }
+}
+
+
+/* IMPLEMENTATION: ClutterActor */
+
+/* Allocate position and size of actor and its children */
+static void _xfdashboard_popup_menu_allocate(ClutterActor *inActor,
+ const ClutterActorBox *inBox,
+ ClutterAllocationFlags inFlags)
+{
+ ClutterAllocationFlags flags;
+
+ /* Chain up to store the allocation of the actor */
+ flags=inFlags | CLUTTER_DELEGATE_LAYOUT;
+ CLUTTER_ACTOR_CLASS(xfdashboard_popup_menu_parent_class)->allocate(inActor, inBox, flags);
+}
+
+
+/* IMPLEMENTATION: Interface XfdashboardFocusable */
+
+/* Determine if actor can get the focus */
+static gboolean _xfdashboard_popup_menu_focusable_can_focus(XfdashboardFocusable *inFocusable)
+{
+ XfdashboardPopupMenu *self;
+ XfdashboardPopupMenuPrivate *priv;
+ XfdashboardFocusableInterface *selfIface;
+ XfdashboardFocusableInterface *parentIface;
+
+ g_return_val_if_fail(XFDASHBOARD_IS_FOCUSABLE(inFocusable), FALSE);
+ g_return_val_if_fail(XFDASHBOARD_IS_POPUP_MENU(inFocusable), FALSE);
+
+ self=XFDASHBOARD_POPUP_MENU(inFocusable);
+ priv=self->priv;
+
+ /* Call parent class interface function */
+ selfIface=XFDASHBOARD_FOCUSABLE_GET_IFACE(inFocusable);
+ parentIface=g_type_interface_peek_parent(selfIface);
+
+ if(parentIface && parentIface->can_focus)
+ {
+ if(!parentIface->can_focus(inFocusable))
+ {
+ return(FALSE);
+ }
+ }
+
+ /* Only active pop-up menus can be focused */
+ if(!priv->isActive) return(FALSE);
+
+ /* If we get here this actor can be focused */
+ return(TRUE);
+}
+
+/* Actor lost focus */
+static void _xfdashboard_popup_menu_focusable_unset_focus(XfdashboardFocusable *inFocusable)
+{
+ XfdashboardPopupMenu *self;
+ XfdashboardPopupMenuPrivate *priv;
+ XfdashboardFocusableInterface *selfIface;
+ XfdashboardFocusableInterface *parentIface;
+
+ g_return_if_fail(XFDASHBOARD_IS_FOCUSABLE(inFocusable));
+ g_return_if_fail(XFDASHBOARD_IS_POPUP_MENU(inFocusable));
+
+ self=XFDASHBOARD_POPUP_MENU(inFocusable);
+ priv=self->priv;
+
+ /* Call parent class interface function */
+ selfIface=XFDASHBOARD_FOCUSABLE_GET_IFACE(inFocusable);
+ parentIface=g_type_interface_peek_parent(selfIface);
+
+ if(parentIface && parentIface->unset_focus)
+ {
+ parentIface->unset_focus(inFocusable);
+ }
+
+ /* If this pop-up menu is active (has flag set) then it was not cancelled and
+ * this actor lost its focus in any other way than expected. So do not refocus
+ * old remembered focusable as it may not be the one which has the focus before.
+ */
+ if(priv->isActive &&
+ priv->oldFocusable)
+ {
+ g_object_remove_weak_pointer(G_OBJECT(priv->oldFocusable), &priv->oldFocusable);
+ priv->oldFocusable=NULL;
+ }
+
+ /* This actor lost focus so ensure that this popup menu is cancelled */
+ xfdashboard_popup_menu_cancel(self);
+}
+
+/* Determine if this actor supports selection */
+static gboolean _xfdashboard_popup_menu_focusable_supports_selection(XfdashboardFocusable *inFocusable)
+{
+ g_return_val_if_fail(XFDASHBOARD_IS_FOCUSABLE(inFocusable), FALSE);
+ g_return_val_if_fail(XFDASHBOARD_IS_POPUP_MENU(inFocusable), FALSE);
+
+ /* This actor supports selection */
+ return(TRUE);
+}
+
+/* Get current selection */
+static ClutterActor* _xfdashboard_popup_menu_focusable_get_selection(XfdashboardFocusable *inFocusable)
+{
+ XfdashboardPopupMenu *self;
+ XfdashboardPopupMenuPrivate *priv;
+
+ g_return_val_if_fail(XFDASHBOARD_IS_FOCUSABLE(inFocusable), NULL);
+ g_return_val_if_fail(XFDASHBOARD_IS_POPUP_MENU(inFocusable), NULL);
+
+ self=XFDASHBOARD_POPUP_MENU(inFocusable);
+ priv=self->priv;
+
+ /* Return current selection */
+ return(priv->selectedItem);
+}
+
+/* Set new selection */
+static gboolean _xfdashboard_popup_menu_focusable_set_selection(XfdashboardFocusable *inFocusable,
+ ClutterActor *inSelection)
+{
+ XfdashboardPopupMenu *self;
+ XfdashboardPopupMenuPrivate *priv;
+
+ g_return_val_if_fail(XFDASHBOARD_IS_FOCUSABLE(inFocusable), FALSE);
+ g_return_val_if_fail(XFDASHBOARD_IS_POPUP_MENU(inFocusable), FALSE);
+ g_return_val_if_fail(!inSelection || CLUTTER_IS_ACTOR(inSelection), FALSE);
+
+ self=XFDASHBOARD_POPUP_MENU(inFocusable);
+ priv=self->priv;
+
+ /* Check that selection is a child of this actor */
+ if(inSelection &&
+ !clutter_actor_contains(CLUTTER_ACTOR(self), inSelection))
+ {
+ g_warning(_("%s is not a child of %s and cannot be selected"),
+ G_OBJECT_TYPE_NAME(inSelection),
+ G_OBJECT_TYPE_NAME(self));
+
+ return(FALSE);
+ }
+
+ /* Remove weak reference at current selection */
+ if(priv->selectedItem)
+ {
+ g_object_remove_weak_pointer(G_OBJECT(priv->selectedItem), &priv->selectedItem);
+ }
+
+ /* Set new selection */
+ priv->selectedItem=inSelection;
+
+ /* Add weak reference at new selection */
+ if(priv->selectedItem)
+ {
+ g_object_add_weak_pointer(G_OBJECT(priv->selectedItem), &priv->selectedItem);
+ }
+
+ /* New selection was set successfully */
+ return(TRUE);
+}
+
+/* Find requested selection target depending of current selection */
+static ClutterActor* _xfdashboard_popup_menu_focusable_find_selection(XfdashboardFocusable *inFocusable,
+ ClutterActor *inSelection,
+ XfdashboardSelectionTarget inDirection)
+{
+ XfdashboardPopupMenu *self;
+ XfdashboardPopupMenuPrivate *priv;
+ ClutterActor *selection;
+ ClutterActor *newSelection;
+ gchar *valueName;
+
+ g_return_val_if_fail(XFDASHBOARD_IS_FOCUSABLE(inFocusable), NULL);
+ g_return_val_if_fail(XFDASHBOARD_IS_POPUP_MENU(inFocusable), NULL);
+ g_return_val_if_fail(!inSelection || CLUTTER_IS_ACTOR(inSelection), NULL);
+ g_return_val_if_fail(inDirection>=0 && inDirection<=XFDASHBOARD_SELECTION_TARGET_NEXT, NULL);
+
+ self=XFDASHBOARD_POPUP_MENU(inFocusable);
+ priv=self->priv;
+ selection=inSelection;
+ newSelection=NULL;
+
+ /* If there is nothing selected, select first actor and return */
+ if(!inSelection)
+ {
+ selection=clutter_actor_get_first_child(CLUTTER_ACTOR(priv->itemsContainer));
+
+ valueName=xfdashboard_get_enum_value_name(XFDASHBOARD_TYPE_SELECTION_TARGET, inDirection);
+ g_debug("No selection at %s, so select first child %s for direction %s",
+ G_OBJECT_TYPE_NAME(self),
+ selection ? G_OBJECT_TYPE_NAME(selection) : "<nil>",
+ valueName);
+ g_free(valueName);
+
+ return(selection);
+ }
+
+ /* Check that selection is a child of this actor otherwise return NULL */
+ if(!clutter_actor_contains(CLUTTER_ACTOR(self), inSelection))
+ {
+ ClutterActor *parent;
+
+ parent=clutter_actor_get_parent(inSelection);
+ g_warning(_("Cannot lookup selection target at %s because %s is a child of %s"),
+ G_OBJECT_TYPE_NAME(self),
+ G_OBJECT_TYPE_NAME(inSelection),
+ parent ? G_OBJECT_TYPE_NAME(parent) : "<nil>");
+
+ return(NULL);
+ }
+
+ /* Find target selection */
+ switch(inDirection)
+ {
+ case XFDASHBOARD_SELECTION_TARGET_UP:
+ newSelection=clutter_actor_get_previous_sibling(inSelection);
+ break;
+
+ case XFDASHBOARD_SELECTION_TARGET_DOWN:
+ newSelection=clutter_actor_get_next_sibling(inSelection);
+ break;
+
+ case XFDASHBOARD_SELECTION_TARGET_FIRST:
+ case XFDASHBOARD_SELECTION_TARGET_PAGE_UP:
+ newSelection=clutter_actor_get_first_child(CLUTTER_ACTOR(priv->itemsContainer));
+ break;
+
+ case XFDASHBOARD_SELECTION_TARGET_LAST:
+ case XFDASHBOARD_SELECTION_TARGET_PAGE_DOWN:
+ newSelection=clutter_actor_get_last_child(CLUTTER_ACTOR(priv->itemsContainer));
+ break;
+
+ case XFDASHBOARD_SELECTION_TARGET_NEXT:
+ newSelection=clutter_actor_get_next_sibling(inSelection);
+ if(!newSelection) newSelection=clutter_actor_get_previous_sibling(inSelection);
+ break;
+
+ default:
+ {
+ valueName=xfdashboard_get_enum_value_name(XFDASHBOARD_TYPE_SELECTION_TARGET, inDirection);
+ g_critical(_("Focusable object %s does not handle selection direction of type %s."),
+ G_OBJECT_TYPE_NAME(self),
+ valueName);
+ g_free(valueName);
+ }
+ break;
+ }
+
+ /* If new selection could be found override current selection with it */
+ if(newSelection) selection=newSelection;
+
+ /* Return new selection found */
+ g_debug("Selecting %s at %s for current selection %s in direction %u",
+ selection ? G_OBJECT_TYPE_NAME(selection) : "<nil>",
+ G_OBJECT_TYPE_NAME(self),
+ inSelection ? G_OBJECT_TYPE_NAME(inSelection) : "<nil>",
+ inDirection);
+
+ return(selection);
+}
+
+/* Activate selection */
+static gboolean _xfdashboard_popup_menu_focusable_activate_selection(XfdashboardFocusable *inFocusable,
+ ClutterActor *inSelection)
+{
+ XfdashboardPopupMenu *self;
+ XfdashboardPopupMenuItemMeta *meta;
+
+ g_return_val_if_fail(XFDASHBOARD_IS_FOCUSABLE(inFocusable), FALSE);
+ g_return_val_if_fail(XFDASHBOARD_IS_POPUP_MENU(inFocusable), FALSE);
+ g_return_val_if_fail(CLUTTER_IS_ACTOR(inSelection), FALSE);
+
+ self=XFDASHBOARD_POPUP_MENU(inFocusable);
+
+ /* Check that selection is a child of this actor */
+ if(!clutter_actor_contains(CLUTTER_ACTOR(self), inSelection))
+ {
+ ClutterActor *parent;
+
+ parent=clutter_actor_get_parent(inSelection);
+ g_warning(_("%s is a child of %s and cannot be activated at %s"),
+ G_OBJECT_TYPE_NAME(inSelection),
+ parent ? G_OBJECT_TYPE_NAME(parent) : "<nil>",
+ G_OBJECT_TYPE_NAME(self));
+
+ return(FALSE);
+ }
+
+ /* Get meta of requested menu item actor */
+ meta=g_object_get_qdata(G_OBJECT(inSelection), _xfdashboard_popup_menu_items_container_child_meta_quark);
+ if(!meta || !XFDASHBOARD_IS_POPUP_MENU_ITEM_META(meta)) return(FALSE);
+
+ /* Meta must really belong to the requested menu item actor */
+ if(xfdashboard_popup_menu_item_meta_get_menu_item(meta)!=inSelection)
+ {
+ g_critical(_("Menu item meta does not match menu item!"));
+ return(FALSE);
+ }
+
+ /* Meta must belong to this pop-up menu */
+ if(xfdashboard_popup_menu_item_meta_get_popup_menu(meta)!=self)
+ {
+ g_critical(_("Menu item meta does not match pop-up menu!"));
+ return(FALSE);
+ }
+
+ /* Activate selection */
+ xfdashboard_popup_menu_item_meta_activate(XFDASHBOARD_POPUP_MENU_ITEM_META(meta));
+
+ /* If we get here activation of menu item was successful */
+ return(TRUE);
+}
+
+/* Interface initialization
+ * Set up default functions
+ */
+void _xfdashboard_popup_menu_focusable_iface_init(XfdashboardFocusableInterface *iface)
+{
+ iface->can_focus=_xfdashboard_popup_menu_focusable_can_focus;
+ iface->unset_focus=_xfdashboard_popup_menu_focusable_unset_focus;
+
+ iface->supports_selection=_xfdashboard_popup_menu_focusable_supports_selection;
+ iface->get_selection=_xfdashboard_popup_menu_focusable_get_selection;
+ iface->set_selection=_xfdashboard_popup_menu_focusable_set_selection;
+ iface->find_selection=_xfdashboard_popup_menu_focusable_find_selection;
+ iface->activate_selection=_xfdashboard_popup_menu_focusable_activate_selection;
+}
+
+
+/* IMPLEMENTATION: GObject */
+
+/* Dispose this object */
+static void _xfdashboard_popup_menu_dispose(GObject *inObject)
+{
+ XfdashboardPopupMenu *self=XFDASHBOARD_POPUP_MENU(inObject);
+ XfdashboardPopupMenuPrivate *priv=self->priv;
+
+ /* Cancel this pop-up menu if it is still active */
+ xfdashboard_popup_menu_cancel(self);
+
+ /* Release our allocated variables */
+ if(priv->capturedEventSignalID)
+ {
+ g_signal_handler_disconnect(priv->stage, priv->capturedEventSignalID);
+ priv->capturedEventSignalID=0;
+ }
+
+ if(priv->source)
+ {
+ gchar *cssClass;
+
+ /* Disconnect signal handler */
+ if(priv->sourceDestroySignalID)
+ {
+ g_signal_handler_disconnect(priv->source, priv->sourceDestroySignalID);
+ priv->sourceDestroySignalID=0;
+ }
+
+ /* Remove style */
+ cssClass=g_strdup_printf("popup-menu-source-%s", G_OBJECT_TYPE_NAME(priv->source));
+ xfdashboard_stylable_remove_class(XFDASHBOARD_STYLABLE(self), cssClass);
+ g_free(cssClass);
+
+ /* Release source */
+ g_object_remove_weak_pointer(G_OBJECT(priv->source), (gpointer*)&priv->source);
+ priv->source=NULL;
+ }
+
+ if(priv->selectedItem)
+ {
+ g_object_remove_weak_pointer(G_OBJECT(priv->selectedItem), &priv->selectedItem);
+ priv->selectedItem=NULL;
+ }
+
+ if(priv->oldFocusable)
+ {
+ g_object_remove_weak_pointer(G_OBJECT(priv->oldFocusable), &priv->oldFocusable);
+ priv->oldFocusable=NULL;
+ }
+
+ if(priv->itemsContainer)
+ {
+ clutter_actor_destroy(priv->itemsContainer);
+ priv->itemsContainer=NULL;
+ }
+
+ if(priv->focusManager)
+ {
+ xfdashboard_focus_manager_unregister(priv->focusManager, XFDASHBOARD_FOCUSABLE(self));
+ g_object_unref(priv->focusManager);
+ priv->focusManager=NULL;
+ }
+
+ if(priv->windowTracker)
+ {
+ g_object_unref(priv->windowTracker);
+ priv->windowTracker=NULL;
+ }
+
+ if(priv->stage)
+ {
+ priv->stage=NULL;
+ }
+
+ /* Call parent's class dispose method */
+ G_OBJECT_CLASS(xfdashboard_popup_menu_parent_class)->dispose(inObject);
+}
+
+/* Set/get properties */
+static void _xfdashboard_popup_menu_set_property(GObject *inObject,
+ guint inPropID,
+ const GValue *inValue,
+ GParamSpec *inSpec)
+{
+ XfdashboardPopupMenu *self=XFDASHBOARD_POPUP_MENU(inObject);
+
+ switch(inPropID)
+ {
+ case PROP_DESTROY_ON_CANCEL:
+ xfdashboard_popup_menu_set_destroy_on_cancel(self, g_value_get_boolean(inValue));
+ break;
+
+ case PROP_SOURCE:
+ xfdashboard_popup_menu_set_source(self, CLUTTER_ACTOR(g_value_get_object(inValue)));
+ break;
+
+ case PROP_SHOW_TITLE:
+ xfdashboard_popup_menu_set_show_title(self, g_value_get_boolean(inValue));
+ break;
+
+ case PROP_TITLE:
+ xfdashboard_popup_menu_set_title(self, g_value_get_string(inValue));
+ break;
+
+ case PROP_SHOW_TITLE_ICON:
+ xfdashboard_popup_menu_set_show_title_icon(self, g_value_get_boolean(inValue));
+ break;
+
+ case PROP_TITLE_ICON_NAME:
+ xfdashboard_popup_menu_set_title_icon_name(self, g_value_get_string(inValue));
+ break;
+
+ case PROP_TITLE_GICON:
+ xfdashboard_popup_menu_set_title_gicon(self, G_ICON(g_value_get_object(inValue)));
+ break;
+
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID(inObject, inPropID, inSpec);
+ break;
+ }
+}
+
+static void _xfdashboard_popup_menu_get_property(GObject *inObject,
+ guint inPropID,
+ GValue *outValue,
+ GParamSpec *inSpec)
+{
+ XfdashboardPopupMenu *self=XFDASHBOARD_POPUP_MENU(inObject);
+ XfdashboardPopupMenuPrivate *priv=self->priv;
+
+ switch(inPropID)
+ {
+ case PROP_DESTROY_ON_CANCEL:
+ g_value_set_boolean(outValue, priv->destroyOnCancel);
+ break;
+
+ case PROP_SOURCE:
+ g_value_set_object(outValue, priv->source);
+ break;
+
+ case PROP_SHOW_TITLE:
+ g_value_set_boolean(outValue, priv->showTitle);
+ break;
+
+ case PROP_TITLE:
+ g_value_set_string(outValue, xfdashboard_popup_menu_get_title(self));
+ break;
+
+ case PROP_SHOW_TITLE_ICON:
+ g_value_set_boolean(outValue, priv->showTitleIcon);
+ break;
+
+ case PROP_TITLE_ICON_NAME:
+ g_value_set_string(outValue, xfdashboard_popup_menu_get_title_icon_name(self));
+ break;
+
+ case PROP_TITLE_GICON:
+ g_value_set_object(outValue, xfdashboard_popup_menu_get_title_gicon(self));
+ break;
+
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID(inObject, inPropID, inSpec);
+ break;
+ }
+}
+
+/* Class initialization
+ * Override functions in parent classes and define properties
+ * and signals
+ */
+static void xfdashboard_popup_menu_class_init(XfdashboardPopupMenuClass *klass)
+{
+ XfdashboardActorClass *actorClass=XFDASHBOARD_ACTOR_CLASS(klass);
+ ClutterActorClass *clutterActorClass=CLUTTER_ACTOR_CLASS(klass);
+ GObjectClass *gobjectClass=G_OBJECT_CLASS(klass);
+
+ /* Override functions */
+ gobjectClass->dispose=_xfdashboard_popup_menu_dispose;
+ gobjectClass->set_property=_xfdashboard_popup_menu_set_property;
+ gobjectClass->get_property=_xfdashboard_popup_menu_get_property;
+
+ clutterActorClass->allocate=_xfdashboard_popup_menu_allocate;
+
+ /* Set up private structure */
+ g_type_class_add_private(klass, sizeof(XfdashboardPopupMenuPrivate));
+
+ /* Set up quark for child meta used at menu item actors */
+ _xfdashboard_popup_menu_items_container_child_meta_quark=g_quark_from_static_string("xfdashboard-popup-menu-items-container-child-data");
+
+ /* Define properties */
+ /**
+ * XfdashboardPopupMenu:destroy-on-cancel:
+ *
+ * A flag indicating if this pop-up menu should be destroyed automatically
+ * when it is cancelled.
+ */
+ XfdashboardPopupMenuProperties[PROP_DESTROY_ON_CANCEL]=
+ g_param_spec_boolean("destroy-on-cancel",
+ _("Destroy on cancel"),
+ _("Flag indicating this pop-up menu should be destroyed automatically when it is cancelled"),
+ FALSE,
+ G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS);
+
+ /**
+ * XfdashboardPopupMenu:source:
+ *
+ * The #ClutterActor on which this pop-up menu depends on. If this actor is
+ * destroyed then this pop-up menu is cancelled when active.
+ */
+ XfdashboardPopupMenuProperties[PROP_SOURCE]=
+ g_param_spec_object("source",
+ _("Source"),
+ _("The object on which this pop-up menu depends on"),
+ CLUTTER_TYPE_ACTOR,
+ G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS);
+
+ /**
+ * XfdashboardPopupMenu:show-title:
+ *
+ * A flag indicating if the title of this pop-up menu should be shown.
+ */
+ XfdashboardPopupMenuProperties[PROP_SHOW_TITLE]=
+ g_param_spec_boolean("show-title",
+ _("Show title"),
+ _("Flag indicating if the title of this pop-up menu should be shown"),
+ FALSE,
+ G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS);
+
+ /**
+ * XfdashboardPopupMenu:title:
+ *
+ * A string containing the title of this pop-up menu.
+ */
+ XfdashboardPopupMenuProperties[PROP_TITLE]=
+ g_param_spec_string("title",
+ _("Title"),
+ _("Title of pop-up menu"),
+ N_(""),
+ G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS);
+
+ /**
+ * XfdashboardPopupMenu:show-title-icon:
+ *
+ * A flag indicating if the icon of the title of this pop-up menu should be shown.
+ */
+ XfdashboardPopupMenuProperties[PROP_SHOW_TITLE_ICON]=
+ g_param_spec_boolean("show-title-icon",
+ _("Show title icon"),
+ _("Flag indicating if the icon of title of this pop-up menu should be shown"),
+ FALSE,
+ G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS);
+
+ /**
+ * XfdashboardPopupMenu:title-icon-name:
+ *
+ * A string containing the stock icon name or file name for the icon to use
+ * at title of this pop-up menu.
+ */
+ XfdashboardPopupMenuProperties[PROP_TITLE_ICON_NAME]=
+ g_param_spec_string("title-icon-name",
+ _("Title icon name"),
+ _("Themed icon name or file name of icon used in title"),
+ N_(""),
+ G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS);
+
+ /**
+ * XfdashboardPopupMenu:title-gicon:
+ *
+ * A #GIcon containing the icon image to use at title of this pop-up menu.
+ */
+ XfdashboardPopupMenuProperties[PROP_TITLE_GICON]=
+ g_param_spec_object("title-gicon",
+ _("Title GIcon"),
+ _("The GIcon of icon used in title"),
+ G_TYPE_ICON,
+ G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS);
+
+ g_object_class_install_properties(gobjectClass, PROP_LAST, XfdashboardPopupMenuProperties);
+
+ /* Define stylable properties */
+ xfdashboard_actor_install_stylable_property(actorClass, XfdashboardPopupMenuProperties[PROP_SHOW_TITLE]);
+ xfdashboard_actor_install_stylable_property(actorClass, XfdashboardPopupMenuProperties[PROP_SHOW_TITLE_ICON]);
+
+ /* Define signals */
+ /**
+ * XfdashboardPopupMenu::activated:
+ * @self: The pop-up menu which was activated
+ *
+ * The ::activated signal is emitted when the pop-up menu is shown and the
+ * user can perform an action by selecting an item.
+ */
+ XfdashboardPopupMenuSignals[SIGNAL_ACTIVATED]=
+ g_signal_new("activated",
+ G_TYPE_FROM_CLASS(klass),
+ G_SIGNAL_RUN_LAST,
+ G_STRUCT_OFFSET(XfdashboardPopupMenuClass, activated),
+ NULL,
+ NULL,
+ g_cclosure_marshal_VOID__VOID,
+ G_TYPE_NONE,
+ 0);
+
+ /**
+ * XfdashboardPopupMenu::cancelled:
+ * @self: The pop-up menu which was cancelled
+ *
+ * The ::cancelled signal is emitted when the pop-up menu is hidden. This
+ * signal is emitted regardless the user has chosen an item and perform the
+ * associated action or not.
+ *
+ * Note: This signal does not indicate if a selection was made or not.
+ */
+ XfdashboardPopupMenuSignals[SIGNAL_CANCELLED]=
+ g_signal_new("cancelled",
+ G_TYPE_FROM_CLASS(klass),
+ G_SIGNAL_RUN_LAST,
+ G_STRUCT_OFFSET(XfdashboardPopupMenuClass, cancelled),
+ NULL,
+ NULL,
+ g_cclosure_marshal_VOID__VOID,
+ G_TYPE_NONE,
+ 0);
+
+ /**
+ * XfdashboardPopupMenu::item-activated:
+ * @self: The pop-up menu containing the activated menu item
+ * @inMenuItem: The menu item which was activated
+ *
+ * The ::item-activated signal is emitted when a menu item at pop-up menu
+ * was activated either by key-press or by clicking on it.
+ */
+ XfdashboardPopupMenuSignals[SIGNAL_ITEM_ACTIVATED]=
+ g_signal_new("item-activated",
+ G_TYPE_FROM_CLASS(klass),
+ G_SIGNAL_RUN_LAST,
+ G_STRUCT_OFFSET(XfdashboardPopupMenuClass, item_activated),
+ NULL,
+ NULL,
+ g_cclosure_marshal_VOID__OBJECT,
+ G_TYPE_NONE,
+ 1,
+ CLUTTER_TYPE_ACTOR);
+
+ /**
+ * XfdashboardPopupMenu::item-added:
+ * @self: The pop-up menu containing the activated menu item
+ * @inMenuItem: The menu item which was added
+ *
+ * The ::item-added signal is emitted when a menu item was added to pop-up menu.
+ */
+ XfdashboardPopupMenuSignals[SIGNAL_ITEM_ADDED]=
+ g_signal_new("item-added",
+ G_TYPE_FROM_CLASS(klass),
+ G_SIGNAL_RUN_LAST,
+ G_STRUCT_OFFSET(XfdashboardPopupMenuClass, item_added),
+ NULL,
+ NULL,
+ g_cclosure_marshal_VOID__OBJECT,
+ G_TYPE_NONE,
+ 1,
+ CLUTTER_TYPE_ACTOR);
+
+ /**
+ * XfdashboardPopupMenu::item-removed:
+ * @self: The pop-up menu containing the activated menu item
+ * @inMenuItem: The menu item which was added
+ *
+ * The ::item-added signal is emitted when a menu item was added to pop-up menu.
+ */
+ XfdashboardPopupMenuSignals[SIGNAL_ITEM_REMOVED]=
+ g_signal_new("item-removed",
+ G_TYPE_FROM_CLASS(klass),
+ G_SIGNAL_RUN_LAST,
+ G_STRUCT_OFFSET(XfdashboardPopupMenuClass, item_removed),
+ NULL,
+ NULL,
+ g_cclosure_marshal_VOID__OBJECT,
+ G_TYPE_NONE,
+ 1,
+ CLUTTER_TYPE_ACTOR);
+}
+
+/* Object initialization
+ * Create private structure and set up default values
+ */
+static void xfdashboard_popup_menu_init(XfdashboardPopupMenu *self)
+{
+ XfdashboardPopupMenuPrivate *priv;
+ ClutterLayoutManager *layout;
+
+ priv=self->priv=XFDASHBOARD_POPUP_MENU_GET_PRIVATE(self);
+
+ /* Set up default values */
+ priv->destroyOnCancel=FALSE;
+ priv->source=NULL;
+ priv->showTitle=FALSE;
+ priv->showTitleIcon=FALSE;
+ priv->isActive=FALSE;
+ priv->title=NULL;
+ priv->itemsContainer=NULL;
+ priv->windowTracker=xfdashboard_window_tracker_get_default();
+ priv->focusManager=xfdashboard_focus_manager_get_default();
+ priv->oldFocusable=NULL;
+ priv->selectedItem=NULL;
+ priv->stage=NULL;
+ priv->capturedEventSignalID=0;
+ priv->sourceDestroySignalID=0;
+
+ /* This actor is react on events */
+ clutter_actor_set_reactive(CLUTTER_ACTOR(self), TRUE);
+
+ /* Set up title actor */
+ priv->title=xfdashboard_button_new();
+ xfdashboard_button_set_style(XFDASHBOARD_BUTTON(priv->title), XFDASHBOARD_BUTTON_STYLE_TEXT);
+ xfdashboard_button_set_text(XFDASHBOARD_BUTTON(priv->title), NULL);
+ clutter_actor_set_x_expand(priv->title, TRUE);
+ clutter_actor_set_y_expand(priv->title, TRUE);
+ clutter_actor_hide(priv->title);
+ xfdashboard_stylable_add_class(XFDASHBOARD_STYLABLE(priv->title), "popup-menu-title");
+
+ /* Set up items container which will hold all menu items */
+ layout=xfdashboard_box_layout_new();
+ clutter_box_layout_set_orientation(CLUTTER_BOX_LAYOUT(layout), CLUTTER_ORIENTATION_VERTICAL);
+ clutter_box_layout_set_homogeneous(CLUTTER_BOX_LAYOUT(layout), TRUE);
+
+ priv->itemsContainer=xfdashboard_actor_new();
+ clutter_actor_set_x_expand(priv->itemsContainer, TRUE);
+ clutter_actor_set_y_expand(priv->itemsContainer, TRUE);
+ clutter_actor_set_layout_manager(priv->itemsContainer, layout);
+
+ /* Set up this actor */
+ layout=xfdashboard_box_layout_new();
+ clutter_box_layout_set_orientation(CLUTTER_BOX_LAYOUT(layout), CLUTTER_ORIENTATION_VERTICAL);
+ clutter_actor_set_layout_manager(CLUTTER_ACTOR(self), layout);
+
+ clutter_actor_add_child(CLUTTER_ACTOR(self), priv->title);
+ clutter_actor_add_child(CLUTTER_ACTOR(self), priv->itemsContainer);
+ xfdashboard_stylable_add_class(XFDASHBOARD_STYLABLE(self), "popup-menu");
+
+ /* Register this actor at focus manager but ensure that this actor is
+ * not focusable initially */
+ xfdashboard_actor_set_can_focus(XFDASHBOARD_ACTOR(self), FALSE);
+ xfdashboard_focus_manager_register(priv->focusManager, XFDASHBOARD_FOCUSABLE(self));
+
+ /* Add popup menu to stage */
+ priv->stage=xfdashboard_application_get_stage(xfdashboard_application_get_default());
+ clutter_actor_insert_child_above(CLUTTER_ACTOR(priv->stage), CLUTTER_ACTOR(self), NULL);
+}
+
+/* IMPLEMENTATION: Public API */
+
+/**
+ * xfdashboard_popup_menu_new:
+ *
+ * Creates a new #XfdashboardPopupMenu actor
+ *
+ * Return value: The newly created #XfdashboardPopupMenu
+ */
+ClutterActor* xfdashboard_popup_menu_new(void)
+{
+ return(g_object_new(XFDASHBOARD_TYPE_POPUP_MENU, NULL));
+}
+
+/**
+ * xfdashboard_popup_menu_new_for_source:
+ * @inSource: A #ClutterActor which this pop-up menu should depend on
+ *
+ * Creates a new #XfdashboardPopupMenu actor which depends on actor @inSource.
+ * When the actor @inSource is destroyed and the pop-up menu is active then it
+ * will be cancelled automatically.
+ *
+ * Return value: The newly created #XfdashboardPopupMenu
+ */
+ClutterActor* xfdashboard_popup_menu_new_for_source(ClutterActor *inSource)
+{
+ g_return_val_if_fail(CLUTTER_IS_ACTOR(inSource), NULL);
+
+ return(g_object_new(XFDASHBOARD_TYPE_POPUP_MENU,
+ "source", inSource,
+ NULL));
+}
+
+/**
+ * xfdashboard_popup_menu_get_destroy_on_cancel:
+ * @self: A #XfdashboardPopupMenu
+ *
+ * Retrieves the automatic destruction mode of @self. If automatic destruction mode
+ * is %TRUE then the pop-up menu will be destroy by calling clutter_actor_destroy()
+ * when it is cancelled, e.g. by calling xfdashboard_popup_menu_cancel().
+ *
+ * Return value: Returns %TRUE if automatic destruction mode is enabled, otherwise
+ * %FALSE.
+ */
+gboolean xfdashboard_popup_menu_get_destroy_on_cancel(XfdashboardPopupMenu *self)
+{
+ g_return_val_if_fail(XFDASHBOARD_IS_POPUP_MENU(self), FALSE);
+
+ return(self->priv->destroyOnCancel);
+}
+
+/**
+ * xfdashboard_popup_menu_set_destroy_on_cancel:
+ * @self: A #XfdashboardPopupMenu
+ * @inDestroyOnCancel: The automatic destruction mode to set at @self
+ *
+ * Sets the automatic destruction mode of @self. If @inDestroyOnCancel is set to
+ * %TRUE then the pop-up menu will automatically destroyed by calling clutter_actor_destroy()
+ * when it is cancelled, e.g. by calling xfdashboard_popup_menu_cancel().
+ */
+void xfdashboard_popup_menu_set_destroy_on_cancel(XfdashboardPopupMenu *self, gboolean inDestroyOnCancel)
+{
+ XfdashboardPopupMenuPrivate *priv;
+
+ g_return_if_fail(XFDASHBOARD_IS_POPUP_MENU(self));
+
+ priv=self->priv;
+
+ /* Set value if changed */
+ if(priv->destroyOnCancel!=inDestroyOnCancel)
+ {
+ /* Set value */
+ priv->destroyOnCancel=inDestroyOnCancel;
+
+ /* Notify about property change */
+ g_object_notify_by_pspec(G_OBJECT(self), XfdashboardPopupMenuProperties[PROP_DESTROY_ON_CANCEL]);
+ }
+}
+
+/**
+ * xfdashboard_popup_menu_get_source:
+ * @self: A #XfdashboardPopupMenu
+ *
+ * Retrieves the source actor to @inSource which the pop-up menu at @self depends on.
+ *
+ * Return value: (transfer none): Returns #ClutterActor which the pop-up menu or
+ * %NULL if no source actor is set.
+ */
+ClutterActor* xfdashboard_popup_menu_get_source(XfdashboardPopupMenu *self)
+{
+ g_return_val_if_fail(XFDASHBOARD_IS_POPUP_MENU(self), NULL);
+
+ return(self->priv->source);
+}
+
+/**
+ * xfdashboard_popup_menu_set_source:
+ * @self: A #XfdashboardPopupMenu
+ * @inSource: A #ClutterActor which this pop-up menu should depend on or %NULL
+ * if it should not depend on any actor
+ *
+ * Sets the source actor to @inSource which the pop-up menu at @self depends on.
+ * When the actor @inSource is destroyed and the pop-up menu at @self is active
+ * then it will be cancelled automatically.
+ *
+ * In addition the CSS class "popup-menu-source-SOURCE_CLASS_NAME" will be set
+ * on pop-up menu at @self, e.g. if source is of type ClutterActor the CSS class
+ * "popup-menu-source-ClutterActor" will be set.
+ */
+void xfdashboard_popup_menu_set_source(XfdashboardPopupMenu *self, ClutterActor *inSource)
+{
+ XfdashboardPopupMenuPrivate *priv;
+ gchar *cssClass;
+
+ g_return_if_fail(XFDASHBOARD_IS_POPUP_MENU(self));
+ g_return_if_fail(!inSource || CLUTTER_IS_ACTOR(inSource));
+
+ priv=self->priv;
+
+ /* Set value if changed */
+ if(priv->source!=inSource)
+ {
+ /* Release old source if set */
+ if(priv->source)
+ {
+ /* Disconnect signal handler */
+ g_signal_handler_disconnect(priv->source, priv->sourceDestroySignalID);
+ priv->sourceDestroySignalID=0;
+
+ /* Remove style */
+ cssClass=g_strdup_printf("popup-menu-source-%s", G_OBJECT_TYPE_NAME(priv->source));
+ xfdashboard_stylable_remove_class(XFDASHBOARD_STYLABLE(self), cssClass);
+ g_free(cssClass);
+
+ /* Release source */
+ g_object_remove_weak_pointer(G_OBJECT(priv->source), (gpointer*)&priv->source);
+ priv->source=NULL;
+ }
+
+ /* Set value */
+ if(inSource)
+ {
+ /* Set up source */
+ priv->source=inSource;
+ g_object_add_weak_pointer(G_OBJECT(priv->source), (gpointer*)&priv->source);
+
+ /* Add style */
+ cssClass=g_strdup_printf("popup-menu-source-%s", G_OBJECT_TYPE_NAME(priv->source));
+ xfdashboard_stylable_add_class(XFDASHBOARD_STYLABLE(self), cssClass);
+ g_free(cssClass);
+
+ /* Connect signal handler */
+ priv->sourceDestroySignalID=g_signal_connect_swapped(priv->source,
+ "destroy",
+ G_CALLBACK(_xfdashboard_popup_menu_on_source_destroy),
+ self);
+ }
+
+ /* Notify about property change */
+ g_object_notify_by_pspec(G_OBJECT(self), XfdashboardPopupMenuProperties[PROP_SOURCE]);
+ }
+}
+
+/**
+ * xfdashboard_popup_menu_get_show_title:
+ * @self: A #XfdashboardPopupMenu
+ *
+ * Retrieves the state if the title of pop-up menu at @self should be shown or not.
+ *
+ * Return value: Returns %TRUE if title of pop-up menu should be shown and
+ * %FALSE if not.
+ */
+gboolean xfdashboard_popup_menu_get_show_title(XfdashboardPopupMenu *self)
+{
+ g_return_val_if_fail(XFDASHBOARD_IS_POPUP_MENU(self), FALSE);
+
+ return(self->priv->showTitle);
+}
+
+/**
+ * xfdashboard_popup_menu_set_show_title:
+ * @self: A #XfdashboardPopupMenu
+ * @inShowTitle: Flag indicating if the title of pop-up menu should be shown.
+ *
+ * If @inShowTitle is %TRUE then the title of the pop-up menu at @self will be
+ * shown. If @inShowTitle is %FALSE it will be hidden.
+ */
+void xfdashboard_popup_menu_set_show_title(XfdashboardPopupMenu *self, gboolean inShowTitle)
+{
+ XfdashboardPopupMenuPrivate *priv;
+
+ g_return_if_fail(XFDASHBOARD_IS_POPUP_MENU(self));
+
+ priv=self->priv;
+
+ /* Set value if changed */
+ if(priv->showTitle!=inShowTitle)
+ {
+ /* Set value */
+ priv->showTitle=inShowTitle;
+
+ /* Update visibility state of title actor */
+ _xfdashboard_popup_menu_update_title_actors_visibility(self);
+
+ /* Notify about property change */
+ g_object_notify_by_pspec(G_OBJECT(self), XfdashboardPopupMenuProperties[PROP_SHOW_TITLE]);
+ }
+}
+
+/**
+ * xfdashboard_popup_menu_get_title:
+ * @self: A #XfdashboardPopupMenu
+ *
+ * Retrieves the title of pop-up menu.
+ *
+ * Return value: Returns string with title of pop-up menu.
+ */
+const gchar* xfdashboard_popup_menu_get_title(XfdashboardPopupMenu *self)
+{
+ g_return_val_if_fail(XFDASHBOARD_IS_POPUP_MENU(self), NULL);
+
+ return(xfdashboard_button_get_text(XFDASHBOARD_BUTTON(self->priv->title)));
+}
+
+/**
+ * xfdashboard_popup_menu_set_title:
+ * @self: A #XfdashboardPopupMenu
+ * @inMarkupTitle: The title to set
+ *
+ * Sets @inMarkupTitle as title of pop-up menu at @self. The title string can
+ * contain markup.
+ */
+void xfdashboard_popup_menu_set_title(XfdashboardPopupMenu *self, const gchar *inMarkupTitle)
+{
+ XfdashboardPopupMenuPrivate *priv;
+
+ g_return_if_fail(XFDASHBOARD_IS_POPUP_MENU(self));
+ g_return_if_fail(inMarkupTitle);
+
+ priv=self->priv;
+
+ /* Set value if changed */
+ if(g_strcmp0(xfdashboard_button_get_text(XFDASHBOARD_BUTTON(priv->title)), inMarkupTitle)!=0)
+ {
+ /* Set value */
+ xfdashboard_button_set_text(XFDASHBOARD_BUTTON(priv->title), inMarkupTitle);
+
+ /* Notify about property change */
+ g_object_notify_by_pspec(G_OBJECT(self), XfdashboardPopupMenuProperties[PROP_TITLE]);
+ }
+}
+
+/**
+ * xfdashboard_popup_menu_get_show_title_icon:
+ * @self: A #XfdashboardPopupMenu
+ *
+ * Retrieves the state if the icon of title of pop-up menu at @self should be
+ * shown or not.
+ *
+ * Return value: Returns %TRUE if icon of title of pop-up menu should be shown
+ * and %FALSE if not.
+ */
+gboolean xfdashboard_popup_menu_get_show_title_icon(XfdashboardPopupMenu *self)
+{
+ g_return_val_if_fail(XFDASHBOARD_IS_POPUP_MENU(self), FALSE);
+
+ return(self->priv->showTitleIcon);
+}
+
+/**
+ * xfdashboard_popup_menu_set_show_title_icon:
+ * @self: A #XfdashboardPopupMenu
+ * @inShowTitle: Flag indicating if the icon of title of pop-up menu should be shown.
+ *
+ * If @inShowTitle is %TRUE then the icon of title of the pop-up menu at @self will be
+ * shown. If @inShowTitle is %FALSE it will be hidden.
+ */
+void xfdashboard_popup_menu_set_show_title_icon(XfdashboardPopupMenu *self, gboolean inShowTitleIcon)
+{
+ XfdashboardPopupMenuPrivate *priv;
+
+ g_return_if_fail(XFDASHBOARD_IS_POPUP_MENU(self));
+
+ priv=self->priv;
+
+ /* Set value if changed */
+ if(priv->showTitleIcon!=inShowTitleIcon)
+ {
+ /* Set value */
+ priv->showTitleIcon=inShowTitleIcon;
+
+ /* Update visibility state of title actor */
+ _xfdashboard_popup_menu_update_title_actors_visibility(self);
+
+ /* Notify about property change */
+ g_object_notify_by_pspec(G_OBJECT(self), XfdashboardPopupMenuProperties[PROP_SHOW_TITLE_ICON]);
+ }
+}
+
+/**
+ * xfdashboard_popup_menu_get_title_icon_name:
+ * @self: A #XfdashboardPopupMenu
+ *
+ * Retrieves the stock icon name or file name of title icon of pop-up menu at @self.
+ *
+ * Return value: Returns string with icon name or file name of pop-up menu's title.
+ */
+const gchar* xfdashboard_popup_menu_get_title_icon_name(XfdashboardPopupMenu *self)
+{
+ g_return_val_if_fail(XFDASHBOARD_IS_POPUP_MENU(self), NULL);
+
+ return(xfdashboard_button_get_icon_name(XFDASHBOARD_BUTTON(self->priv->title)));
+}
+
+/**
+ * xfdashboard_popup_menu_set_title_icon_name:
+ * @self: A #XfdashboardPopupMenu
+ * @inIconName: A string containing the stock icon name or file name for the icon
+ * to be place in the toogle button
+ *
+ * Sets the icon in title to icon at @inIconName of pop-up menu at @self. If set to
+ * %NULL the title icon is hidden.
+ */
+void xfdashboard_popup_menu_set_title_icon_name(XfdashboardPopupMenu *self, const gchar *inIconName)
+{
+ XfdashboardPopupMenuPrivate *priv;
+
+ g_return_if_fail(XFDASHBOARD_IS_POPUP_MENU(self));
+ g_return_if_fail(inIconName);
+
+ priv=self->priv;
+
+ /* Set value if changed */
+ if(g_strcmp0(xfdashboard_button_get_icon_name(XFDASHBOARD_BUTTON(priv->title)), inIconName)!=0)
+ {
+ /* Set value */
+ xfdashboard_button_set_icon_name(XFDASHBOARD_BUTTON(priv->title), inIconName);
+
+ /* Notify about property change */
+ g_object_notify_by_pspec(G_OBJECT(self), XfdashboardPopupMenuProperties[PROP_TITLE_ICON_NAME]);
+ }
+}
+
+/**
+ * xfdashboard_popup_menu_get_title_gicon:
+ * @self: A #XfdashboardPopupMenu
+ *
+ * Retrieves the title's icon of type #GIcon of pop-up menu at @self.
+ *
+ * Return value: Returns #GIcon of pop-up menu's title.
+ */
+GIcon* xfdashboard_popup_menu_get_title_gicon(XfdashboardPopupMenu *self)
+{
+ g_return_val_if_fail(XFDASHBOARD_IS_POPUP_MENU(self), NULL);
+
+ return(xfdashboard_button_get_gicon(XFDASHBOARD_BUTTON(self->priv->title)));
+}
+
+/**
+ * xfdashboard_popup_menu_set_title_gicon:
+ * @self: A #XfdashboardPopupMenu
+ * @inIcon: A #GIcon containing the icon image
+ *
+ * Sets the icon in title to icon at @inIcon of pop-up menu at @self. If set to
+ * %NULL the title icon is hidden.
+ */
+void xfdashboard_popup_menu_set_title_gicon(XfdashboardPopupMenu *self, GIcon *inIcon)
+{
+ XfdashboardPopupMenuPrivate *priv;
+ GIcon *icon;
+
+ g_return_if_fail(XFDASHBOARD_IS_POPUP_MENU(self));
+ g_return_if_fail(G_IS_ICON(inIcon));
+
+ priv=self->priv;
+
+ /* Set value if changed */
+ icon=xfdashboard_button_get_gicon(XFDASHBOARD_BUTTON(priv->title));
+ if(icon!=inIcon ||
+ (icon && inIcon && !g_icon_equal(icon, inIcon)))
+ {
+ /* Set value */
+ xfdashboard_button_set_gicon(XFDASHBOARD_BUTTON(priv->title), inIcon);
+
+ /* Notify about property change */
+ g_object_notify_by_pspec(G_OBJECT(self), XfdashboardPopupMenuProperties[PROP_TITLE_GICON]);
+ }
+}
+
+/**
+ * xfdashboard_popup_menu_add_item:
+ * @self: A #XfdashboardPopupMenu
+ * @inMenuItem: A #ClutterActor used as menu item
+ * @inCallback: The callback handler to call when this menu item was activated
+ * @inUserData: The user data to pass to callback handler when this menu item
+ * was activated.
+ *
+ * Adds the actor @inMenuItem to end of pop-up menu. When the pop-up menu at @self
+ * is displayed and this menu item is activated then the callback handler @inCallback
+ * is called and the user data @inUserData is passed to that callback handler.
+ *
+ * If menu item actor implements the #XfdashboardStylable interface the CSS class
+ * popup-menu-item will be added.
+ *
+ * Return value: Returns index where item was inserted at or -1 if it failed.
+ */
+gint xfdashboard_popup_menu_add_item(XfdashboardPopupMenu *self,
+ ClutterActor *inMenuItem,
+ XfdashboardPopupMenuItemActivateCallback inCallback,
+ gpointer inUserData)
+{
+ return(xfdashboard_popup_menu_insert_item(self, inMenuItem, -1, inCallback, inUserData));
+}
+
+/**
+ * xfdashboard_popup_menu_insert_item:
+ * @self: A #XfdashboardPopupMenu
+ * @inMenuItem: A #ClutterActor used as menu item
+ * @inIndex: The position where to insert this item at
+ * @inCallback: The callback handler to call when this menu item was activated
+ * @inUserData: The user data to pass to callback handler when this menu item
+ * was activated.
+ *
+ * Inserts the actor @inMenuItem at position @inIndex into pop-up menu. When the
+ * pop-up menu at @self is displayed and this menu item is activated then the
+ * callback handler @inCallback is called and the user data @inUserData is passed
+ * to that callback handler.
+ *
+ * If position @inIndex is greater than the number of menu items in @self or is
+ * less than 0, then the menu item actor @inMenuItem is added to end to
+ * pop-up menu.
+ *
+ * If menu item actor implements the #XfdashboardStylable interface the CSS class
+ * popup-menu-item will be added.
+ *
+ * Return value: Returns index where item was inserted at or -1 if it failed.
+ */
+gint xfdashboard_popup_menu_insert_item(XfdashboardPopupMenu *self,
+ ClutterActor *inMenuItem,
+ gint inIndex,
+ XfdashboardPopupMenuItemActivateCallback inCallback,
+ gpointer inUserData)
+{
+ XfdashboardPopupMenuPrivate *priv;
+ XfdashboardPopupMenuItemMeta *meta;
+
+ g_return_val_if_fail(XFDASHBOARD_IS_POPUP_MENU(self), -1);
+ g_return_val_if_fail(CLUTTER_IS_ACTOR(inMenuItem), -1);
+ g_return_val_if_fail(clutter_actor_get_parent(inMenuItem)==NULL, -1);
+
+ priv=self->priv;
+
+ /* Insert menu item actor to container at requested position */
+ clutter_actor_insert_child_at_index(priv->itemsContainer, inMenuItem, inIndex);
+
+ /* Create and add meta for menu item actor */
+ meta=xfdashboard_popup_menu_item_meta_new(self, inMenuItem, inCallback, inUserData);
+ g_object_set_qdata_full(G_OBJECT(inMenuItem),
+ _xfdashboard_popup_menu_items_container_child_meta_quark,
+ meta,
+ (GDestroyNotify)g_object_unref);
+
+ g_signal_connect_swapped(meta,
+ "activated",
+ G_CALLBACK(_xfdashboard_popup_menu_on_meta_activated),
+ self);
+
+ /* Emit signal */
+ g_signal_emit(self, XfdashboardPopupMenuSignals[SIGNAL_ITEM_ADDED], 0, inMenuItem);
+
+ /* Get index where menu item actor was inserted at */
+ return(xfdashboard_popup_menu_get_item_index(self, inMenuItem));
+}
+
+/**
+ * xfdashboard_popup_menu_move_item:
+ * @self: A #XfdashboardPopupMenu
+ * @inMenuItem: A #ClutterActor menu item to move
+ * @inIndex: The position where to insert this item at
+ *
+ * Moves the actor @inMenuItem to position @inIndex at pop-up menu @self. If position
+ * @inIndex is greater than the number of menu items in @self or is less than 0,
+ * then the menu item actor @inMenuItem is added to end to pop-up menu.
+ *
+ * Return value: Returns %TRUE if moving menu item was successful, otherwise %FALSE.
+ */
+gboolean xfdashboard_popup_menu_move_item(XfdashboardPopupMenu *self,
+ ClutterActor *inMenuItem,
+ gint inIndex)
+{
+ XfdashboardPopupMenuPrivate *priv;
+
+ g_return_val_if_fail(XFDASHBOARD_IS_POPUP_MENU(self), FALSE);
+ g_return_val_if_fail(CLUTTER_IS_ACTOR(inMenuItem), FALSE);
+
+ priv=self->priv;
+
+ /* Check if menu item is really part of this pop-up menu */
+ if(!_xfdashboard_popup_menu_contains_menu_item(self, inMenuItem))
+ {
+ g_warning(_("%s is not a child of %s and cannot be moved"),
+ G_OBJECT_TYPE_NAME(inMenuItem),
+ G_OBJECT_TYPE_NAME(self));
+ return(FALSE);
+ }
+
+ /* Move menu item actor to new position */
+ g_object_ref(inMenuItem);
+ clutter_actor_remove_child(priv->itemsContainer, inMenuItem);
+ clutter_actor_insert_child_at_index(priv->itemsContainer, inMenuItem, inIndex);
+ g_object_unref(inMenuItem);
+
+ /* If we get here moving menu item actor was successful */
+ return(TRUE);
+}
+
+/**
+ * xfdashboard_popup_menu_get_item:
+ * @self: A #XfdashboardPopupMenu
+ * @inIndex: The position whose menu item to get
+ *
+ * Returns the menu item actor at position @inIndex at pop-up menu @self.
+ *
+ * Return value: Returns #ClutterActor of the menu item at position @inIndex or
+ * %NULL in case of errors or if index is out of range that means it is greater
+ * than the number of menu items in @self or is less than 0.
+ */
+ClutterActor* xfdashboard_popup_menu_get_item(XfdashboardPopupMenu *self, gint inIndex)
+{
+ XfdashboardPopupMenuPrivate *priv;
+
+ g_return_val_if_fail(XFDASHBOARD_IS_POPUP_MENU(self), NULL);
+ g_return_val_if_fail(inIndex>=0 && inIndex<clutter_actor_get_n_children(self->priv->itemsContainer), NULL);
+
+ priv=self->priv;
+
+ /* Get and return child at requested position at items container */
+ return(clutter_actor_get_child_at_index(priv->itemsContainer, inIndex));
+}
+
+/**
+ * xfdashboard_popup_menu_get_item_index:
+ * @self: A #XfdashboardPopupMenu
+ * @inMenuItem: The #ClutterActor menu item whose index to lookup
+ *
+ * Returns the position for menu item actor @inMenuItem of pop-up menu @self.
+ *
+ * Return value: Returns the position of the menu item or -1 in case of errors
+ * or if pop-up menu does not have the menu item
+ */
+gint xfdashboard_popup_menu_get_item_index(XfdashboardPopupMenu *self, ClutterActor *inMenuItem)
+{
+ XfdashboardPopupMenuPrivate *priv;
+ gint index;
+ ClutterActorIter iter;
+ ClutterActor *child;
+
+ g_return_val_if_fail(XFDASHBOARD_IS_POPUP_MENU(self), -1);
+ g_return_val_if_fail(CLUTTER_IS_ACTOR(inMenuItem), -1);
+
+ priv=self->priv;
+
+ /* Iterate through menu item and return the index if the requested one was found */
+ index=0;
+
+ clutter_actor_iter_init(&iter, priv->itemsContainer);
+ while(clutter_actor_iter_next(&iter, &child))
+ {
+ /* If this child is the one we are looking for return index now */
+ if(child==inMenuItem) return(index);
+
+ /* Increase index */
+ index++;
+ }
+
+ /* If we get here we did not find the requested menu item */
+ return(-1);
+}
+
+/**
+ * xfdashboard_popup_menu_remove_item:
+ * @self: A #XfdashboardPopupMenu
+ * @inMenuItem: A #ClutterActor menu item to remove
+ *
+ * Removes the actor @inMenuItem from pop-up menu @self. When the pop-up menu holds
+ * the last reference on that menu item actor then it will be destroyed otherwise
+ * it will only be removed from pop-up menu.
+ *
+ * If the removed menu item actor implements the #XfdashboardStylable interface
+ * the CSS class popup-menu-item will be removed also.
+ *
+ * Return value: Returns %TRUE if moving menu item was successful, otherwise %FALSE.
+ */
+gboolean xfdashboard_popup_menu_remove_item(XfdashboardPopupMenu *self, ClutterActor *inMenuItem)
+{
+ XfdashboardPopupMenuPrivate *priv;
+
+ g_return_val_if_fail(XFDASHBOARD_IS_POPUP_MENU(self), FALSE);
+ g_return_val_if_fail(CLUTTER_IS_ACTOR(inMenuItem), FALSE);
+
+ priv=self->priv;
+
+ /* Check if menu item is really part of this pop-up menu */
+ if(!_xfdashboard_popup_menu_contains_menu_item(self, inMenuItem))
+ {
+ g_warning(_("%s is not a child of %s and cannot be removed"),
+ G_OBJECT_TYPE_NAME(inMenuItem),
+ G_OBJECT_TYPE_NAME(self));
+ return(FALSE);
+ }
+
+ /* Take extra reference on actor to remove to keep it alive while working with it */
+ g_object_ref(inMenuItem);
+
+ /* Remove menu item actor from pop-up menu */
+ clutter_actor_remove_child(priv->itemsContainer, inMenuItem);
+
+ /* Remove meta from menu item actor */
+ g_object_set_qdata(G_OBJECT(inMenuItem),
+ _xfdashboard_popup_menu_items_container_child_meta_quark,
+ NULL);
+
+ /* Emit signal */
+ g_signal_emit(self, XfdashboardPopupMenuSignals[SIGNAL_ITEM_REMOVED], 0, inMenuItem);
+
+ /* Release extra reference on actor to took to keep it alive */
+ g_object_unref(inMenuItem);
+
+ /* If we get here we removed the menu item actor successfully */
+ return(TRUE);
+}
+
+/**
+ * xfdashboard_popup_menu_add_separator:
+ * @self: A #XfdashboardPopupMenu
+ *
+ * Adds a separator menu item to end of pop-up menu.
+ *
+ * The separator menu item implements the #XfdashboardStylable interface and
+ * adds the CSS class popup-menu-separator.
+ *
+ * Return value: Returns index where item was inserted at or -1 if it failed.
+ */
+gint xfdashboard_popup_menu_add_separator(XfdashboardPopupMenu *self)
+{
+ return(xfdashboard_popup_menu_insert_separator(self, -1));
+}
+
+/**
+ * xfdashboard_popup_menu_add_separator:
+ * @self: A #XfdashboardPopupMenu
+ * @inIndex: The position where to insert this item at
+ *
+ * Inserts a separator menu item at position @inIndex into pop-up menu.
+ *
+ * If position @inIndex is greater than the number of menu items in @self or is
+ * less than 0, then the menu item actor is added to end of pop-up menu.
+ *
+ * The separator menu item implements the #XfdashboardStylable interface and
+ * adds the CSS class popup-menu-separator.
+ *
+ * Return value: Returns index where item was inserted at or -1 if it failed.
+ */
+gint xfdashboard_popup_menu_insert_separator(XfdashboardPopupMenu *self, gint inIndex)
+{
+ XfdashboardPopupMenuPrivate *priv;
+ ClutterActor *separator;
+
+ g_return_val_if_fail(XFDASHBOARD_IS_POPUP_MENU(self), -1);
+
+ priv=self->priv;
+
+ /* Create separator menu item */
+ separator=xfdashboard_actor_new();
+ clutter_actor_set_x_expand(separator, TRUE);
+ clutter_actor_set_y_expand(separator, TRUE);
+ xfdashboard_stylable_add_class(XFDASHBOARD_STYLABLE(separator), "popup-menu-separator");
+
+ /* Insert menu item actor to container at requested position */
+ clutter_actor_insert_child_at_index(priv->itemsContainer, separator, inIndex);
+
+ /* Emit signal */
+ g_signal_emit(self, XfdashboardPopupMenuSignals[SIGNAL_ITEM_ADDED], 0, separator);
+
+ /* Get index where menu item actor was inserted at */
+ return(xfdashboard_popup_menu_get_item_index(self, separator));
+}
+
+/**
+ * xfdashboard_popup_menu_activate:
+ * @self: A #XfdashboardPopupMenu
+ *
+ * Displays the pop-up menu at @self and makes it available for selection.
+ *
+ * This actor will gain the focus automatically and will select the first menu item.
+ */
+void xfdashboard_popup_menu_activate(XfdashboardPopupMenu *self)
+{
+ XfdashboardPopupMenuPrivate *priv;
+ GdkDisplay *display;
+ GdkDeviceManager *deviceManager;
+ GdkDevice *pointerDevice;
+ gint pointerX, pointerY;
+ XfdashboardWindowTrackerMonitor *monitor;
+ gint monitorX, monitorY, monitorWidth, monitorHeight;
+ gfloat x, y, w, h;
+
+ g_return_if_fail(XFDASHBOARD_IS_POPUP_MENU(self));
+
+ priv=self->priv;
+
+ /* If this actor is already active, then do nothing */
+ if(priv->isActive) return;
+
+ /* Move popup menu next to pointer similar to tooltips but keep it on current monitor */
+ display=gdk_display_get_default();
+ deviceManager=gdk_display_get_device_manager(display);
+ pointerDevice=gdk_device_manager_get_client_pointer(deviceManager);
+ gdk_device_get_position(pointerDevice, NULL, &pointerX, &pointerY);
+
+ monitor=xfdashboard_window_tracker_get_monitor_by_position(priv->windowTracker, pointerX, pointerY);
+ if(!monitor)
+ {
+ /* Show error message */
+ g_critical(_("Could not find monitor at pointer position %d,%d"),
+ pointerX,
+ pointerY);
+
+ return;
+ }
+
+ xfdashboard_window_tracker_monitor_get_geometry(monitor, &monitorX, &monitorY, &monitorWidth, &monitorHeight);
+ g_debug("Pointer is on monitor %d with position at %d,%d and size of %dx%d",
+ xfdashboard_window_tracker_monitor_get_number(monitor),
+ monitorX, monitorY,
+ monitorWidth, monitorHeight);
+
+ x=pointerX;
+ y=pointerY;
+ clutter_actor_get_size(CLUTTER_ACTOR(self), &w, &h);
+ if(x<monitorX) x=monitorX;
+ if((x+w)>=(monitorX+monitorWidth)) x=(monitorX+monitorWidth)-w;
+ if(y<monitorY) y=monitorY;
+ if((y+h)>=(monitorY+monitorHeight)) y=(monitorY+monitorHeight)-h;
+ clutter_actor_set_position(CLUTTER_ACTOR(self), floor(x), floor(y));
+
+ /* Now start capturing event in "capture" phase to stop propagating event to
+ * other actors except this one while popup menu is active.
+ */
+ priv->capturedEventSignalID=
+ g_signal_connect_swapped(priv->stage,
+ "captured-event",
+ G_CALLBACK(_xfdashboard_popup_menu_on_captured_event),
+ self);
+
+ /* Show popup menu */
+ clutter_actor_show(CLUTTER_ACTOR(self));
+
+ /* Set flag that this pop-up menu is now active otherwise we cannot focus
+ * this actor.
+ */
+ priv->isActive=TRUE;
+
+ /* Make popup menu focusable as this also marks this actor to be active */
+ xfdashboard_actor_set_can_focus(XFDASHBOARD_ACTOR(self), TRUE);
+
+ /* Move focus to popup menu but remember the actor which has current focus */
+ priv->oldFocusable=xfdashboard_focus_manager_get_focus(priv->focusManager);
+ if(priv->oldFocusable) g_object_add_weak_pointer(G_OBJECT(priv->oldFocusable), &priv->oldFocusable);
+
+ xfdashboard_focus_manager_set_focus(priv->focusManager, XFDASHBOARD_FOCUSABLE(self));
+}
+
+/**
+ * xfdashboard_popup_menu_cancel:
+ * @self: A #XfdashboardPopupMenu
+ *
+ * Hides the pop-up menu if displayed and stops making it available for selection.
+ *
+ * The actor tries to refocus the actor which had the focus before this pop-up
+ * menu was displayed. If that actor cannot be focused it move the focus to the
+ * next focusable actor.
+ */
+void xfdashboard_popup_menu_cancel(XfdashboardPopupMenu *self)
+{
+ XfdashboardPopupMenuPrivate *priv;
+
+ g_return_if_fail(XFDASHBOARD_IS_POPUP_MENU(self));
+
+ priv=self->priv;
+
+ /* Do nothing if pop-up menu is not active */
+ if(!priv->isActive) return;
+
+ /* Unset flag that pop-up menu is active to prevent recursive calls on this
+ * function, e.g. if pop-up menu is cancelled because the object instance
+ * is disposed.
+ */
+ priv->isActive=FALSE;
+
+ /* Stop capturing events in "capture" phase as this popup menu actor will not
+ * be active anymore.
+ */
+ if(priv->capturedEventSignalID)
+ {
+ g_signal_handler_disconnect(priv->stage, priv->capturedEventSignalID);
+ priv->capturedEventSignalID=0;
+ }
+
+ /* Move focus to actor which had the focus previously */
+ if(priv->oldFocusable)
+ {
+ /* Remove weak pointer from previously focused actor */
+ g_object_remove_weak_pointer(G_OBJECT(priv->oldFocusable), &priv->oldFocusable);
+
+ /* Move focus to previous focussed actor */
+ xfdashboard_focus_manager_set_focus(priv->focusManager, priv->oldFocusable);
+
+ /* Forget previous focussed actor */
+ priv->oldFocusable=NULL;
+ }
+
+ /* Hide popup menu */
+ clutter_actor_hide(CLUTTER_ACTOR(self));
+
+ /* Reset popup menu to be not focusable as this also marks this actor is
+ * not active anymore.
+ */
+ xfdashboard_actor_set_can_focus(XFDASHBOARD_ACTOR(self), FALSE);
+
+ /* Destroy this pop-up menu actor when destroy-on-cancel was enabled */
+ if(priv->destroyOnCancel)
+ {
+ clutter_actor_destroy(CLUTTER_ACTOR(self));
+ }
+}
diff --git a/libxfdashboard/popup-menu.h b/libxfdashboard/popup-menu.h
new file mode 100644
index 0000000..900a5df
--- /dev/null
+++ b/libxfdashboard/popup-menu.h
@@ -0,0 +1,151 @@
+/*
+ * popup-menu: A pop-up menu with entries performing an action when an entry
+ * was clicked
+ *
+ * Copyright 2012-2016 Stephan Haller <nomad at froevel.de>
+ *
+ * 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 2 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, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
+ * MA 02110-1301, USA.
+ *
+ *
+ */
+
+#ifndef __LIBXFDASHBOARD_POPUP_MENU__
+#define __LIBXFDASHBOARD_POPUP_MENU__
+
+#if !defined(__LIBXFDASHBOARD_H_INSIDE__) && !defined(LIBXFDASHBOARD_COMPILATION)
+#error "Only <libxfdashboard/libxfdashboard.h> can be included directly."
+#endif
+
+#include <libxfdashboard/background.h>
+
+G_BEGIN_DECLS
+
+#define XFDASHBOARD_TYPE_POPUP_MENU (xfdashboard_popup_menu_get_type())
+#define XFDASHBOARD_POPUP_MENU(obj) (G_TYPE_CHECK_INSTANCE_CAST((obj), XFDASHBOARD_TYPE_POPUP_MENU, XfdashboardPopupMenu))
+#define XFDASHBOARD_IS_POPUP_MENU(obj) (G_TYPE_CHECK_INSTANCE_TYPE((obj), XFDASHBOARD_TYPE_POPUP_MENU))
+#define XFDASHBOARD_POPUP_MENU_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST((klass), XFDASHBOARD_TYPE_POPUP_MENU, XfdashboardPopupMenuClass))
+#define XFDASHBOARD_IS_POPUP_MENU_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE((klass), XFDASHBOARD_TYPE_POPUP_MENU))
+#define XFDASHBOARD_POPUP_MENU_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS((obj), XFDASHBOARD_TYPE_POPUP_MENU, XfdashboardPopupMenuClass))
+
+typedef struct _XfdashboardPopupMenu XfdashboardPopupMenu;
+typedef struct _XfdashboardPopupMenuClass XfdashboardPopupMenuClass;
+typedef struct _XfdashboardPopupMenuPrivate XfdashboardPopupMenuPrivate;
+
+/**
+ * XfdashboardPopupMenu:
+ *
+ * The #XfdashboardPopupMenu structure contains only private data and
+ * should be accessed using the provided API
+ */
+struct _XfdashboardPopupMenu
+{
+ /*< private >*/
+ /* Parent instance */
+ XfdashboardBackground parent_instance;
+
+ /* Private structure */
+ XfdashboardPopupMenuPrivate *priv;
+};
+
+/**
+ * XfdashboardPopupMenuClass:
+ *
+ * The #XfdashboardPopupMenuClass structure contains only private data
+ */
+struct _XfdashboardPopupMenuClass
+{
+ /*< private >*/
+ /* Parent class */
+ XfdashboardBackgroundClass parent_class;
+
+ /*< public >*/
+ /* Virtual functions */
+ void (*activated)(XfdashboardPopupMenu *self);
+ void (*cancelled)(XfdashboardPopupMenu *self);
+
+ void (*item_activated)(XfdashboardPopupMenu *self, ClutterActor *inMenuItem);
+
+ void (*item_added)(XfdashboardPopupMenu *self, ClutterActor *inMenuItem);
+ void (*item_removed)(XfdashboardPopupMenu *self, ClutterActor *inMenuItem);
+};
+
+/**
+ * XfdashboardPopupMenuItemActivateCallback:
+ * @self: The popup menu containing the activated menu item
+ * @inMenuItem: The menu item actor which was activated
+ * @inUserData: Data passed to the function, set with xfdashboard_popup_menu_add_entry()
+ *
+ * A callback called when the menu item @inMenuItem at pop-up menu @self was
+ * activated.
+ */
+typedef void (*XfdashboardPopupMenuItemActivateCallback)(XfdashboardPopupMenu *self,
+ ClutterActor *inMenuItem,
+ gpointer inUserData);
+
+/* Public API */
+GType xfdashboard_popup_menu_get_type(void) G_GNUC_CONST;
+
+ClutterActor* xfdashboard_popup_menu_new(void);
+ClutterActor* xfdashboard_popup_menu_new_for_source(ClutterActor *inSource);
+
+gboolean xfdashboard_popup_menu_get_destroy_on_cancel(XfdashboardPopupMenu *self);
+void xfdashboard_popup_menu_set_destroy_on_cancel(XfdashboardPopupMenu *self, gboolean inDestroyOnCancel);
+
+ClutterActor* xfdashboard_popup_menu_get_source(XfdashboardPopupMenu *self);
+void xfdashboard_popup_menu_set_source(XfdashboardPopupMenu *self, ClutterActor *inSource);
+
+gboolean xfdashboard_popup_menu_get_show_title(XfdashboardPopupMenu *self);
+void xfdashboard_popup_menu_set_show_title(XfdashboardPopupMenu *self, gboolean inShowTitle);
+
+const gchar* xfdashboard_popup_menu_get_title(XfdashboardPopupMenu *self);
+void xfdashboard_popup_menu_set_title(XfdashboardPopupMenu *self, const gchar *inMarkupTitle);
+
+gboolean xfdashboard_popup_menu_get_show_title_icon(XfdashboardPopupMenu *self);
+void xfdashboard_popup_menu_set_show_title_icon(XfdashboardPopupMenu *self, gboolean inShowTitleIcon);
+
+const gchar* xfdashboard_popup_menu_get_title_icon_name(XfdashboardPopupMenu *self);
+void xfdashboard_popup_menu_set_title_icon_name(XfdashboardPopupMenu *self, const gchar *inIconName);
+
+GIcon* xfdashboard_popup_menu_get_title_gicon(XfdashboardPopupMenu *self);
+void xfdashboard_popup_menu_set_title_gicon(XfdashboardPopupMenu *self, GIcon *inIcon);
+
+
+gint xfdashboard_popup_menu_add_item(XfdashboardPopupMenu *self,
+ ClutterActor *inMenuItem,
+ XfdashboardPopupMenuItemActivateCallback inCallback,
+ gpointer inUserData);
+gint xfdashboard_popup_menu_insert_item(XfdashboardPopupMenu *self,
+ ClutterActor *inMenuItem,
+ gint inIndex,
+ XfdashboardPopupMenuItemActivateCallback inCallback,
+ gpointer inUserData);
+gboolean xfdashboard_popup_menu_move_item(XfdashboardPopupMenu *self,
+ ClutterActor *inMenuItem,
+ gint inIndex);
+ClutterActor* xfdashboard_popup_menu_get_item(XfdashboardPopupMenu *self, gint inIndex);
+gint xfdashboard_popup_menu_get_item_index(XfdashboardPopupMenu *self, ClutterActor *inMenuItem);
+gboolean xfdashboard_popup_menu_remove_item(XfdashboardPopupMenu *self, ClutterActor *inMenuItem);
+
+gint xfdashboard_popup_menu_add_separator(XfdashboardPopupMenu *self);
+gint xfdashboard_popup_menu_insert_separator(XfdashboardPopupMenu *self, gint inIndex);
+
+
+void xfdashboard_popup_menu_activate(XfdashboardPopupMenu *self);
+void xfdashboard_popup_menu_cancel(XfdashboardPopupMenu *self);
+
+G_END_DECLS
+
+#endif /* __LIBXFDASHBOARD_POPUP_MENU__ */
--
To stop receiving notification emails like this one, please contact
the administrator of this repository.
More information about the Xfce4-commits
mailing list