[Xfce4-commits] [apps/xfdashboard] 01/01: Implement our own model class and replace ClutterModel with new XfdashboardModel because ClutterModel is deprecated since Clutter version 1.24. This new model implementation should be compatible to all Clutter version and silence compiler warnings when compiled agaist Clutter 1.24 or higher.

noreply at xfce.org noreply at xfce.org
Wed Mar 16 22:27:28 CET 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 7ee7e6490d558c216280c45bb5c4997eda69aa6a
Author: Stephan Haller <nomad at froevel.de>
Date:   Wed Mar 16 22:25:39 2016 +0100

    Implement our own model class and replace ClutterModel with new XfdashboardModel because ClutterModel is deprecated since Clutter version 1.24. This new model implementation should be compatible to all Clutter version and silence compiler warnings when compiled agaist Clutter 1.24 or higher.
    
    This commit should finally fix for issue GH #117
---
 libxfdashboard/Makefile.am               |    2 +
 libxfdashboard/application.c             |    1 +
 libxfdashboard/applications-menu-model.c |  519 ++++++------
 libxfdashboard/applications-menu-model.h |   19 +-
 libxfdashboard/applications-view.c       |   33 +-
 libxfdashboard/model.c                   | 1306 ++++++++++++++++++++++++++++++
 libxfdashboard/model.h                   |  196 +++++
 7 files changed, 1765 insertions(+), 311 deletions(-)

diff --git a/libxfdashboard/Makefile.am b/libxfdashboard/Makefile.am
index 0dafee6..f61f9ca 100644
--- a/libxfdashboard/Makefile.am
+++ b/libxfdashboard/Makefile.am
@@ -51,6 +51,7 @@ libxfdashboard_la_headers = \
 	image-content.h \
 	live-window.h \
 	live-workspace.h \
+	model.h \
 	outline-effect.h \
 	plugin.h \
 	plugins-manager.h \
@@ -117,6 +118,7 @@ libxfdashboard_la_SOURCES = \
 	image-content.c \
 	live-window.c \
 	live-workspace.c \
+	model.c \
 	outline-effect.c \
 	plugin.c \
 	plugins-manager.c \
diff --git a/libxfdashboard/application.c b/libxfdashboard/application.c
index 4f6b27f..c90f87c 100644
--- a/libxfdashboard/application.c
+++ b/libxfdashboard/application.c
@@ -123,6 +123,7 @@ enum
 
 static guint XfdashboardApplicationSignals[SIGNAL_LAST]={ 0, };
 
+
 /* IMPLEMENTATION: Private variables and methods */
 #define XFDASHBOARD_APP_ID					"de.froevel.nomad.xfdashboard"
 #define XFDASHBOARD_XFCONF_CHANNEL			"xfdashboard"
diff --git a/libxfdashboard/applications-menu-model.c b/libxfdashboard/applications-menu-model.c
index f707563..8f95940 100644
--- a/libxfdashboard/applications-menu-model.c
+++ b/libxfdashboard/applications-menu-model.c
@@ -36,7 +36,7 @@
 /* Define these classes in GObject system */
 G_DEFINE_TYPE(XfdashboardApplicationsMenuModel,
 				xfdashboard_applications_menu_model,
-				CLUTTER_TYPE_LIST_MODEL)
+				XFDASHBOARD_TYPE_MODEL)
 
 /* Private structure - access only by public API if needed */
 #define XFDASHBOARD_APPLICATIONS_MENU_MODEL_GET_PRIVATE(obj) \
@@ -65,13 +65,53 @@ static guint XfdashboardApplicationsMenuModelSignals[SIGNAL_LAST]={ 0, };
 typedef struct _XfdashboardApplicationsMenuModelFillData		XfdashboardApplicationsMenuModelFillData;
 struct _XfdashboardApplicationsMenuModelFillData
 {
-	gint		sequenceID;
-	GSList		*populatedMenus;
+	gint				sequenceID;
+	GSList				*populatedMenus;
+};
+
+typedef struct _XfdashboardApplicationsMenuModelItem			XfdashboardApplicationsMenuModelItem;
+struct _XfdashboardApplicationsMenuModelItem
+{
+	guint				sequenceID;
+	GarconMenuElement	*menuElement;
+	GarconMenu			*parentMenu;
+	GarconMenu			*section;
+	gchar				*title;
+	gchar				*description;
 };
 
 /* Forward declarations */
 static void _xfdashboard_applications_menu_model_fill_model(XfdashboardApplicationsMenuModel *self);
 
+/* Free an item of application menu model */
+static void _xfdashboard_applications_menu_model_item_free(XfdashboardApplicationsMenuModelItem *inItem)
+{
+	if(inItem)
+	{
+		/* Release allocated resources in item */
+		if(inItem->menuElement) g_object_unref(inItem->menuElement);
+		if(inItem->parentMenu) g_object_unref(inItem->parentMenu);
+		if(inItem->section) g_object_unref(inItem->section);
+		if(inItem->title) g_free(inItem->title);
+		if(inItem->description) g_free(inItem->description);
+
+		/* Free item */
+		g_free(inItem);
+	}
+}
+
+/* Create a new item for application menu model */
+static XfdashboardApplicationsMenuModelItem* _xfdashboard_applications_menu_model_item_new(void)
+{
+	XfdashboardApplicationsMenuModelItem	*item;
+
+	/* Create empty item */
+	item=g_new0(XfdashboardApplicationsMenuModelItem, 1);
+
+	/* Return new empty item */
+	return(item);
+}
+
 /* A menu was changed and needs to be reloaded */
 static void _xfdashboard_applications_menu_model_on_reload_required(XfdashboardApplicationsMenuModel *self,
 																	gpointer inUserData)
@@ -87,33 +127,16 @@ static void _xfdashboard_applications_menu_model_on_reload_required(XfdashboardA
 static void _xfdashboard_applications_menu_model_clear(XfdashboardApplicationsMenuModel *self)
 {
 	XfdashboardApplicationsMenuModelPrivate		*priv;
-	ClutterModelIter							*iterator;
-	GarconMenuElement							*menuElement;
 
 	g_return_if_fail(XFDASHBOARD_IS_APPLICATIONS_MENU_MODEL(self));
 
 	priv=self->priv;
 
 	/* Unset filter (forces all rows being accessible and not being skipped/filtered) */
-	clutter_model_set_filter(CLUTTER_MODEL(self), NULL, NULL, NULL);
+	xfdashboard_model_set_filter(XFDASHBOARD_MODEL(self), NULL, NULL, NULL);
 
 	/* Clean up and remove all rows */
-	while(clutter_model_get_n_rows(CLUTTER_MODEL(self)))
-	{
-		/* Get data from model for clean up */
-		menuElement=NULL;
-		iterator=clutter_model_get_iter_at_row(CLUTTER_MODEL(self), 0);
-		clutter_model_iter_get(iterator,
-								XFDASHBOARD_APPLICATIONS_MENU_MODEL_COLUMN_MENU_ELEMENT, &menuElement,
-								-1);
-
-		/* Remove row */
-		clutter_model_remove(CLUTTER_MODEL(self), 0);
-
-		/* Release iterator */
-		if(menuElement) g_object_unref(menuElement);
-		g_object_unref(iterator);
-	}
+	xfdashboard_model_remove_all(XFDASHBOARD_MODEL(self));
 
 	/* Destroy root menu */
 	if(priv->rootMenu)
@@ -124,48 +147,41 @@ static void _xfdashboard_applications_menu_model_clear(XfdashboardApplicationsMe
 }
 
 /* Helper function to filter model data */
-static gboolean _xfdashboard_applications_menu_model_filter_by_menu(ClutterModel *inModel,
-																	ClutterModelIter *inIter,
+static gboolean _xfdashboard_applications_menu_model_filter_by_menu(XfdashboardModelIter *inIter,
 																	gpointer inUserData)
 {
-	XfdashboardApplicationsMenuModel			*self;
-	XfdashboardApplicationsMenuModelPrivate		*priv;
-	GarconMenu									*parentMenu;
+	XfdashboardApplicationsMenuModel			*model;
+	XfdashboardApplicationsMenuModelPrivate		*modelPriv;
+	gboolean									doShow;
 	GarconMenu									*requestedParentMenu;
-	GarconMenuElement							*menuElement;
+	XfdashboardApplicationsMenuModelItem		*item;
 	GarconMenuItemPool							*itemPool;
 	const gchar									*desktopID;
-	gboolean									doShow;
 
-	g_return_val_if_fail(XFDASHBOARD_IS_APPLICATIONS_MENU_MODEL(inModel), FALSE);
-	g_return_val_if_fail(CLUTTER_IS_MODEL_ITER(inIter), FALSE);
+	g_return_val_if_fail(XFDASHBOARD_IS_MODEL_ITER(inIter), FALSE);
 	g_return_val_if_fail(GARCON_IS_MENU(inUserData), FALSE);
+	g_return_val_if_fail(XFDASHBOARD_IS_APPLICATIONS_MENU_MODEL(xfdashboard_model_iter_get_model(inIter)), FALSE);
 
-	self=XFDASHBOARD_APPLICATIONS_MENU_MODEL(inModel);
-	priv=self->priv;
-	requestedParentMenu=GARCON_MENU(inUserData);
-	menuElement=NULL;
 	doShow=FALSE;
+	requestedParentMenu=GARCON_MENU(inUserData);
+	model=XFDASHBOARD_APPLICATIONS_MENU_MODEL(xfdashboard_model_iter_get_model(inIter));
+	modelPriv=model->priv;
 
 	/* Get menu element at iterator */
-	clutter_model_iter_get(inIter,
-							XFDASHBOARD_APPLICATIONS_MENU_MODEL_COLUMN_MENU_ELEMENT, &menuElement,
-							XFDASHBOARD_APPLICATIONS_MENU_MODEL_COLUMN_PARENT_MENU, &parentMenu,
-							-1);
-	if(menuElement==NULL) return(FALSE);
+	item=(XfdashboardApplicationsMenuModelItem*)xfdashboard_model_iter_get(inIter);
+	if(item->menuElement==NULL) return(FALSE);
 
 	/* Only menu items and sub-menus can be visible */
-	if(!GARCON_IS_MENU(menuElement) && !GARCON_IS_MENU_ITEM(menuElement))
+	if(!GARCON_IS_MENU(item->menuElement) && !GARCON_IS_MENU_ITEM(item->menuElement))
 	{
-		g_object_unref(menuElement);
 		return(FALSE);
 	}
 
 	/* If menu element is a menu check if it's parent menu is the requested one */
-	if(GARCON_IS_MENU(menuElement))
+	if(GARCON_IS_MENU(item->menuElement))
 	{
-		if(requestedParentMenu==parentMenu ||
-			(!requestedParentMenu && parentMenu==priv->rootMenu))
+		if(requestedParentMenu==item->parentMenu ||
+			(!requestedParentMenu && item->parentMenu==modelPriv->rootMenu))
 		{
 			doShow=TRUE;
 		}
@@ -174,70 +190,58 @@ static gboolean _xfdashboard_applications_menu_model_filter_by_menu(ClutterModel
 		else
 		{
 			/* Get desktop ID of menu item */
-			desktopID=garcon_menu_item_get_desktop_id(GARCON_MENU_ITEM(menuElement));
+			desktopID=garcon_menu_item_get_desktop_id(GARCON_MENU_ITEM(item->menuElement));
 
 			/* Get menu items of menu */
-			itemPool=garcon_menu_get_item_pool(parentMenu);
+			itemPool=garcon_menu_get_item_pool(item->parentMenu);
 
 			/* Determine if menu item at iterator is in menu's item pool */
 			if(garcon_menu_item_pool_lookup(itemPool, desktopID)!=FALSE) doShow=TRUE;
 		}
 
-	/* Release allocated resources */
-	if(parentMenu) g_object_unref(parentMenu);
-	g_object_unref(menuElement);
-
 	/* If we get here return TRUE to show model data item or FALSE to hide */
 	return(doShow);
 }
 
-static gboolean _xfdashboard_applications_menu_model_filter_by_section(ClutterModel *inModel,
-																		ClutterModelIter *inIter,
+static gboolean _xfdashboard_applications_menu_model_filter_by_section(XfdashboardModelIter *inIter,
 																		gpointer inUserData)
 {
-	XfdashboardApplicationsMenuModel			*self;
-	XfdashboardApplicationsMenuModelPrivate		*priv;
-	GarconMenu									*section;
-	GarconMenu									*requestedSection;
+	XfdashboardApplicationsMenuModel			*model;
+	XfdashboardApplicationsMenuModelPrivate		*modelPriv;
 	gboolean									doShow;
+	GarconMenu									*requestedSection;
+	XfdashboardApplicationsMenuModelItem		*item;
 
-	g_return_val_if_fail(XFDASHBOARD_IS_APPLICATIONS_MENU_MODEL(inModel), FALSE);
-	g_return_val_if_fail(CLUTTER_IS_MODEL_ITER(inIter), FALSE);
+	g_return_val_if_fail(XFDASHBOARD_IS_MODEL_ITER(inIter), FALSE);
+	g_return_val_if_fail(XFDASHBOARD_IS_APPLICATIONS_MENU_MODEL(xfdashboard_model_iter_get_model(inIter)), FALSE);
 	g_return_val_if_fail(GARCON_IS_MENU(inUserData), FALSE);
 
-	self=XFDASHBOARD_APPLICATIONS_MENU_MODEL(inModel);
-	priv=self->priv;
-	requestedSection=GARCON_MENU(inUserData);
 	doShow=FALSE;
+	requestedSection=GARCON_MENU(inUserData);
+	model=XFDASHBOARD_APPLICATIONS_MENU_MODEL(xfdashboard_model_iter_get_model(inIter));
+	modelPriv=model->priv;
 
 	/* Check if root section is requested */
-	if(!requestedSection) requestedSection=priv->rootMenu;
+	if(!requestedSection) requestedSection=modelPriv->rootMenu;
 
 	/* Get menu element at iterator */
-	clutter_model_iter_get(inIter,
-							XFDASHBOARD_APPLICATIONS_MENU_MODEL_COLUMN_SECTION, &section,
-							-1);
+	item=(XfdashboardApplicationsMenuModelItem*)xfdashboard_model_iter_get(inIter);
 
 	/* If menu element is a menu check if root menu is parent menu and root menu is requested */
-	if((section && section==requestedSection) ||
-		(!section && requestedSection==priv->rootMenu))
+	if((item->section && item->section==requestedSection) ||
+		(!item->section && requestedSection==modelPriv->rootMenu))
 	{
 		doShow=TRUE;
 	}
 
-	/* Release allocated resources */
-	if(section) g_object_unref(section);
-
 	/* If we get here return TRUE to show model data item or FALSE to hide */
 	return(doShow);
 }
 
-static gboolean _xfdashboard_applications_menu_model_filter_empty(ClutterModel *inModel,
-																	ClutterModelIter *inIter,
+static gboolean _xfdashboard_applications_menu_model_filter_empty(XfdashboardModelIter *inIter,
 																	gpointer inUserData)
 {
-	g_return_val_if_fail(XFDASHBOARD_IS_APPLICATIONS_MENU_MODEL(inModel), FALSE);
-	g_return_val_if_fail(CLUTTER_IS_MODEL_ITER(inIter), FALSE);
+	g_return_val_if_fail(XFDASHBOARD_IS_MODEL_ITER(inIter), FALSE);
 	g_return_val_if_fail(GARCON_IS_MENU(inUserData), FALSE);
 
 	/* This functions always returns FALSE because each entry is considered empty and hidden */
@@ -391,6 +395,7 @@ static void _xfdashboard_applications_menu_model_fill_model_collect_menu(Xfdashb
 	GarconMenu										*menu;
 	GarconMenu										*section;
 	GList											*elements, *element;
+	XfdashboardApplicationsMenuModelItem			*item;
 
 	g_return_if_fail(XFDASHBOARD_IS_APPLICATIONS_MENU_MODEL(self));
 	g_return_if_fail(GARCON_IS_MENU(inMenu));
@@ -431,14 +436,16 @@ static void _xfdashboard_applications_menu_model_fill_model_collect_menu(Xfdashb
 			 * and no similar menu
 			 */
 			inFillData->sequenceID++;
-			clutter_model_append(CLUTTER_MODEL(self),
-									XFDASHBOARD_APPLICATIONS_MENU_MODEL_COLUMN_SEQUENCE_ID, inFillData->sequenceID,
-									XFDASHBOARD_APPLICATIONS_MENU_MODEL_COLUMN_MENU_ELEMENT, inMenu,
-									XFDASHBOARD_APPLICATIONS_MENU_MODEL_COLUMN_PARENT_MENU, inParentMenu,
-									XFDASHBOARD_APPLICATIONS_MENU_MODEL_COLUMN_SECTION, section,
-									XFDASHBOARD_APPLICATIONS_MENU_MODEL_COLUMN_TITLE, title,
-									XFDASHBOARD_APPLICATIONS_MENU_MODEL_COLUMN_DESCRIPTION, description,
-									-1);
+
+			item=_xfdashboard_applications_menu_model_item_new();
+			item->sequenceID=inFillData->sequenceID;
+			if(inMenu) item->menuElement=g_object_ref(inMenu);
+			if(inParentMenu) item->parentMenu=g_object_ref(inParentMenu);
+			if(section) item->section=g_object_ref(section);
+			if(title) item->title=g_strdup(title);
+			if(description) item->description=g_strdup(description);
+
+			xfdashboard_model_append(XFDASHBOARD_MODEL(self), item, NULL);
 
 			/* Add menu to list of populated ones */
 			inFillData->populatedMenus=g_slist_prepend(inFillData->populatedMenus, inMenu);
@@ -496,14 +503,16 @@ static void _xfdashboard_applications_menu_model_fill_model_collect_menu(Xfdashb
 
 			/* Add menu item to model */
 			inFillData->sequenceID++;
-			clutter_model_append(CLUTTER_MODEL(self),
-									XFDASHBOARD_APPLICATIONS_MENU_MODEL_COLUMN_SEQUENCE_ID, inFillData->sequenceID,
-									XFDASHBOARD_APPLICATIONS_MENU_MODEL_COLUMN_MENU_ELEMENT, menuElement,
-									XFDASHBOARD_APPLICATIONS_MENU_MODEL_COLUMN_PARENT_MENU, menu,
-									XFDASHBOARD_APPLICATIONS_MENU_MODEL_COLUMN_SECTION, section,
-									XFDASHBOARD_APPLICATIONS_MENU_MODEL_COLUMN_TITLE, title,
-									XFDASHBOARD_APPLICATIONS_MENU_MODEL_COLUMN_DESCRIPTION, description,
-									-1);
+
+			item=_xfdashboard_applications_menu_model_item_new();
+			item->sequenceID=inFillData->sequenceID;
+			if(menuElement) item->menuElement=g_object_ref(menuElement);
+			if(menu) item->parentMenu=g_object_ref(menu);
+			if(section) item->section=g_object_ref(section);
+			if(title) item->title=g_strdup(title);
+			if(description) item->description=g_strdup(description);
+
+			xfdashboard_model_append(XFDASHBOARD_MODEL(self), item, NULL);
 
 			/* Release allocated resources */
 			g_free(title);
@@ -559,174 +568,6 @@ static gboolean _xfdashboard_applications_menu_model_init_idle(gpointer inUserDa
 }
 
 
-/* IMPLEMENTATION: ClutterModel */
-
-/* Resort model */
-static gint _xfdashboard_applications_menu_model_resort_menu_element_callback(ClutterModel *inModel,
-																				const GValue *inLeft,
-																				const GValue *inRight,
-																				gpointer inUserData)
-{
-	GarconMenuElement		*leftValue=GARCON_MENU_ELEMENT(g_value_get_object(inLeft));
-	GarconMenuElement		*rightValue=GARCON_MENU_ELEMENT(g_value_get_object(inRight));
-	const gchar				*leftName=garcon_menu_element_get_name(leftValue);
-	const gchar				*rightName=garcon_menu_element_get_name(rightValue);
-
-	return(g_strcmp0(leftName, rightName));
-}
-
-static gint _xfdashboard_applications_menu_model_resort_parent_menu_callback(ClutterModel *inModel,
-																				const GValue *inLeft,
-																				const GValue *inRight,
-																				gpointer inUserData)
-{
-	GarconMenu				*leftValue=GARCON_MENU(g_value_get_object(inLeft));
-	GarconMenu				*rightValue=GARCON_MENU(g_value_get_object(inRight));
-	gint					result=0;
-
-	/* If both menus have the same parent menu sort them by name ... */
-	if(garcon_menu_get_parent(leftValue)==garcon_menu_get_parent(rightValue))
-	{
-		const gchar			*leftName=garcon_menu_element_get_name(GARCON_MENU_ELEMENT(leftValue));
-		const gchar			*rightName=garcon_menu_element_get_name(GARCON_MENU_ELEMENT(rightValue));
-
-		result=g_strcmp0(leftName, rightName);
-	}
-		/* ... otherwise get depth of each value and compare name of upper menu */
-		else
-		{
-			GList			*leftPath=NULL;
-			GList			*rightPath=NULL;
-			GarconMenu		*leftMenu=leftValue;
-			GarconMenu		*rightMenu=rightValue;
-			const gchar		*leftName;
-			const gchar		*rightName;
-			gint			upperLevel;
-
-			/* Build path of left value */
-			while(leftMenu)
-			{
-				leftPath=g_list_prepend(leftPath, leftMenu);
-				leftMenu=garcon_menu_get_parent(leftMenu);
-			}
-
-			/* Build path of right value */
-			while(rightMenu)
-			{
-				rightPath=g_list_prepend(rightPath, rightMenu);
-				rightMenu=garcon_menu_get_parent(rightMenu);
-			}
-
-			/* Find level of upper path of both values */
-			upperLevel=MIN(g_list_length(leftPath), g_list_length(rightPath));
-			if(upperLevel>0) upperLevel--;
-
-			/* Get name of both values at upper path */
-			leftName=garcon_menu_element_get_name(GARCON_MENU_ELEMENT(g_list_nth_data(leftPath, upperLevel)));
-			rightName=garcon_menu_element_get_name(GARCON_MENU_ELEMENT(g_list_nth_data(rightPath, upperLevel)));
-
-			/* Compare name of both value at upper path */
-			result=g_strcmp0(leftName, rightName);
-		}
-
-	/* Return result */
-	return(result);
-}
-
-static gint _xfdashboard_applications_menu_model_resort_section_callback(ClutterModel *inModel,
-																				const GValue *inLeft,
-																				const GValue *inRight,
-																				gpointer inUserData)
-{
-	GObject					*leftValue=g_value_get_object(inLeft);
-	GObject					*rightValue=g_value_get_object(inLeft);
-	const gchar				*leftName=NULL;
-	const gchar				*rightName=NULL;
-
-	if(leftValue &&
-		GARCON_IS_MENU_ELEMENT(leftValue))
-	{
-		leftName=garcon_menu_element_get_name(GARCON_MENU_ELEMENT(leftValue));
-	}
-
-	if(rightValue &&
-		GARCON_IS_MENU_ELEMENT(rightValue))
-	{
-		rightName=garcon_menu_element_get_name(GARCON_MENU_ELEMENT(rightValue));
-	}
-
-	return(g_strcmp0(leftName, rightName));
-}
-
-static gint _xfdashboard_applications_menu_model_resort_string_callback(ClutterModel *inModel,
-																		const GValue *inLeft,
-																		const GValue *inRight,
-																		gpointer inUserData)
-{
-	const gchar		*leftValue=g_value_get_string(inLeft);
-	const gchar		*rightValue=g_value_get_string(inRight);
-
-	return(g_strcmp0(leftValue, rightValue));
-}
-
-static gint _xfdashboard_applications_menu_model_resort_uint_callback(ClutterModel *inModel,
-																		const GValue *inLeft,
-																		const GValue *inRight,
-																		gpointer inUserData)
-{
-	guint		leftValue=g_value_get_uint(inLeft);
-	guint		rightValue=g_value_get_uint(inRight);
-
-	if(leftValue<rightValue) return(-1);
-		else if(leftValue>rightValue) return(1);
-	return(0);
-}
-
-static void _xfdashboard_applications_menu_model_resort(ClutterModel *inModel,
-														ClutterModelSortFunc inSortCallback,
-														gpointer inUserData)
-{
-	g_return_if_fail(XFDASHBOARD_IS_APPLICATIONS_MENU_MODEL(inModel));
-
-	/* If given sort function is NULL use default one */
-	if(inSortCallback==NULL)
-	{
-		gint	sortColumn=clutter_model_get_sorting_column(inModel);
-
-		switch(sortColumn)
-		{
-			case XFDASHBOARD_APPLICATIONS_MENU_MODEL_COLUMN_SEQUENCE_ID:
-				inSortCallback=_xfdashboard_applications_menu_model_resort_uint_callback;
-				break;
-
-			case XFDASHBOARD_APPLICATIONS_MENU_MODEL_COLUMN_MENU_ELEMENT:
-				inSortCallback=_xfdashboard_applications_menu_model_resort_menu_element_callback;
-				break;
-
-			case XFDASHBOARD_APPLICATIONS_MENU_MODEL_COLUMN_PARENT_MENU:
-				inSortCallback=_xfdashboard_applications_menu_model_resort_parent_menu_callback;
-				break;
-
-			case XFDASHBOARD_APPLICATIONS_MENU_MODEL_COLUMN_SECTION:
-				inSortCallback=_xfdashboard_applications_menu_model_resort_section_callback;
-				break;
-
-			case XFDASHBOARD_APPLICATIONS_MENU_MODEL_COLUMN_TITLE:
-			case XFDASHBOARD_APPLICATIONS_MENU_MODEL_COLUMN_DESCRIPTION:
-				inSortCallback=_xfdashboard_applications_menu_model_resort_string_callback;
-				break;
-
-			default:
-				g_critical(_("Sorting column %d without user-defined function is not possible"), sortColumn);
-				g_assert_not_reached();
-				break;
-		}
-	}
-
-	/* Call parent's class resort method */
-	CLUTTER_MODEL_CLASS(xfdashboard_applications_menu_model_parent_class)->resort(inModel, inSortCallback, inUserData);
-}
-
 /* IMPLEMENTATION: GObject */
 
 /* Dispose this object */
@@ -764,12 +605,8 @@ static void _xfdashboard_applications_menu_model_dispose(GObject *inObject)
  */
 static void xfdashboard_applications_menu_model_class_init(XfdashboardApplicationsMenuModelClass *klass)
 {
-	ClutterModelClass		*modelClass=CLUTTER_MODEL_CLASS(klass);
 	GObjectClass			*gobjectClass=G_OBJECT_CLASS(klass);
 
-	/* Override functions */
-	modelClass->resort=_xfdashboard_applications_menu_model_resort;
-
 	gobjectClass->dispose=_xfdashboard_applications_menu_model_dispose;
 
 	/* Set up private structure */
@@ -794,22 +631,6 @@ static void xfdashboard_applications_menu_model_class_init(XfdashboardApplicatio
 static void xfdashboard_applications_menu_model_init(XfdashboardApplicationsMenuModel *self)
 {
 	XfdashboardApplicationsMenuModelPrivate	*priv;
-	GType									columnTypes[]=	{
-																G_TYPE_UINT, /* XFDASHBOARD_APPLICATIONS_MENU_MODEL_COLUMN_SEQUENCE_ID */
-																GARCON_TYPE_MENU_ELEMENT, /* XFDASHBOARD_APPLICATIONS_MENU_MODEL_COLUMN_MENU_ELEMENT */
-																GARCON_TYPE_MENU, /* XFDASHBOARD_APPLICATIONS_MENU_MODEL_COLUMN_PARENT_MENU */
-																GARCON_TYPE_MENU, /* XFDASHBOARD_APPLICATIONS_MENU_MODEL_COLUMN_SECTION */
-																G_TYPE_STRING, /* XFDASHBOARD_APPLICATIONS_MENU_MODEL_COLUMN_TITLE */
-																G_TYPE_STRING /* XFDASHBOARD_APPLICATIONS_MENU_MODEL_COLUMN_DESCRIPTION */
-															};
-	const gchar*							columnNames[]=	{
-																_("ID"), /* XFDASHBOARD_APPLICATIONS_MENU_MODEL_COLUMN_SEQUENCE_ID */
-																_("Menu item"), /* XFDASHBOARD_APPLICATIONS_MENU_MODEL_COLUMN_MENU_ELEMENT */
-																_("Parent menu"), /* XFDASHBOARD_APPLICATIONS_MENU_MODEL_COLUMN_PARENT_MENU */
-																_("Section"), /* XFDASHBOARD_APPLICATIONS_MENU_MODEL_COLUMN_SECTION */
-																_("Title"), /* XFDASHBOARD_APPLICATIONS_MENU_MODEL_COLUMN_TITLE */
-																_("Description"), /* XFDASHBOARD_APPLICATIONS_MENU_MODEL_COLUMN_DESCRIPTION */
-															};
 
 	priv=self->priv=XFDASHBOARD_APPLICATIONS_MENU_MODEL_GET_PRIVATE(self);
 
@@ -818,10 +639,6 @@ static void xfdashboard_applications_menu_model_init(XfdashboardApplicationsMenu
 	priv->appDB=NULL;
 	priv->reloadRequiredSignalID=0;
 
-	/* Set up model */
-	clutter_model_set_types(CLUTTER_MODEL(self), XFDASHBOARD_APPLICATIONS_MENU_MODEL_COLUMN_LAST, columnTypes);
-	clutter_model_set_names(CLUTTER_MODEL(self), XFDASHBOARD_APPLICATIONS_MENU_MODEL_COLUMN_LAST, columnNames);
-
 	/* Get application database and connect signals */
 	priv->appDB=xfdashboard_application_database_get_default();
 	priv->reloadRequiredSignalID=g_signal_connect_swapped(priv->appDB,
@@ -832,15 +649,124 @@ static void xfdashboard_applications_menu_model_init(XfdashboardApplicationsMenu
 	clutter_threads_add_idle(_xfdashboard_applications_menu_model_init_idle, self);
 }
 
+
 /* IMPLEMENTATION: Public API */
 
-ClutterModel* xfdashboard_applications_menu_model_new(void)
+/* Create a new instance of application menu model */
+XfdashboardModel* xfdashboard_applications_menu_model_new(void)
+{
+	GObject		*model;
+
+	/* Create instance */
+	model=g_object_new(XFDASHBOARD_TYPE_APPLICATIONS_MENU_MODEL,
+						"free-data-callback", _xfdashboard_applications_menu_model_item_free,
+						NULL);
+	if(!model) return(NULL);
+
+	/* Return new instance */
+	return(XFDASHBOARD_MODEL(model));
+}
+
+/* Get values from application menu model at requested iterator and columns */
+void xfdashboard_applications_menu_model_get(XfdashboardApplicationsMenuModel *self,
+												XfdashboardModelIter *inIter,
+												...)
 {
-	return(CLUTTER_MODEL(g_object_new(XFDASHBOARD_TYPE_APPLICATIONS_MENU_MODEL, NULL)));
+	XfdashboardModel							*model;
+	XfdashboardApplicationsMenuModelItem		*item;
+	va_list										args;
+	gint										column;
+	gpointer									*storage;
+
+	g_return_if_fail(XFDASHBOARD_IS_APPLICATIONS_MENU_MODEL(self));
+	g_return_if_fail(XFDASHBOARD_IS_MODEL_ITER(inIter));
+
+	/* Check if iterator belongs to this model */
+	model=xfdashboard_model_iter_get_model(inIter);
+	if(!XFDASHBOARD_IS_APPLICATIONS_MENU_MODEL(model) ||
+		XFDASHBOARD_APPLICATIONS_MENU_MODEL(model)!=self)
+	{
+		g_critical(_("Iterator does not belong to application menu model."));
+		return;
+	}
+
+	/* Get item from iterator */
+	item=(XfdashboardApplicationsMenuModelItem*)xfdashboard_model_iter_get(inIter);
+	g_assert(item);
+
+	/* Iterate through column index and pointer where to store value until
+	 * until end of list (marked with -1) is reached.
+	 */
+	va_start(args, inIter);
+
+	column=va_arg(args, gint);
+	while(column!=-1)
+	{
+		if(column<0 || column>=XFDASHBOARD_APPLICATIONS_MENU_MODEL_COLUMN_LAST)
+		{
+			g_warning(_("Invalid column number %d added to iter (remember to end your list of columns with a -1)"),
+						column);
+			break;
+		}
+
+		/* Get generic pointer to storage as it will be casted as necessary
+		 * when determining which column is requested.
+		 */
+		storage=va_arg(args, gpointer*);
+		if(!storage)
+		{
+			g_warning(_("No storage pointer provided to store value of column number %d"),
+						column);
+			break;
+		}
+
+		/* Check which column is requested and store value at pointer */
+		switch(column)
+		{
+			case XFDASHBOARD_APPLICATIONS_MENU_MODEL_COLUMN_SEQUENCE_ID:
+				*((gint*)storage)=item->sequenceID;
+				break;
+
+			case XFDASHBOARD_APPLICATIONS_MENU_MODEL_COLUMN_MENU_ELEMENT:
+				if(item->menuElement) *storage=g_object_ref(item->menuElement);
+					else *storage=NULL;
+				break;
+
+			case XFDASHBOARD_APPLICATIONS_MENU_MODEL_COLUMN_PARENT_MENU:
+				if(item->parentMenu) *storage=g_object_ref(item->parentMenu);
+					else *storage=NULL;
+				break;
+
+			case XFDASHBOARD_APPLICATIONS_MENU_MODEL_COLUMN_SECTION:
+				if(item->section) *storage=g_object_ref(item->section);
+					else *storage=NULL;
+				break;
+
+			case XFDASHBOARD_APPLICATIONS_MENU_MODEL_COLUMN_TITLE:
+				if(item->title) *((gchar**)storage)=g_strdup(item->title);
+					else *((gchar**)storage)=g_strdup("");
+				break;
+
+			case XFDASHBOARD_APPLICATIONS_MENU_MODEL_COLUMN_DESCRIPTION:
+				if(item->description) *((gchar**)storage)=g_strdup(item->description);
+					else *((gchar**)storage)=g_strdup("");
+				break;
+
+			default:
+				g_assert_not_reached();
+				break;
+		}
+
+		/* Continue with next column and storage pointer */
+		column=va_arg(args, gint);
+	}
+
+	va_end(args);
 }
 
 /* Filter menu items being a direct child item of requested menu */
-void xfdashboard_applications_menu_model_filter_by_menu(XfdashboardApplicationsMenuModel *self, GarconMenu *inMenu)
+void xfdashboard_applications_menu_model_filter_by_menu(XfdashboardApplicationsMenuModel *self,
+														GarconMenu *inMenu)
 {
 	XfdashboardApplicationsMenuModelPrivate		*priv;
 
@@ -853,11 +779,15 @@ void xfdashboard_applications_menu_model_filter_by_menu(XfdashboardApplicationsM
 	if(inMenu==NULL) inMenu=priv->rootMenu;
 
 	/* Filter model data */
-	clutter_model_set_filter(CLUTTER_MODEL(self), _xfdashboard_applications_menu_model_filter_by_menu, g_object_ref(inMenu), g_object_unref);
+	xfdashboard_model_set_filter(XFDASHBOARD_MODEL(self),
+									_xfdashboard_applications_menu_model_filter_by_menu,
+									g_object_ref(inMenu),
+									g_object_unref);
 }
 
 /* Filter menu items being an indirect child item of requested section */
-void xfdashboard_applications_menu_model_filter_by_section(XfdashboardApplicationsMenuModel *self, GarconMenu *inSection)
+void xfdashboard_applications_menu_model_filter_by_section(XfdashboardApplicationsMenuModel *self,
+															GarconMenu *inSection)
 {
 	XfdashboardApplicationsMenuModelPrivate		*priv;
 
@@ -872,11 +802,18 @@ void xfdashboard_applications_menu_model_filter_by_section(XfdashboardApplicatio
 	/* Filter model data */
 	if(inSection)
 	{
-		clutter_model_set_filter(CLUTTER_MODEL(self), _xfdashboard_applications_menu_model_filter_by_section, g_object_ref(inSection), (GDestroyNotify)g_object_unref);
+		g_debug("Filtering section '%s'", garcon_menu_element_get_name(GARCON_MENU_ELEMENT(inSection)));
+		xfdashboard_model_set_filter(XFDASHBOARD_MODEL(self),
+										_xfdashboard_applications_menu_model_filter_by_section,
+										g_object_ref(inSection),
+										g_object_unref);
 	}
 		else
 		{
-			g_debug("Filtering empty (root) section");
-			clutter_model_set_filter(CLUTTER_MODEL(self), _xfdashboard_applications_menu_model_filter_empty, NULL, NULL);
+			g_debug("Filtering root section because no section requested");
+			xfdashboard_model_set_filter(XFDASHBOARD_MODEL(self),
+											_xfdashboard_applications_menu_model_filter_empty,
+											NULL,
+											NULL);
 		}
 }
diff --git a/libxfdashboard/applications-menu-model.h b/libxfdashboard/applications-menu-model.h
index b517cc6..f3fbdb0 100644
--- a/libxfdashboard/applications-menu-model.h
+++ b/libxfdashboard/applications-menu-model.h
@@ -29,9 +29,10 @@
 #error "Only <libxfdashboard/libxfdashboard.h> can be included directly."
 #endif
 
-#include <clutter/clutter.h>
 #include <garcon/garcon.h>
 
+#include <libxfdashboard/model.h>
+
 G_BEGIN_DECLS
 
 #define XFDASHBOARD_TYPE_APPLICATIONS_MENU_MODEL			(xfdashboard_applications_menu_model_get_type())
@@ -48,7 +49,7 @@ typedef struct _XfdashboardApplicationsMenuModelClass		XfdashboardApplicationsMe
 struct _XfdashboardApplicationsMenuModel
 {
 	/* Parent instance */
-	ClutterListModel							parent_instance;
+	XfdashboardModel							parent_instance;
 
 	/* Private structure */
 	XfdashboardApplicationsMenuModelPrivate		*priv;
@@ -58,7 +59,7 @@ struct _XfdashboardApplicationsMenuModelClass
 {
 	/*< private >*/
 	/* Parent class */
-	ClutterListModelClass						parent_class;
+	XfdashboardModelClass						parent_class;
 
 	/*< public >*/
 	/* Virtual functions */
@@ -84,10 +85,16 @@ enum
 
 GType xfdashboard_applications_menu_model_get_type(void) G_GNUC_CONST;
 
-ClutterModel* xfdashboard_applications_menu_model_new(void);
+XfdashboardModel* xfdashboard_applications_menu_model_new(void);
+
+void xfdashboard_applications_menu_model_get(XfdashboardApplicationsMenuModel *self,
+												XfdashboardModelIter *inIter,
+												...);
 
-void xfdashboard_applications_menu_model_filter_by_menu(XfdashboardApplicationsMenuModel *self, GarconMenu *inMenu);
-void xfdashboard_applications_menu_model_filter_by_section(XfdashboardApplicationsMenuModel *self, GarconMenu *inSection);
+void xfdashboard_applications_menu_model_filter_by_menu(XfdashboardApplicationsMenuModel *self,
+														GarconMenu *inMenu);
+void xfdashboard_applications_menu_model_filter_by_section(XfdashboardApplicationsMenuModel *self,
+															GarconMenu *inSection);
 
 G_END_DECLS
 
diff --git a/libxfdashboard/applications-view.c b/libxfdashboard/applications-view.c
index 4c2233d..8ddb76f 100644
--- a/libxfdashboard/applications-view.c
+++ b/libxfdashboard/applications-view.c
@@ -471,7 +471,7 @@ static void _xfdashboard_applications_view_on_all_applications_menu_clicked(Xfda
 static void _xfdashboard_applications_view_on_filter_changed(XfdashboardApplicationsView *self, gpointer inUserData)
 {
 	XfdashboardApplicationsViewPrivate	*priv;
-	ClutterModelIter					*iterator;
+	XfdashboardModelIter				*iterator;
 	ClutterActor						*actor;
 	GarconMenuElement					*menuElement=NULL;
 	GarconMenu							*parentMenu=NULL;
@@ -552,15 +552,19 @@ static void _xfdashboard_applications_view_on_filter_changed(XfdashboardApplicat
 	}
 
 	/* Iterate through (filtered) data model and create actor for each entry */
-	iterator=clutter_model_get_first_iter(CLUTTER_MODEL(priv->apps));
-	if(iterator && CLUTTER_IS_MODEL_ITER(iterator))
+	iterator=xfdashboard_model_iter_new(XFDASHBOARD_MODEL(priv->apps));
+	if(iterator)
 	{
-		while(!clutter_model_iter_is_last(iterator))
+		while(xfdashboard_model_iter_next(iterator))
 		{
+			/* If row is filtered continue with next one immediately */
+			if(!xfdashboard_model_iter_filter(iterator)) continue;
+
 			/* Get data from model */
-			clutter_model_iter_get(iterator,
-									XFDASHBOARD_APPLICATIONS_MENU_MODEL_COLUMN_MENU_ELEMENT, &menuElement,
-									-1);
+			xfdashboard_applications_menu_model_get(priv->apps,
+													iterator,
+													XFDASHBOARD_APPLICATIONS_MENU_MODEL_COLUMN_MENU_ELEMENT, &menuElement,
+													-1);
 
 			if(!menuElement) continue;
 
@@ -579,22 +583,27 @@ static void _xfdashboard_applications_view_on_filter_changed(XfdashboardApplicat
 				{
 					gchar		*actorText;
 					const gchar	*iconName;
+					const gchar	*title;
+					const gchar	*description;
 
 					actor=xfdashboard_button_new();
 
 					iconName=garcon_menu_element_get_icon_name(menuElement);
 					if(iconName) xfdashboard_button_set_icon_name(XFDASHBOARD_BUTTON(actor), iconName);
 
+					title=garcon_menu_element_get_name(menuElement);
+					description=garcon_menu_element_get_comment(menuElement);
+
 					if(priv->viewMode==XFDASHBOARD_VIEW_MODE_LIST)
 					{
 						actorText=g_markup_printf_escaped(priv->formatTitleDescription,
-															garcon_menu_element_get_name(menuElement),
-															garcon_menu_element_get_comment(menuElement));
+															title ? title : "",
+															description ? description : "");
 					}
 						else
 						{
 							actorText=g_markup_printf_escaped(priv->formatTitleOnly,
-																garcon_menu_element_get_name(menuElement));
+																title ? title : "");
 						}
 					xfdashboard_button_set_text(XFDASHBOARD_BUTTON(actor), actorText);
 					g_free(actorText);
@@ -628,9 +637,6 @@ static void _xfdashboard_applications_view_on_filter_changed(XfdashboardApplicat
 			/* Release allocated resources */
 			g_object_unref(menuElement);
 			menuElement=NULL;
-
-			/* Go to next entry in model */
-			iterator=clutter_model_iter_next(iterator);
 		}
 		g_object_unref(iterator);
 	}
@@ -1406,7 +1412,6 @@ static void xfdashboard_applications_view_init(XfdashboardApplicationsView *self
 
 	xfdashboard_view_set_view_fit_mode(XFDASHBOARD_VIEW(self), XFDASHBOARD_VIEW_FIT_MODE_HORIZONTAL);
 	xfdashboard_applications_view_set_view_mode(self, XFDASHBOARD_VIEW_MODE_LIST);
-	clutter_model_set_sorting_column(CLUTTER_MODEL(priv->apps), XFDASHBOARD_APPLICATIONS_MENU_MODEL_COLUMN_TITLE);
 
 	/* Connect signals */
 	g_signal_connect_swapped(priv->apps, "filter-changed", G_CALLBACK(_xfdashboard_applications_view_on_filter_changed), self);
diff --git a/libxfdashboard/model.c b/libxfdashboard/model.c
new file mode 100644
index 0000000..7aaf801
--- /dev/null
+++ b/libxfdashboard/model.c
@@ -0,0 +1,1306 @@
+/*
+ * model: A simple and generic data model holding one value per row
+ * 
+ * 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.
+ * 
+ * 
+ */
+
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+
+#include <libxfdashboard/model.h>
+
+#include <glib/gi18n-lib.h>
+
+
+/* Define theses classes in GObject system */
+G_DEFINE_TYPE(XfdashboardModel,
+				xfdashboard_model,
+				G_TYPE_OBJECT);
+
+G_DEFINE_TYPE(XfdashboardModelIter,
+				xfdashboard_model_iter,
+				G_TYPE_OBJECT);
+
+/* Private structure - access only by public API if needed */
+#define XFDASHBOARD_MODEL_GET_PRIVATE(obj) \
+	(G_TYPE_INSTANCE_GET_PRIVATE((obj), XFDASHBOARD_TYPE_MODEL, XfdashboardModelPrivate))
+
+struct _XfdashboardModelPrivate
+{
+	/* Instance related */
+	GSequence					*data;
+	GDestroyNotify				freeDataCallback;
+
+	XfdashboardModelSortFunc	sortCallback;
+	gpointer					sortUserData;
+	GDestroyNotify				sortUserDataDestroyCallback;
+
+	XfdashboardModelFilterFunc	filterCallback;
+	gpointer					filterUserData;
+	GDestroyNotify				filterUserDataDestroyCallback;
+};
+
+#define XFDASHBOARD_MODEL_ITER_GET_PRIVATE(obj) \
+	(G_TYPE_INSTANCE_GET_PRIVATE((obj), XFDASHBOARD_TYPE_MODEL_ITER, XfdashboardModelIterPrivate))
+
+struct _XfdashboardModelIterPrivate
+{
+	/* Instance related */
+	XfdashboardModel			*model;
+	GSequenceIter				*iter;
+};
+
+/* Properties */
+enum
+{
+	PROP_0,
+
+	PROP_ROWS,
+	PROP_SORT_SET,
+	PROP_FILTER_SET,
+	PROP_FREE_DATA_CALLBACK,
+
+	PROP_LAST
+};
+
+static GParamSpec* XfdashboardModelProperties[PROP_LAST]={ 0, };
+
+/* Signals */
+enum
+{
+	SIGNAL_ROW_ADDED,
+	SIGNAL_ROW_REMOVED,
+	SIGNAL_ROW_CHANGED,
+	SIGNAL_SORT_CHANGED,
+	SIGNAL_FILTER_CHANGED,
+
+	SIGNAL_LAST
+};
+
+static guint XfdashboardModelSignals[SIGNAL_LAST]={ 0, };
+
+
+/* IMPLEMENTATION: Private variables and methods */
+
+typedef struct _XfdashboardModelSortData			XfdashboardModelSortData;
+struct _XfdashboardModelSortData
+{
+	XfdashboardModel			*model;
+	XfdashboardModelIter		*leftIter;
+	XfdashboardModelIter		*rightIter;
+};
+
+/* Checks for valid iterator for model */
+static gboolean _xfdashboard_model_iter_is_valid(XfdashboardModelIter *self, gboolean inNeedsIter)
+{
+	XfdashboardModelIterPrivate		*priv;
+
+	g_return_val_if_fail(XFDASHBOARD_IS_MODEL_ITER(self), FALSE);
+
+	priv=self->priv;
+
+	/* Check if model is set */
+	if(!priv->model) return(FALSE);
+
+	/* Check if an iterator is set when an iterator is needed */
+	if(inNeedsIter && !priv->iter) return(FALSE);
+
+	/* Check if an iterator is set and if it is then check if associated
+	 * GSequence at iterator matches the one associated with the model.
+	 * If an iterator is needed the check before ensures that in this check
+	 * an iterator exists and the check will be performed.
+	 */
+	if(priv->iter)
+	{
+		if(g_sequence_iter_get_sequence(priv->iter)!=priv->model->priv->data) return(FALSE);
+	}
+
+	/* If we get here all tests are passed successfully and iterator is valid */
+	return(TRUE);
+}
+
+/* Checks if requested row is valid in this model */
+static gboolean _xfdashboard_model_is_valid_row(XfdashboardModel *self, gint inRow)
+{
+	XfdashboardModelPrivate			*priv;
+
+	g_return_val_if_fail(XFDASHBOARD_IS_MODEL(self), FALSE);
+
+	priv=self->priv;
+
+	/* Check if row is a positive number and smaller than the total numbers
+	 * of rows in model's data.
+	 */
+	if(inRow<0 || inRow>=g_sequence_get_length(priv->data)) return(FALSE);
+
+	/* If we get here the requested row is within model's data and valid */
+	return(TRUE);
+}
+
+/* Internal callback function for sorting which creates iterators used for
+ * user supplied callback function.
+ */
+static gint _xfdashboard_model_sort_internal(GSequenceIter *inLeft,
+												GSequenceIter *inRight,
+												gpointer inUserData)
+{
+	XfdashboardModelSortData		*sortData;
+	XfdashboardModelPrivate			*priv;
+	gint							result;
+
+	g_return_val_if_fail(inLeft, 0);
+	g_return_val_if_fail(inRight, 0);
+	g_return_val_if_fail(inUserData, 0);
+
+	sortData=(XfdashboardModelSortData*)inUserData;
+	priv=sortData->model->priv;
+
+	/* Update iterators to pass to user supplied sort callback function */
+	sortData->leftIter->priv->iter=inLeft;
+	sortData->rightIter->priv->iter=inRight;
+
+	/* Call user supplied sort callback function and return its result */
+	result=(priv->sortCallback)(sortData->leftIter,
+								sortData->rightIter,
+								priv->sortUserData);
+
+	/* Return result of user supplied sort callback function */
+	return(result);
+}
+
+/* IMPLEMENTATION: GObject */
+
+/* Dispose this object of type XfdashboardModel */
+static void _xfdashboard_model_dispose(GObject *inObject)
+{
+	XfdashboardModel				*self=XFDASHBOARD_MODEL(inObject);
+	XfdashboardModelPrivate			*priv=self->priv;
+
+	/* Release our allocated variables */
+	if(priv->sortUserData &&
+		priv->sortUserDataDestroyCallback)
+	{
+		(priv->sortUserDataDestroyCallback)(priv->sortUserData);
+	}
+	priv->sortUserDataDestroyCallback=NULL;
+	priv->sortUserData=NULL;
+	priv->sortCallback=NULL;
+
+	if(priv->filterUserData &&
+		priv->filterUserDataDestroyCallback)
+	{
+		(priv->filterUserDataDestroyCallback)(priv->filterUserData);
+	}
+	priv->filterUserDataDestroyCallback=NULL;
+	priv->filterUserData=NULL;
+	priv->filterCallback=NULL;
+
+	if(priv->data)
+	{
+		g_sequence_free(priv->data);
+		priv->data=NULL;
+	}
+	priv->freeDataCallback=NULL;
+
+	/* Call parent's class dispose method */
+	G_OBJECT_CLASS(xfdashboard_model_parent_class)->dispose(inObject);
+}
+
+/* Set/get properties of type XfdashboardModel */
+static void _xfdashboard_model_set_property(GObject *inObject,
+											guint inPropID,
+											const GValue *inValue,
+											GParamSpec *inSpec)
+{
+	XfdashboardModel				*self=XFDASHBOARD_MODEL(inObject);
+
+	switch(inPropID)
+	{
+		case PROP_FREE_DATA_CALLBACK:
+			self->priv->freeDataCallback=g_value_get_pointer(inValue);
+			break;
+
+		default:
+			G_OBJECT_WARN_INVALID_PROPERTY_ID(inObject, inPropID, inSpec);
+			break;
+	}
+}
+
+static void _xfdashboard_model_get_property(GObject *inObject,
+											guint inPropID,
+											GValue *outValue,
+											GParamSpec *inSpec)
+{
+	XfdashboardModel				*self=XFDASHBOARD_MODEL(inObject);
+
+	switch(inPropID)
+	{
+		case PROP_ROWS:
+			g_value_set_int(outValue, xfdashboard_model_get_rows_count(self));
+			break;
+
+		case PROP_SORT_SET:
+			g_value_set_boolean(outValue, xfdashboard_model_is_sorted(self));
+			break;
+
+		case PROP_FILTER_SET:
+			g_value_set_boolean(outValue, xfdashboard_model_is_filtered(self));
+			break;
+
+		default:
+			G_OBJECT_WARN_INVALID_PROPERTY_ID(inObject, inPropID, inSpec);
+			break;
+	}
+}
+
+/* Class initialization of type XfdashboardModel
+ * Override functions in parent classes and define properties
+ * and signals
+ */
+static void xfdashboard_model_class_init(XfdashboardModelClass *klass)
+{
+	GObjectClass			*gobjectClass=G_OBJECT_CLASS(klass);
+
+	/* Override functions */
+	gobjectClass->dispose=_xfdashboard_model_dispose;
+	gobjectClass->set_property=_xfdashboard_model_set_property;
+	gobjectClass->get_property=_xfdashboard_model_get_property;
+
+	/* Set up private structure */
+	g_type_class_add_private(klass, sizeof(XfdashboardModelPrivate));
+
+	/* Define properties */
+	XfdashboardModelProperties[PROP_ROWS]=
+		g_param_spec_int("rows",
+							_("Rows"),
+							_("The number of rows this model contains"),
+							0, G_MAXINT,
+							0,
+							G_PARAM_READABLE | G_PARAM_STATIC_STRINGS);
+
+	XfdashboardModelProperties[PROP_SORT_SET]=
+		g_param_spec_boolean("sort-set",
+								_("Sort set"),
+								_("Whether a sorting function is set or not"),
+								FALSE,
+								G_PARAM_READABLE | G_PARAM_STATIC_STRINGS);
+
+	XfdashboardModelProperties[PROP_FILTER_SET]=
+		g_param_spec_boolean("filter-set",
+								_("Filter set"),
+								_("Whether a filter is set or not"),
+								FALSE,
+								G_PARAM_READABLE | G_PARAM_STATIC_STRINGS);
+
+	XfdashboardModelProperties[PROP_FREE_DATA_CALLBACK]=
+		g_param_spec_pointer("free-data-callback",
+								_("Free data callback"),
+								_("Callback function to free data when removing or overwriting data in model"),
+								G_PARAM_WRITABLE | G_PARAM_CONSTRUCT_ONLY | G_PARAM_STATIC_STRINGS);
+
+	g_object_class_install_properties(gobjectClass, PROP_LAST, XfdashboardModelProperties);
+
+	/* Define signals */
+	XfdashboardModelSignals[SIGNAL_ROW_ADDED]=
+		g_signal_new("row-added",
+						G_TYPE_FROM_CLASS(klass),
+						G_SIGNAL_RUN_LAST,
+						G_STRUCT_OFFSET(XfdashboardModelClass, row_added),
+						NULL,
+						NULL,
+						g_cclosure_marshal_VOID__OBJECT,
+						G_TYPE_NONE,
+						1,
+						XFDASHBOARD_TYPE_MODEL_ITER);
+
+	XfdashboardModelSignals[SIGNAL_ROW_REMOVED]=
+		g_signal_new("row-removed",
+						G_TYPE_FROM_CLASS(klass),
+						G_SIGNAL_RUN_LAST,
+						G_STRUCT_OFFSET(XfdashboardModelClass, row_removed),
+						NULL,
+						NULL,
+						g_cclosure_marshal_VOID__OBJECT,
+						G_TYPE_NONE,
+						1,
+						XFDASHBOARD_TYPE_MODEL_ITER);
+
+	XfdashboardModelSignals[SIGNAL_ROW_CHANGED]=
+		g_signal_new("row-changed",
+						G_TYPE_FROM_CLASS(klass),
+						G_SIGNAL_RUN_LAST,
+						G_STRUCT_OFFSET(XfdashboardModelClass, row_changed),
+						NULL,
+						NULL,
+						g_cclosure_marshal_VOID__OBJECT,
+						G_TYPE_NONE,
+						1,
+						XFDASHBOARD_TYPE_MODEL_ITER);
+
+	XfdashboardModelSignals[SIGNAL_SORT_CHANGED]=
+		g_signal_new("sort-changed",
+						G_TYPE_FROM_CLASS(klass),
+						G_SIGNAL_RUN_LAST,
+						G_STRUCT_OFFSET(XfdashboardModelClass, sort_changed),
+						NULL,
+						NULL,
+						g_cclosure_marshal_VOID__VOID,
+						G_TYPE_NONE,
+						0);
+
+	XfdashboardModelSignals[SIGNAL_FILTER_CHANGED]=
+		g_signal_new("filter-changed",
+						G_TYPE_FROM_CLASS(klass),
+						G_SIGNAL_RUN_LAST,
+						G_STRUCT_OFFSET(XfdashboardModelClass, filter_changed),
+						NULL,
+						NULL,
+						g_cclosure_marshal_VOID__VOID,
+						G_TYPE_NONE,
+						0);
+}
+
+/* Object initialization of type XfdashboardModel
+ * Create private structure and set up default values
+ */
+static void xfdashboard_model_init(XfdashboardModel *self)
+{
+	XfdashboardModelPrivate			*priv;
+
+	priv=self->priv=XFDASHBOARD_MODEL_GET_PRIVATE(self);
+
+	/* Set up default values */
+	priv->data=g_sequence_new(NULL);
+	priv->freeDataCallback=NULL;
+
+	priv->sortCallback=NULL;
+	priv->sortUserData=NULL;
+	priv->sortUserDataDestroyCallback=NULL;
+
+	priv->filterCallback=NULL;
+	priv->filterUserData=NULL;
+	priv->filterUserDataDestroyCallback=NULL;
+}
+
+/* Dispose this object of type XfdashboardModelIter */
+static void _xfdashboard_model_iter_dispose(GObject *inObject)
+{
+	XfdashboardModelIter			*self=XFDASHBOARD_MODEL_ITER(inObject);
+	XfdashboardModelIterPrivate		*priv=self->priv;
+
+	/* Release our allocated variables */
+	if(priv->model)
+	{
+		g_object_unref(priv->model);
+		priv->model=NULL;
+	}
+
+	priv->iter=NULL;
+
+	/* Call parent's class dispose method */
+	G_OBJECT_CLASS(xfdashboard_model_iter_parent_class)->dispose(inObject);
+}
+
+/* Class initialization of type XfdashboardModelIter
+ * Override functions in parent classes and define properties
+ * and signals
+ */
+static void xfdashboard_model_iter_class_init(XfdashboardModelIterClass *klass)
+{
+	GObjectClass			*gobjectClass=G_OBJECT_CLASS(klass);
+
+	/* Override functions */
+	gobjectClass->dispose=_xfdashboard_model_iter_dispose;
+
+	/* Set up private structure */
+	g_type_class_add_private(klass, sizeof(XfdashboardModelIterPrivate));
+}
+
+/* Object initialization of type XfdashboardModelIter
+ * Create private structure and set up default values
+ */
+static void xfdashboard_model_iter_init(XfdashboardModelIter *self)
+{
+	XfdashboardModelIterPrivate		*priv;
+
+	priv=self->priv=XFDASHBOARD_MODEL_ITER_GET_PRIVATE(self);
+
+	/* Set up default values */
+	priv->model=NULL;
+	priv->iter=NULL;
+}
+
+
+/* IMPLEMENTATION: Public API of XfdashboardModel */
+
+/* Model creation functions */
+XfdashboardModel* xfdashboard_model_new(void)
+{
+	GObject		*model;
+
+	model=g_object_new(XFDASHBOARD_TYPE_MODEL, NULL);
+	if(!model) return(NULL);
+
+	return(XFDASHBOARD_MODEL(model));
+}
+
+/* Return number of rows in this model */
+gint xfdashboard_model_get_rows_count(XfdashboardModel *self)
+{
+	g_return_val_if_fail(XFDASHBOARD_IS_MODEL(self), 0);
+
+	return(g_sequence_get_length(self->priv->data));
+}
+
+/* Get item at requested row of this model */
+gpointer xfdashboard_model_get(XfdashboardModel *self, gint inRow)
+{
+	XfdashboardModelPrivate			*priv;
+	GSequenceIter					*iter;
+	gpointer						item;
+
+	g_return_val_if_fail(XFDASHBOARD_IS_MODEL(self), NULL);
+	g_return_val_if_fail(_xfdashboard_model_is_valid_row(self, inRow), NULL);
+
+	priv=self->priv;
+	item=NULL;
+
+	/* Get iterator at requested row */
+	iter=g_sequence_get_iter_at_pos(priv->data, inRow);
+	if(iter)
+	{
+		/* Get item from iterator */
+		item=g_sequence_get(iter);
+	}
+
+	/* Return item found */
+	return(item);
+}
+
+/* Add a new item to end of model's data */
+gboolean xfdashboard_model_append(XfdashboardModel *self,
+									gpointer inData,
+									XfdashboardModelIter **outIter)
+{
+	XfdashboardModelPrivate			*priv;
+	XfdashboardModelIter			*iter;
+	GSequenceIter					*seqIter;
+
+	g_return_val_if_fail(XFDASHBOARD_IS_MODEL(self), FALSE);
+	g_return_val_if_fail(outIter==NULL || *outIter==NULL, FALSE);
+
+	priv=self->priv;
+
+	/* Append data to model's data */
+	seqIter=g_sequence_append(priv->data, inData);
+
+	/* Create iterator for returned sequence iterator */
+	iter=xfdashboard_model_iter_new(self);
+	iter->priv->iter=seqIter;
+
+	/* Emit signal */
+	g_signal_emit(self, XfdashboardModelSignals[SIGNAL_ROW_ADDED], 0, iter);
+
+	/* Store iterator if callee requested it */
+	if(outIter) *outIter=XFDASHBOARD_MODEL_ITER(g_object_ref(iter));
+
+	/* Release allocated resources */
+	if(iter) g_object_unref(iter);
+
+	/* Return TRUE for success */
+	return(TRUE);
+}
+
+/* Add a new item to begin of model's data */
+gboolean xfdashboard_model_prepend(XfdashboardModel *self,
+									gpointer inData,
+									XfdashboardModelIter **outIter)
+{
+	XfdashboardModelPrivate			*priv;
+	XfdashboardModelIter			*iter;
+	GSequenceIter					*seqIter;
+
+	g_return_val_if_fail(XFDASHBOARD_IS_MODEL(self), FALSE);
+	g_return_val_if_fail(outIter==NULL || *outIter==NULL, FALSE);
+
+	priv=self->priv;
+
+	/* Append data to model's data */
+	seqIter=g_sequence_prepend(priv->data, inData);
+
+	/* Create iterator for returned sequence iterator */
+	iter=xfdashboard_model_iter_new(self);
+	iter->priv->iter=seqIter;
+
+	/* Emit signal */
+	g_signal_emit(self, XfdashboardModelSignals[SIGNAL_ROW_ADDED], 0, iter);
+
+	/* Store iterator if callee requested it */
+	if(outIter) *outIter=XFDASHBOARD_MODEL_ITER(g_object_ref(iter));
+
+	/* Release allocated resources */
+	if(iter) g_object_unref(iter);
+
+	/* Return TRUE for success */
+	return(TRUE);
+}
+
+/* Add a new item at requested row (i.e. before the item at requested row)
+ * at model's data.
+ */
+gboolean xfdashboard_model_insert(XfdashboardModel *self,
+									gint inRow,
+									gpointer inData,
+									XfdashboardModelIter **outIter)
+{
+	XfdashboardModelPrivate			*priv;
+	XfdashboardModelIter			*iter;
+	GSequenceIter					*seqIter;
+	GSequenceIter					*insertIter;
+
+	g_return_val_if_fail(XFDASHBOARD_IS_MODEL(self), FALSE);
+	g_return_val_if_fail(_xfdashboard_model_is_valid_row(self, inRow), FALSE);
+	g_return_val_if_fail(outIter==NULL || *outIter==NULL, FALSE);
+
+	priv=self->priv;
+
+	/* Create sequence iterator where to insert new data at */
+	insertIter=g_sequence_get_iter_at_pos(priv->data, inRow);
+
+	/* Insert data at "insert iterator" at model's data */
+	seqIter=g_sequence_insert_before(insertIter, inData);
+
+	/* Create iterator for returned sequence iterator */
+	iter=xfdashboard_model_iter_new(self);
+	iter->priv->iter=seqIter;
+
+	/* Emit signal */
+	g_signal_emit(self, XfdashboardModelSignals[SIGNAL_ROW_ADDED], 0, iter);
+
+	/* Store iterator if callee requested it */
+	if(outIter) *outIter=XFDASHBOARD_MODEL_ITER(g_object_ref(iter));
+
+	/* Release allocated resources */
+	if(iter) g_object_unref(iter);
+
+	/* Return TRUE for success */
+	return(TRUE);
+}
+
+/* Set or replace data at iterator */
+gboolean xfdashboard_model_set(XfdashboardModel *self,
+								gint inRow,
+								gpointer inData,
+								XfdashboardModelIter **outIter)
+{
+	XfdashboardModelPrivate			*priv;
+	XfdashboardModelIter			*iter;
+	GSequenceIter					*seqIter;
+
+	g_return_val_if_fail(XFDASHBOARD_IS_MODEL(self), FALSE);
+	g_return_val_if_fail(_xfdashboard_model_is_valid_row(self, inRow), FALSE);
+
+	priv=self->priv;
+
+	/* Create sequence iterator to row which is set */
+	seqIter=g_sequence_get_iter_at_pos(priv->data, inRow);
+
+	/* If a function is provided to free data on removal then call it now */
+	if(priv->freeDataCallback)
+	{
+		gpointer					oldData;
+
+		/* Get data to remove */
+		oldData=g_sequence_get(seqIter);
+
+		/* Call function to free data */
+		(priv->freeDataCallback)(oldData);
+	}
+
+	/* Set new data at iterator */
+	g_sequence_set(seqIter, inData);
+
+	/* Create iterator for returned sequence iterator */
+	iter=xfdashboard_model_iter_new(self);
+	iter->priv->iter=seqIter;
+
+	/* Emit signal */
+	g_signal_emit(self, XfdashboardModelSignals[SIGNAL_ROW_CHANGED], 0, iter);
+
+	/* Store iterator if callee requested it */
+	if(outIter) *outIter=XFDASHBOARD_MODEL_ITER(g_object_ref(iter));
+
+	/* Release allocated resources */
+	if(iter) g_object_unref(iter);
+
+	/* Return TRUE for success */
+	return(TRUE);
+}
+
+/* Remove data at requested row from model's data */
+gboolean xfdashboard_model_remove(XfdashboardModel *self, gint inRow)
+{
+	XfdashboardModelPrivate			*priv;
+	XfdashboardModelIter			*iter;
+	GSequenceIter					*seqIter;
+
+	g_return_val_if_fail(XFDASHBOARD_IS_MODEL(self), FALSE);
+	g_return_val_if_fail(_xfdashboard_model_is_valid_row(self, inRow), FALSE);
+
+	priv=self->priv;
+
+	/* Create sequence iterator to row which is to remove */
+	seqIter=g_sequence_get_iter_at_pos(priv->data, inRow);
+
+	/* Create iterator for returned sequence iterator */
+	iter=xfdashboard_model_iter_new(self);
+	iter->priv->iter=seqIter;
+
+	/* Emit signal before removal to give signal handlers a changed
+	 * to access the data at iterator a last time.
+	 */
+	g_signal_emit(self, XfdashboardModelSignals[SIGNAL_ROW_REMOVED], 0, iter);
+
+	/* If a function is provided to free data on removal then call it now */
+	if(priv->freeDataCallback)
+	{
+		gpointer					oldData;
+
+		/* Get data to remove */
+		oldData=g_sequence_get(seqIter);
+
+		/* Call function to free data */
+		(priv->freeDataCallback)(oldData);
+	}
+
+	/* Remove data from model's data */
+	g_sequence_remove(seqIter);
+
+	/* Release allocated resources */
+	if(iter) g_object_unref(iter);
+
+	/* Return TRUE for success */
+	return(TRUE);
+}
+
+/* Remove all data from model's data */
+void xfdashboard_model_remove_all(XfdashboardModel *self)
+{
+	XfdashboardModelPrivate			*priv;
+	XfdashboardModelIter			*iter;
+
+	g_return_if_fail(XFDASHBOARD_IS_MODEL(self));
+
+	priv=self->priv;
+
+	/* Create iterator used to iterate through all items in model's data
+	 * and it is used when emitting signal.
+	 */
+	iter=xfdashboard_model_iter_new(self);
+	iter->priv->iter=g_sequence_get_begin_iter(priv->data);
+
+	/* Iterate through all items in model's data, emit signal for each item
+	 * being remove and remove them finally. If model provides a function to
+	 * free data call it with the item to remove.
+	 */
+	while(!g_sequence_iter_is_end(iter->priv->iter))
+	{
+		/* Emit signal before removal to give signal handlers a changed
+		 * to access the data at iterator a last time.
+		 */
+		g_signal_emit(self, XfdashboardModelSignals[SIGNAL_ROW_REMOVED], 0, iter);
+
+		/* If a function is provided to free data on removal then call it now */
+		if(priv->freeDataCallback)
+		{
+			gpointer					oldData;
+
+			/* Get data to remove */
+			oldData=g_sequence_get(iter->priv->iter);
+
+			/* Call function to free data */
+			(priv->freeDataCallback)(oldData);
+		}
+
+		/* Remove data from model's data */
+		g_sequence_remove(iter->priv->iter);
+
+		/* Move iterator to next item in model's data */
+		iter->priv->iter=g_sequence_iter_next(iter->priv->iter);
+	}
+
+	/* Release allocated resources */
+	if(iter) g_object_unref(iter);
+}
+
+/* Iterate through all items in model's data and call user supplied callback
+ * function for each item.
+ */
+void xfdashboard_model_foreach(XfdashboardModel *self,
+								XfdashboardModelForeachFunc inForeachCallback,
+								gpointer inUserData)
+{
+	XfdashboardModelIter			*iter;
+	gpointer						item;
+
+	g_return_if_fail(XFDASHBOARD_IS_MODEL(self));
+	g_return_if_fail(inForeachCallback);
+
+	/* Iterate through all items in model's data */
+	/* Call user supplied callback function */
+	iter=xfdashboard_model_iter_new(self);
+	while(xfdashboard_model_iter_next(iter))
+	{
+		/* Get item at position the iterator points to */
+		item=xfdashboard_model_iter_get(iter);
+
+		/* Call user supplied callback function */
+		(inForeachCallback)(iter, item, inUserData);
+	}
+
+	/* Release allocated resources */
+	if(iter) g_object_unref(iter);
+}
+
+/* Model sort functions */
+gboolean xfdashboard_model_is_sorted(XfdashboardModel *self)
+{
+	XfdashboardModelPrivate			*priv;
+
+	g_return_val_if_fail(XFDASHBOARD_IS_MODEL(self), FALSE);
+
+	priv=self->priv;
+
+	/* If a sort function is set then return TRUE ... */
+	if(priv->sortCallback) return(TRUE);
+
+	/* ... otherwise FALSE */
+	return(FALSE);
+}
+
+/* Set sorting function */
+void xfdashboard_model_set_sort(XfdashboardModel *self,
+								XfdashboardModelSortFunc inSortCallback,
+								gpointer inUserData,
+								GDestroyNotify inUserDataDestroyCallback)
+{
+	XfdashboardModelPrivate			*priv;
+
+	g_return_if_fail(XFDASHBOARD_IS_MODEL(self));
+
+	priv=self->priv;
+
+	/* Set value if changed */
+	if(priv->sortCallback!=inSortCallback ||
+		priv->sortUserData!=inUserData ||
+		priv->sortUserDataDestroyCallback!=inUserDataDestroyCallback)
+	{
+		gboolean				oldSortIsSet;
+		gboolean				newSortIsSet;
+
+		/* Get old "sort-set" value. It is used later to determine if this
+		 * property has changed also.
+		 */
+		oldSortIsSet=xfdashboard_model_is_sorted(self);
+
+		/* Release old values */
+		if(priv->sortUserData &&
+			priv->sortUserDataDestroyCallback)
+		{
+			(priv->sortUserDataDestroyCallback)(priv->sortUserData);
+		}
+		priv->sortUserDataDestroyCallback=NULL;
+		priv->sortUserData=NULL;
+		priv->sortCallback=NULL;
+
+		/* Set value */
+		priv->sortCallback=inSortCallback;
+		priv->sortUserData=inUserData;
+		priv->sortUserDataDestroyCallback=inUserDataDestroyCallback;
+
+		/* Get new "sort-set" value to determine if this property has
+		 * changed also.
+		 */
+		newSortIsSet=xfdashboard_model_is_sorted(self);
+
+		/* Sort model if sort function is set */
+		if(newSortIsSet) xfdashboard_model_resort(self);
+
+		/* Notify about change of 'sort-set' if changed and after model
+		 * was sorted.
+		 */
+		if(oldSortIsSet!=newSortIsSet)
+		{
+			g_object_notify_by_pspec(G_OBJECT(self), XfdashboardModelProperties[PROP_SORT_SET]);
+		}
+
+		/* Emit signal that sorting has changed */
+		g_signal_emit(self, XfdashboardModelSignals[SIGNAL_SORT_CHANGED], 0);
+	}
+}
+
+/* Resort this model's data with sorting function set */
+void xfdashboard_model_resort(XfdashboardModel *self)
+{
+	XfdashboardModelPrivate			*priv;
+	XfdashboardModelSortData		sortData;
+
+	g_return_if_fail(XFDASHBOARD_IS_MODEL(self));
+
+	priv=self->priv;
+
+	/* If no sort function is set return immediately because this model
+	 * cannot be sorted without a sort function.
+	 */
+	if(!priv->sortCallback) return;
+
+	/* Set up sort data which wraps all needed information into a structure.
+	 * The interators are pre-initialized and updated in internal sort function
+	 * which is passed to GSequence's sort function. This internal callback
+	 * only updates the existing iterators to reduce creation and destructions
+	 * of these iterator. This can be done because the model does not change
+	 * while sorting.
+	 */
+	sortData.model=XFDASHBOARD_MODEL(g_object_ref(self));
+	sortData.leftIter=xfdashboard_model_iter_new(self);
+	sortData.rightIter=xfdashboard_model_iter_new(self);
+
+	/* Sort this model again by using internal sorting function which
+	 * calls user's sort function with adjusted parameters.
+	 */
+	g_sequence_sort_iter(priv->data, _xfdashboard_model_sort_internal, &sortData);
+
+	/* Release allocated resources */
+	if(sortData.model) g_object_unref(sortData.model);
+	if(sortData.leftIter) g_object_unref(sortData.leftIter);
+	if(sortData.rightIter) g_object_unref(sortData.rightIter);
+}
+
+/* Model filter functions */
+gboolean xfdashboard_model_is_filtered(XfdashboardModel *self)
+{
+	XfdashboardModelPrivate			*priv;
+
+	g_return_val_if_fail(XFDASHBOARD_IS_MODEL(self), FALSE);
+
+	priv=self->priv;
+
+	/* If a filter function is set then return TRUE ... */
+	if(priv->filterCallback) return(TRUE);
+
+	/* ... otherwise FALSE */
+	return(FALSE);
+}
+
+/* Set filter function */
+void xfdashboard_model_set_filter(XfdashboardModel *self,
+									XfdashboardModelFilterFunc inFilterCallback,
+									gpointer inUserData,
+									GDestroyNotify inUserDataDestroyCallback)
+{
+	XfdashboardModelPrivate			*priv;
+
+	g_return_if_fail(XFDASHBOARD_IS_MODEL(self));
+
+	priv=self->priv;
+
+	/* Set value if changed */
+	if(priv->filterCallback!=inFilterCallback ||
+		priv->filterUserData!=inUserData ||
+		priv->filterUserDataDestroyCallback!=inUserDataDestroyCallback)
+	{
+		gboolean				oldFilterIsSet;
+		gboolean				newFilterIsSet;
+
+		/* Get old "filter-set" value. It is used later to determine if this
+		 * property has changed also.
+		 */
+		oldFilterIsSet=xfdashboard_model_is_filtered(self);
+
+		/* Release old values */
+		if(priv->filterUserData &&
+			priv->filterUserDataDestroyCallback)
+		{
+			(priv->filterUserDataDestroyCallback)(priv->filterUserData);
+		}
+		priv->filterUserDataDestroyCallback=NULL;
+		priv->filterUserData=NULL;
+		priv->filterCallback=NULL;
+
+		/* Set value */
+		priv->filterCallback=inFilterCallback;
+		priv->filterUserData=inUserData;
+		priv->filterUserDataDestroyCallback=inUserDataDestroyCallback;
+
+		/* Get new "sort-set" value to determine if this property has
+		 * changed also.
+		 */
+		newFilterIsSet=xfdashboard_model_is_filtered(self);
+
+		/* Notify about change of 'sort-set' if changed and after model
+		 * was sorted.
+		 */
+		if(oldFilterIsSet!=newFilterIsSet)
+		{
+			g_object_notify_by_pspec(G_OBJECT(self), XfdashboardModelProperties[PROP_FILTER_SET]);
+		}
+
+		/* Emit signal that filter has changed */
+		g_signal_emit(self, XfdashboardModelSignals[SIGNAL_FILTER_CHANGED], 0);
+	}
+}
+
+/* Check if requested row is filtered */
+gboolean xfdashboard_model_filter_row(XfdashboardModel *self, gint inRow)
+{
+	XfdashboardModelPrivate			*priv;
+	XfdashboardModelIter			*iter;
+	gboolean						isVisible;
+
+	g_return_val_if_fail(XFDASHBOARD_IS_MODEL(self), FALSE);
+	g_return_val_if_fail(_xfdashboard_model_is_valid_row(self, inRow), FALSE);
+
+	priv=self->priv;
+	isVisible=TRUE;
+
+	/* Call user supplied filter callback function to determine if this
+	 * row should be filtered or not but only if filter function is set.
+	 */
+	if(priv->filterCallback)
+	{
+		/* Create iterator */
+		iter=xfdashboard_model_iter_new_for_row(self, inRow);
+
+		/* Determine if row is filtered */
+		isVisible=(priv->filterCallback)(iter, priv->filterUserData);
+
+		/* Release allocated resources */
+		if(iter) g_object_unref(iter);
+	}
+
+	/* Return filter status */
+	return(isVisible);
+}
+
+/* Create iterator for model */
+XfdashboardModelIter* xfdashboard_model_iter_new(XfdashboardModel *inModel)
+{
+	XfdashboardModelIter			*iter;
+
+	g_return_val_if_fail(XFDASHBOARD_IS_MODEL(inModel), NULL);
+
+	/* Create iterator */
+	iter=XFDASHBOARD_MODEL_ITER(g_object_new(XFDASHBOARD_TYPE_MODEL_ITER, NULL));
+
+	/* Set up iterator */
+	iter->priv->model=XFDASHBOARD_MODEL(g_object_ref(inModel));
+	iter->priv->iter=NULL;
+
+	/* Return newly created iterator */
+	return(iter);
+}
+
+/* Create iterator for model at requested row */
+XfdashboardModelIter* xfdashboard_model_iter_new_for_row(XfdashboardModel *inModel, gint inRow)
+{
+	XfdashboardModelIter			*iter;
+	XfdashboardModelPrivate			*modelPriv;
+
+	g_return_val_if_fail(XFDASHBOARD_IS_MODEL(inModel), NULL);
+	g_return_val_if_fail(_xfdashboard_model_is_valid_row(inModel, inRow), NULL);
+
+	modelPriv=inModel->priv;
+
+	/* Create iterator */
+	iter=XFDASHBOARD_MODEL_ITER(g_object_new(XFDASHBOARD_TYPE_MODEL_ITER, NULL));
+
+	/* Set up iterator */
+	iter->priv->model=XFDASHBOARD_MODEL(g_object_ref(inModel));
+	iter->priv->iter=g_sequence_get_iter_at_pos(modelPriv->data, inRow);
+
+	/* Return newly created iterator */
+	return(iter);
+}
+
+/* Create copy of an iterator */
+XfdashboardModelIter* xfdashboard_model_iter_copy(XfdashboardModelIter *self)
+{
+	XfdashboardModelIterPrivate		*priv;
+	XfdashboardModelIter			*copyIter;
+
+	g_return_val_if_fail(XFDASHBOARD_IS_MODEL_ITER(self), NULL);
+
+	priv=self->priv;
+
+	/* Create iterator */
+	copyIter=XFDASHBOARD_MODEL_ITER(g_object_new(XFDASHBOARD_TYPE_MODEL_ITER, NULL));
+
+	/* Set up iterator to be a copy of given iterator */
+	copyIter->priv->model=XFDASHBOARD_MODEL(g_object_ref(priv->model));
+	copyIter->priv->iter=priv->iter;
+
+	/* Return copy of iterator */
+	return(copyIter);
+}
+
+/* Move iterator to next item in model's data */
+gboolean xfdashboard_model_iter_next(XfdashboardModelIter *self)
+{
+	XfdashboardModelIterPrivate		*priv;
+	XfdashboardModelPrivate			*modelPriv;
+	GSequenceIter					*newIter;
+
+	g_return_val_if_fail(_xfdashboard_model_iter_is_valid(self, FALSE), FALSE);
+
+	priv=self->priv;
+	modelPriv=priv->model->priv;
+
+	/* If no iterator was initialized then create an iterator pointing
+	 * to begin of model's data ...
+	 */
+	if(!priv->iter)
+	{
+		/* Get and set iterator pointing to begin of model's data */
+		newIter=g_sequence_get_begin_iter(modelPriv->data);
+	}
+		/* ... otherwise move iterator to next item in model's data */
+		else
+		{
+			/* Move iterator to next item in model's data */
+			newIter=g_sequence_iter_next(priv->iter);
+		}
+
+	/* If iterator is invalid or end of model's data is reached
+	 * return FALSE here.
+	 */
+	if(!newIter ||
+		g_sequence_iter_is_end(newIter))
+	{
+		return(FALSE);
+	}
+
+	/* New iterator is valid so set it */
+	priv->iter=newIter;
+
+	/* If we get here then all went well and we can return TRUE */
+	return(TRUE);
+}
+
+/* Move iterator to previous item in model's data */
+gboolean xfdashboard_model_iter_prev(XfdashboardModelIter *self)
+{
+	XfdashboardModelIterPrivate		*priv;
+	XfdashboardModelPrivate			*modelPriv;
+	GSequenceIter					*newIter;
+
+	g_return_val_if_fail(_xfdashboard_model_iter_is_valid(self, FALSE), FALSE);
+
+	priv=self->priv;
+	modelPriv=priv->model->priv;
+
+	/* If no iterator was initialized then create an iterator pointing
+	 * to end of model's data ...
+	 */
+	if(!priv->iter)
+	{
+		/* Get and set iterator pointing to end of model's data */
+		newIter=g_sequence_get_end_iter(modelPriv->data);
+	}
+		/* ... otherwise move iterator to previous item in model's data */
+		else
+		{
+			/* Move iterator to next item in model's data */
+			newIter=g_sequence_iter_prev(priv->iter);
+		}
+
+	/* If iterator is invalid or begin of model's data is reached
+	 * return FALSE here.
+	 */
+	if(!newIter ||
+		g_sequence_iter_is_begin(newIter))
+	{
+		return(FALSE);
+	}
+
+	/* New iterator is valid so set it */
+	priv->iter=newIter;
+
+	/* If we get here then all went well and we can return TRUE */
+	return(TRUE);
+}
+
+/* Move iterator to requested row in model's data */
+gboolean xfdashboard_model_iter_move_to_row(XfdashboardModelIter *self, gint inRow)
+{
+	XfdashboardModelIterPrivate		*priv;
+	XfdashboardModelPrivate			*modelPriv;
+
+	g_return_val_if_fail(_xfdashboard_model_iter_is_valid(self, FALSE), FALSE);
+
+	priv=self->priv;
+	modelPriv=priv->model->priv;
+
+	/* Check that requested row is within range */
+	g_return_val_if_fail(_xfdashboard_model_is_valid_row(priv->model, inRow), FALSE);
+
+	/* Move iterator to requested row */
+	priv->iter=g_sequence_get_iter_at_pos(modelPriv->data, inRow);
+
+	/* If we get here then all went well and we can return TRUE */
+	return(TRUE);
+}
+
+/* Get model to which this iterator belongs to */
+XfdashboardModel* xfdashboard_model_iter_get_model(XfdashboardModelIter *self)
+{
+	g_return_val_if_fail(_xfdashboard_model_iter_is_valid(self, FALSE), FALSE);
+
+	return(self->priv->model);
+}
+
+/* Get row at model's data this iterator points to currently */
+guint xfdashboard_model_iter_get_row(XfdashboardModelIter *self)
+{
+	XfdashboardModelIterPrivate		*priv;
+	gint							position;
+
+	g_return_val_if_fail(_xfdashboard_model_iter_is_valid(self, TRUE), 0);
+
+	priv=self->priv;
+
+	/* Get position from iterator */
+	position=g_sequence_iter_get_position(priv->iter);
+	if(position<0) position=0;
+
+	/* Return position (maybe corrected for unsigned integer) */
+	return((guint)position);
+}
+
+/* Get item at position and model of this iterator */
+gpointer xfdashboard_model_iter_get(XfdashboardModelIter *self)
+{
+	XfdashboardModelIterPrivate		*priv;
+
+	g_return_val_if_fail(_xfdashboard_model_iter_is_valid(self, TRUE), NULL);
+
+	priv=self->priv;
+
+	/* Get item of model this iterator belongs to */
+	return(g_sequence_get(priv->iter));
+}
+
+/* Set or replace data at iterator */
+gboolean xfdashboard_model_iter_set(XfdashboardModelIter *self, gpointer inData)
+{
+	XfdashboardModelIterPrivate		*priv;
+	XfdashboardModelPrivate			*modelPriv;
+
+	g_return_val_if_fail(_xfdashboard_model_iter_is_valid(self, TRUE), FALSE);
+
+	priv=self->priv;
+	modelPriv=priv->model->priv;
+
+	/* If a function at model is provided to free data on removal
+	 * then call it now.
+	 */
+	if(modelPriv->freeDataCallback)
+	{
+		gpointer					oldData;
+
+		/* Get data to remove */
+		oldData=g_sequence_get(priv->iter);
+
+		/* Call function to free data */
+		(modelPriv->freeDataCallback)(oldData);
+	}
+
+	/* Set new data at iterator */
+	g_sequence_set(priv->iter, inData);
+
+	/* Emit signal */
+	g_signal_emit(self, XfdashboardModelSignals[SIGNAL_ROW_CHANGED], 0, self);
+
+	/* Return TRUE for success */
+	return(TRUE);
+}
+
+/* Remove data at iterator */
+gboolean xfdashboard_model_iter_remove(XfdashboardModelIter *self)
+{
+	XfdashboardModelIterPrivate		*priv;
+	XfdashboardModelPrivate			*modelPriv;
+
+	g_return_val_if_fail(_xfdashboard_model_iter_is_valid(self, TRUE), FALSE);
+
+	priv=self->priv;
+	modelPriv=priv->model->priv;
+
+	/* Emit signal before removal to give signal handlers a changed
+	 * to access the data at iterator a last time.
+	 */
+	g_signal_emit(self, XfdashboardModelSignals[SIGNAL_ROW_REMOVED], 0, self);
+
+	/* If a function at model is provided to free data on removal
+	 * then call it now.
+	 */
+	if(modelPriv->freeDataCallback)
+	{
+		gpointer					oldData;
+
+		/* Get data to remove */
+		oldData=g_sequence_get(priv->iter);
+
+		/* Call function to free data */
+		(modelPriv->freeDataCallback)(oldData);
+	}
+
+	/* Remove data from model's data */
+	g_sequence_remove(priv->iter);
+
+	/* Return TRUE for success */
+	return(TRUE);
+}
+
+/* Check if row is filtered to which this iterator is pointing to */
+gboolean xfdashboard_model_iter_filter(XfdashboardModelIter *self)
+{
+	XfdashboardModelIterPrivate		*priv;
+	XfdashboardModelPrivate			*modelPriv;
+	gboolean						isVisible;
+
+	g_return_val_if_fail(_xfdashboard_model_iter_is_valid(self, TRUE), FALSE);
+
+	priv=self->priv;
+	modelPriv=priv->model->priv;
+	isVisible=TRUE;
+
+	/* Call user supplied filter callback function to determine if this
+	 * row should be filtered or not but only if filter function is set.
+	 */
+	if(modelPriv->filterCallback)
+	{
+		/* Determine if row is filtered */
+		isVisible=(modelPriv->filterCallback)(self, modelPriv->filterUserData);
+	}
+
+	/* Return filter status */
+	return(isVisible);
+}
diff --git a/libxfdashboard/model.h b/libxfdashboard/model.h
new file mode 100644
index 0000000..88eb7f6
--- /dev/null
+++ b/libxfdashboard/model.h
@@ -0,0 +1,196 @@
+/*
+ * model: A simple and generic data model holding one value per row
+ * 
+ * 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_MODEL__
+#define __LIBXFDASHBOARD_MODEL__
+
+#if !defined(__LIBXFDASHBOARD_H_INSIDE__) && !defined(LIBXFDASHBOARD_COMPILATION)
+#error "Only <libxfdashboard/libxfdashboard.h> can be included directly."
+#endif
+
+#include <glib-object.h>
+
+G_BEGIN_DECLS
+
+/* Object declaration: XfdashboardModelIter */
+#define XFDASHBOARD_TYPE_MODEL_ITER				(xfdashboard_model_iter_get_type())
+#define XFDASHBOARD_MODEL_ITER(obj)				(G_TYPE_CHECK_INSTANCE_CAST((obj), XFDASHBOARD_TYPE_MODEL_ITER, XfdashboardModelIter))
+#define XFDASHBOARD_IS_MODEL_ITER(obj)			(G_TYPE_CHECK_INSTANCE_TYPE((obj), XFDASHBOARD_TYPE_MODEL_ITER))
+#define XFDASHBOARD_MODEL_ITER_CLASS(klass)		(G_TYPE_CHECK_CLASS_CAST((klass), XFDASHBOARD_TYPE_MODEL_ITER, XfdashboardModelIterClass))
+#define XFDASHBOARD_IS_MODEL_ITER_CLASS(klass)	(G_TYPE_CHECK_CLASS_TYPE((klass), XFDASHBOARD_TYPE_MODEL_ITER))
+#define XFDASHBOARD_MODEL_ITER_GET_CLASS(obj)	(G_TYPE_INSTANCE_GET_CLASS((obj), XFDASHBOARD_TYPE_MODEL_ITER, XfdashboardModelIterClass))
+
+typedef struct _XfdashboardModelIter			XfdashboardModelIter;
+typedef struct _XfdashboardModelIterClass		XfdashboardModelIterClass;
+typedef struct _XfdashboardModelIterPrivate		XfdashboardModelIterPrivate;
+
+struct _XfdashboardModelIter
+{
+	/* Parent instance */
+	GObjectClass					parent_instance;
+
+	/* Private structure */
+	XfdashboardModelIterPrivate		*priv;
+};
+
+struct _XfdashboardModelIterClass
+{
+	/*< private >*/
+	/* Parent class */
+	GObjectClass					parent_class;
+
+	/*< public >*/
+	/* Virtual functions */
+};
+
+
+/* Object declaration: XfdashboardModel */
+#define XFDASHBOARD_TYPE_MODEL				(xfdashboard_model_get_type())
+#define XFDASHBOARD_MODEL(obj)				(G_TYPE_CHECK_INSTANCE_CAST((obj), XFDASHBOARD_TYPE_MODEL, XfdashboardModel))
+#define XFDASHBOARD_IS_MODEL(obj)			(G_TYPE_CHECK_INSTANCE_TYPE((obj), XFDASHBOARD_TYPE_MODEL))
+#define XFDASHBOARD_MODEL_CLASS(klass)		(G_TYPE_CHECK_CLASS_CAST((klass), XFDASHBOARD_TYPE_MODEL, XfdashboardModelClass))
+#define XFDASHBOARD_IS_MODEL_CLASS(klass)	(G_TYPE_CHECK_CLASS_TYPE((klass), XFDASHBOARD_TYPE_MODEL))
+#define XFDASHBOARD_MODEL_GET_CLASS(obj)	(G_TYPE_INSTANCE_GET_CLASS((obj), XFDASHBOARD_TYPE_MODEL, XfdashboardModelClass))
+
+typedef struct _XfdashboardModel			XfdashboardModel;
+typedef struct _XfdashboardModelClass		XfdashboardModelClass;
+typedef struct _XfdashboardModelPrivate		XfdashboardModelPrivate;
+
+struct _XfdashboardModel
+{
+	/* Parent instance */
+	GObjectClass					parent_instance;
+
+	/* Private structure */
+	XfdashboardModelPrivate			*priv;
+};
+
+struct _XfdashboardModelClass
+{
+	/*< private >*/
+	/* Parent class */
+	GObjectClass					parent_class;
+
+	/*< public >*/
+	/* Virtual functions */
+	void (*row_added)(XfdashboardModel *self,
+						XfdashboardModelIter *inIter);
+	void (*row_removed)(XfdashboardModel *self,
+						XfdashboardModelIter *inIter);
+	void (*row_changed)(XfdashboardModel *self,
+						XfdashboardModelIter *inIter);
+
+	void (*sort_changed)(XfdashboardModel *self);
+
+	void (*filter_changed)(XfdashboardModel *self);
+};
+
+
+/* Public API */
+typedef void (*XfdashboardModelForeachFunc)(XfdashboardModelIter *inIter,
+											gpointer inData,
+											gpointer inUserData);
+typedef gint (*XfdashboardModelSortFunc)(XfdashboardModelIter *inLeftIter,
+											XfdashboardModelIter *inRightIter,
+											gpointer inUserData);
+typedef gboolean (*XfdashboardModelFilterFunc)(XfdashboardModelIter *inIter,
+												gpointer inUserData);
+
+GType xfdashboard_model_get_type(void) G_GNUC_CONST;
+GType xfdashboard_model_iter_get_type(void) G_GNUC_CONST;
+
+/* Model creation functions */
+XfdashboardModel* xfdashboard_model_new(void);
+XfdashboardModel* xfdashboard_model_new_with_free_data(GDestroyNotify inFreeDataFunc);
+
+/* Model information functions */
+gint xfdashboard_model_get_rows_count(XfdashboardModel *self);
+
+/* Model access functions */
+gpointer xfdashboard_model_get(XfdashboardModel *self, gint inRow);
+
+/* Model manipulation functions */
+gboolean xfdashboard_model_append(XfdashboardModel *self,
+									gpointer inData,
+									XfdashboardModelIter **outIter);
+gboolean xfdashboard_model_prepend(XfdashboardModel *self,
+									gpointer inData,
+									XfdashboardModelIter **outIter);
+gboolean xfdashboard_model_insert(XfdashboardModel *self,
+									gint inRow,
+									gpointer inData,
+									XfdashboardModelIter **outIter);
+gboolean xfdashboard_model_set(XfdashboardModel *self,
+								gint inRow,
+								gpointer inData,
+								XfdashboardModelIter **outIter);
+gboolean xfdashboard_model_remove(XfdashboardModel *self, gint inRow);
+void xfdashboard_model_remove_all(XfdashboardModel *self);
+
+/* Model foreach functions */
+void xfdashboard_model_foreach(XfdashboardModel *self,
+								XfdashboardModelForeachFunc inForeachCallback,
+								gpointer inUserData);
+
+/* Model sort functions */
+gboolean xfdashboard_model_is_sorted(XfdashboardModel *self);
+void xfdashboard_model_set_sort(XfdashboardModel *self,
+								XfdashboardModelSortFunc inSortCallback,
+								gpointer inUserData,
+								GDestroyNotify inUserDataDestroyCallback);
+void xfdashboard_model_resort(XfdashboardModel *self);
+
+/* Model filter functions */
+gboolean xfdashboard_model_is_filtered(XfdashboardModel *self);
+void xfdashboard_model_set_filter(XfdashboardModel *self,
+									XfdashboardModelFilterFunc inFilterCallback,
+									gpointer inUserData,
+									GDestroyNotify inUserDataDestroyCallback);
+gboolean xfdashboard_model_filter_row(XfdashboardModel *self, gint inRow);
+
+
+/* Model iterator functions */
+XfdashboardModelIter* xfdashboard_model_iter_new(XfdashboardModel *inModel);
+XfdashboardModelIter* xfdashboard_model_iter_new_for_row(XfdashboardModel *inModel, gint inRow);
+XfdashboardModelIter* xfdashboard_model_iter_copy(XfdashboardModelIter *self);
+gboolean xfdashboard_model_iter_next(XfdashboardModelIter *self);
+gboolean xfdashboard_model_iter_prev(XfdashboardModelIter *self);
+gboolean xfdashboard_model_iter_move_to_row(XfdashboardModelIter *self, gint inRow);
+
+/* Model iterator information functions */
+XfdashboardModel* xfdashboard_model_iter_get_model(XfdashboardModelIter *self);
+guint xfdashboard_model_iter_get_row(XfdashboardModelIter *self);
+
+/* Model iterator access functions */
+gpointer xfdashboard_model_iter_get(XfdashboardModelIter *self);
+
+/* Model iterator manipulation functions */
+gboolean xfdashboard_model_iter_set(XfdashboardModelIter *self, gpointer inData);
+gboolean xfdashboard_model_iter_remove(XfdashboardModelIter *self);
+
+/* Model filter functions */
+gboolean xfdashboard_model_iter_filter(XfdashboardModelIter *self);
+
+G_END_DECLS
+
+#endif	/* __LIBXFDASHBOARD_MODEL__ */

-- 
To stop receiving notification emails like this one, please contact
the administrator of this repository.


More information about the Xfce4-commits mailing list