[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