From 3da40296bebe0b6c795cea49907cb71a822572b6 Mon Sep 17 00:00:00 2001
From: c4pp4
Date: Wed, 13 Jul 2022 05:46:54 +0200
Subject: [PATCH 1/1] Add Unity Launcher support

List bookmarks in the context menu and display a bar on the icon during
copies.

Based on Nautilus 12_unity_launcher_support.patch (Xenial).

Signed-off-by: c4pp4
---
 config.h.meson.in                    |   2 +
 libnemo-private/nemo-progress-info.c |  46 ++++++
 libnemo-private/nemo-progress-info.h |   4 +
 meson.build                          |   9 ++
 meson_options.txt                    |   2 +
 po/POTFILES.in                       |   1 +
 src/meson.build                      |   8 +
 src/nemo-main-application.c          |   8 +
 src/nemo-progress-ui-handler.c       | 230 +++++++++++++++++++++++++++
 src/unity-bookmarks-handler.c        | 141 ++++++++++++++++
 src/unity-bookmarks-handler.h        |  31 ++++
 src/unity-quicklist-handler.c        | 152 ++++++++++++++++++
 src/unity-quicklist-handler.h        |  75 +++++++++
 13 files changed, 709 insertions(+)
 create mode 100644 src/unity-bookmarks-handler.c
 create mode 100644 src/unity-bookmarks-handler.h
 create mode 100644 src/unity-quicklist-handler.c
 create mode 100644 src/unity-quicklist-handler.h

diff --git a/config.h.meson.in b/config.h.meson.in
index f724539..29a9790 100644
--- a/config.h.meson.in
+++ b/config.h.meson.in
@@ -13,6 +13,8 @@
 #mesondefine ENABLE_EMPTY_VIEW
 // Define to enable xmp support
 #mesondefine HAVE_EXEMPI
+// Define to add Unity Launcher support
+#mesondefine HAVE_UNITY
 // Define to enable EXIF support
 #mesondefine HAVE_EXIF
 // Define if libselinux is available
diff --git a/libnemo-private/nemo-progress-info.c b/libnemo-private/nemo-progress-info.c
index f0e97e7..d105080 100644
--- a/libnemo-private/nemo-progress-info.c
+++ b/libnemo-private/nemo-progress-info.c
@@ -53,6 +53,10 @@ struct _NemoProgressInfo
 	char *details;
     char *initial_details;
 	double progress;
+#ifdef HAVE_UNITY
+	double current;
+	double total;
+#endif
 	gboolean activity_mode;
 	gboolean started;
 	gboolean finished;
@@ -264,6 +268,44 @@ nemo_progress_info_get_progress (NemoProgressInfo *info)
 	return res;
 }
 
+#ifdef HAVE_UNITY
+double
+nemo_progress_info_get_current (NemoProgressInfo *info)
+{
+	double current;
+
+	G_LOCK (progress_info);
+
+	if (info->activity_mode) {
+		current = 0.0;
+	} else {
+		current = info->current;
+	}
+
+	G_UNLOCK (progress_info);
+
+	return current;
+}
+
+double
+nemo_progress_info_get_total (NemoProgressInfo *info)
+{
+	double total;
+
+	G_LOCK (progress_info);
+
+	if (info->activity_mode) {
+		total = -1.0;
+	} else {
+		total = info->total;
+	}
+
+	G_UNLOCK (progress_info);
+
+	return total;
+}
+#endif
+
 void
 nemo_progress_info_cancel (NemoProgressInfo *info)
 {
@@ -640,6 +682,10 @@ nemo_progress_info_set_progress (NemoProgressInfo *info,
 	    ) {
 		info->activity_mode = FALSE;
 		info->progress = current_percent;
+#ifdef HAVE_UNITY
+		info->current = current;
+		info->total = total;
+#endif
 		info->progress_at_idle = TRUE;
 		queue_idle (info, FALSE);
 	}
diff --git a/libnemo-private/nemo-progress-info.h b/libnemo-private/nemo-progress-info.h
index 235e58c..45c922e 100644
--- a/libnemo-private/nemo-progress-info.h
+++ b/libnemo-private/nemo-progress-info.h
@@ -63,6 +63,10 @@ void          nemo_progress_info_cancel          (NemoProgressInfo *info);
 gboolean      nemo_progress_info_get_is_started  (NemoProgressInfo *info);
 gboolean      nemo_progress_info_get_is_finished (NemoProgressInfo *info);
 gboolean      nemo_progress_info_get_is_paused   (NemoProgressInfo *info);
+#ifdef HAVE_UNITY
+double        nemo_progress_info_get_current     (NemoProgressInfo *info);
+double        nemo_progress_info_get_total       (NemoProgressInfo *info);
+#endif
 
 void          nemo_progress_info_queue           (NemoProgressInfo *info);
 void          nemo_progress_info_start           (NemoProgressInfo *info);
diff --git a/meson.build b/meson.build
index 3fa0cfd..317016c 100644
--- a/meson.build
+++ b/meson.build
@@ -141,6 +141,14 @@ else
   message('................using pango @0@ instead'.format(new_pango.version()))
 endif
 
+unity_enabled = get_option('unity-launcher')
+if unity_enabled
+  unity = dependency('unity',                       version: '>=4.0.0')
+  dbusmenu = dependency('dbusmenu-glib-0.4',        version: '>=0.4.90')
+endif
+conf.set('HAVE_UNITY', unity_enabled)
+
+
 enableEmptyView = get_option('empty_view')
 conf.set10('ENABLE_EMPTY_VIEW', enableEmptyView)
 
@@ -212,6 +220,7 @@ message('\n'.join(['',
 '    prefix:                 @0@'.format(prefix),
 '    source code location:   @0@'.format(meson.source_root()),
 '    compiler:               @0@'.format(cc.get_id()),
+'    Unity support:          @0@'.format(unity_enabled),
 '    libexif support:        @0@'.format(libexif_enabled),
 '    exempi  support:        @0@'.format(exempi_enabled),
 '    Tracker support:        @0@'.format(tracker_enabled),
diff --git a/meson_options.txt b/meson_options.txt
index 6281d58..19be216 100644
--- a/meson_options.txt
+++ b/meson_options.txt
@@ -4,6 +4,8 @@ option('exif', type : 'boolean', value : true,
        description: 'EXIF parsing support (requires libexif)')
 option('xmp', type : 'boolean', value : true,
        description: 'XMP support (requires Exempi)')
+option ('unity-launcher', type : 'boolean', value : true,
+       description: 'Add Unity Launcher support')
 option('gtk_doc', type : 'boolean', value : false,
        description: 'Generate API reference (requires GTK-Doc)')
 option('selinux', type : 'boolean', value : false,
diff --git a/po/POTFILES.in b/po/POTFILES.in
index 2f1eec6..0e83dab 100644
--- a/po/POTFILES.in
+++ b/po/POTFILES.in
@@ -107,3 +107,4 @@ src/nemo-window-pane.c
 src/nemo-window-private.h
 src/nemo-window-slot.c
 src/nemo-x-content-bar.c
+src/unity-quicklist-handler.h
diff --git a/src/meson.build b/src/meson.build
index 3c01acb..bc5423a 100644
--- a/src/meson.build
+++ b/src/meson.build
@@ -113,6 +113,14 @@ if libexif_enabled
   nemo_deps += libexif
 endif
 
+if unity_enabled
+    nemo_deps += [ unity, dbusmenu ]
+    nemoCommon_sources += [
+        'unity-quicklist-handler.c',
+        'unity-bookmarks-handler.c',
+    ]
+endif
+
 nemo = executable('nemo',
   nemoCommon_sources + nemoWindow_sources,
   include_directories: [ rootInclude ],
diff --git a/src/nemo-main-application.c b/src/nemo-main-application.c
index b9834ec..83862d5 100644
--- a/src/nemo-main-application.c
+++ b/src/nemo-main-application.c
@@ -79,6 +79,10 @@
 
 #include <libcinnamon-desktop/gnome-desktop-thumbnail.h>
 
+#ifdef HAVE_UNITY
+#include "src/unity-bookmarks-handler.h"
+#endif
+
 /* Keep window from shrinking down ridiculously small; numbers are somewhat arbitrary */
 #define APPLICATION_WINDOW_MIN_WIDTH	300
 #define APPLICATION_WINDOW_MIN_HEIGHT	100
@@ -914,6 +918,10 @@ nemo_main_application_continue_startup (NemoApplication *app)
 
     g_signal_connect_swapped (nemo_window_state, "changed::" NEMO_WINDOW_STATE_START_WITH_MENU_BAR,
                               G_CALLBACK (menu_state_changed_callback), self);
+
+#ifdef HAVE_UNITY
+    unity_bookmarks_handler_initialize ();
+#endif
 }
 
 static void
diff --git a/src/nemo-progress-ui-handler.c b/src/nemo-progress-ui-handler.c
index af76b19..1abcf1b 100644
--- a/src/nemo-progress-ui-handler.c
+++ b/src/nemo-progress-ui-handler.c
@@ -42,6 +42,11 @@
 #include <libxapp/xapp-gtk-window.h>
 #include <libxapp/xapp-status-icon.h>
 
+#ifdef HAVE_UNITY
+#include <unity.h>
+#include "unity-quicklist-handler.h"
+#endif
+
 struct _NemoProgressUIHandlerPriv {
 	NemoProgressInfoManager *manager;
 
@@ -56,6 +61,9 @@ struct _NemoProgressUIHandlerPriv {
 
 	XAppStatusIcon *status_icon;
     gboolean should_show_status_icon;
+#ifdef HAVE_UNITY
+	UnityQuicklistHandler *unity_quicklist_handler;
+#endif
 };
 
 G_DEFINE_TYPE (NemoProgressUIHandler, nemo_progress_ui_handler, G_TYPE_OBJECT);
@@ -128,6 +136,220 @@ progress_ui_handler_update_status_icon (NemoProgressUIHandler *self)
 	xapp_status_icon_set_visible (self->priv->status_icon, self->priv->should_show_status_icon);
 }
 
+#ifdef HAVE_UNITY
+static void
+progress_ui_handler_unity_progress_changed (NemoProgressInfo *info,
+                                            NemoProgressUIHandler *self)
+{
+	g_return_if_fail (self);
+	g_return_if_fail (self->priv->unity_quicklist_handler);
+	g_return_if_fail (self->priv->manager);
+
+	GList *infos, *l;
+	double progress = 0;
+	double c, current = 0;
+	double t, total = 0;
+
+	infos = nemo_progress_info_manager_get_all_infos (self->priv->manager);
+
+	for (l = infos; l; l = l->next) {
+		NemoProgressInfo *i = l->data;
+		c = nemo_progress_info_get_current (i);
+		t = nemo_progress_info_get_total (i);
+
+		if (c < 0) c = 0;
+		if (t <= 0) continue;
+
+		total += t;
+		current += c;
+	}
+
+	if (current >= 0 && total > 0)
+		progress = current / total;
+
+	if (progress > 1.0)
+		progress = 1.0;
+
+	for (l = unity_quicklist_get_launcher_entries (self->priv->unity_quicklist_handler); l; l = l->next) {
+		UnityLauncherEntry *entry = l->data;
+		unity_launcher_entry_set_progress (entry, progress);
+	}
+}
+
+static gboolean
+progress_ui_handler_disable_unity_urgency (UnityLauncherEntry *entry)
+{
+	g_return_val_if_fail (entry, FALSE);
+
+	unity_launcher_entry_set_urgent (entry, FALSE);
+	return FALSE;
+}
+
+static void
+progress_ui_handler_unity_quicklist_show_activated (DbusmenuMenuitem *menu,
+                                                    guint timestamp,
+                                                    NemoProgressUIHandler *self)
+{
+	g_return_if_fail (self);
+
+	gtk_window_present_with_time (GTK_WINDOW (self->priv->progress_window), timestamp);
+}
+
+static void
+progress_ui_handler_unity_quicklist_cancel_activated (DbusmenuMenuitem *menu,
+                                                      guint timestamp,
+                                                      NemoProgressUIHandler *self)
+{
+	g_return_if_fail (self);
+	g_return_if_fail (self->priv->manager);
+
+	GList *infos, *l;
+	infos = nemo_progress_info_manager_get_all_infos (self->priv->manager);
+
+	for (l = infos; l; l = l->next) {
+		NemoProgressInfo *info = l->data;
+		nemo_progress_info_cancel (info);
+	}
+}
+
+static DbusmenuMenuitem *
+progress_ui_handler_build_unity_quicklist (NemoProgressUIHandler *self)
+{
+	g_return_val_if_fail (self, NULL);
+	GList *l;
+
+	for (l = unity_quicklist_get_launcher_entries (self->priv->unity_quicklist_handler); l; l = l->next) {
+		UnityLauncherEntry *entry = l->data;
+		DbusmenuMenuitem *ql = unity_launcher_entry_get_quicklist (entry);
+		DbusmenuMenuitem *quickmenu;
+
+		if (ql) {
+			quickmenu = dbusmenu_menuitem_new ();
+			dbusmenu_menuitem_property_set (quickmenu,
+			                                DBUSMENU_MENUITEM_PROP_TYPE,
+			                                DBUSMENU_CLIENT_TYPES_SEPARATOR);
+			dbusmenu_menuitem_property_set_bool (quickmenu,
+			                                     DBUSMENU_MENUITEM_PROP_VISIBLE, FALSE);
+			dbusmenu_menuitem_property_set_bool (quickmenu,
+			                                     UNITY_QUICKLIST_PROGRESS_ITEM, TRUE);
+			unity_quicklist_handler_append_menuitem (entry, quickmenu);
+		}
+
+		quickmenu = dbusmenu_menuitem_new ();
+		dbusmenu_menuitem_property_set (quickmenu,
+		                                DBUSMENU_MENUITEM_PROP_LABEL,
+		                                UNITY_QUICKLIST_SHOW_COPY_DIALOG);
+		dbusmenu_menuitem_property_set_bool (quickmenu,
+		                                     DBUSMENU_MENUITEM_PROP_VISIBLE, FALSE);
+		dbusmenu_menuitem_property_set_bool (quickmenu,
+		                                     UNITY_QUICKLIST_PROGRESS_ITEM, TRUE);
+		unity_quicklist_handler_append_menuitem (entry, quickmenu);
+		g_signal_connect (quickmenu, DBUSMENU_MENUITEM_SIGNAL_ITEM_ACTIVATED,
+		                  (GCallback) progress_ui_handler_unity_quicklist_show_activated,
+		                  self);
+
+		quickmenu = dbusmenu_menuitem_new ();
+		dbusmenu_menuitem_property_set (quickmenu,
+		                                DBUSMENU_MENUITEM_PROP_LABEL,
+		                                UNITY_QUICKLIST_CANCEL_COPY);
+		dbusmenu_menuitem_property_set_bool (quickmenu,
+		                                     DBUSMENU_MENUITEM_PROP_VISIBLE, FALSE);
+		dbusmenu_menuitem_property_set_bool (quickmenu,
+		                                     UNITY_QUICKLIST_PROGRESS_ITEM, TRUE);
+		unity_quicklist_handler_append_menuitem (entry, quickmenu);
+		g_signal_connect (quickmenu, DBUSMENU_MENUITEM_SIGNAL_ITEM_ACTIVATED,
+		                  (GCallback) progress_ui_handler_unity_quicklist_cancel_activated,
+		                  self);
+	}
+}
+
+static void
+progress_ui_handler_show_unity_quicklist (NemoProgressUIHandler *self,
+                                          UnityLauncherEntry *entry,
+                                          gboolean show)
+{
+	g_return_if_fail (self);
+	g_return_if_fail (entry);
+
+	DbusmenuMenuitem *ql;
+	GList *children, *l;
+
+	ql = unity_launcher_entry_get_quicklist (entry);
+	children = dbusmenu_menuitem_get_children (ql);
+
+	for (l = children; l; l = l->next) {
+		DbusmenuMenuitem *child = l->data;
+		if (unity_quicklist_handler_menuitem_is_progress_item (child))
+			dbusmenu_menuitem_property_set_bool(child,
+	                                    DBUSMENU_MENUITEM_PROP_VISIBLE, show);
+	}
+}
+
+static void
+progress_ui_handler_update_unity_launcher_entry (NemoProgressUIHandler *self,
+                                                 NemoProgressInfo *info,
+                                                 UnityLauncherEntry *entry)
+{
+	g_return_if_fail (self);
+	g_return_if_fail (entry);
+
+	if (self->priv->active_infos > 0) {
+		unity_launcher_entry_set_progress_visible (entry, TRUE);
+		progress_ui_handler_show_unity_quicklist (self, entry, TRUE);
+		progress_ui_handler_unity_progress_changed (NULL, self);
+
+		if (self->priv->active_infos > 1) {
+			unity_launcher_entry_set_count (entry, self->priv->active_infos);
+			unity_launcher_entry_set_count_visible (entry, TRUE);
+		} else {
+			unity_launcher_entry_set_count_visible (entry, FALSE);
+		}
+	} else {
+		unity_launcher_entry_set_progress_visible (entry, FALSE);
+		unity_launcher_entry_set_progress (entry, 0.0);
+		unity_launcher_entry_set_count_visible (entry, FALSE);
+		progress_ui_handler_show_unity_quicklist (self, entry, FALSE);
+		GCancellable *pc = nemo_progress_info_get_cancellable (info);
+
+		if (!g_cancellable_is_cancelled (pc)) {
+			unity_launcher_entry_set_urgent (entry, TRUE);
+
+			g_timeout_add_seconds (2, (GSourceFunc)
+				               progress_ui_handler_disable_unity_urgency,
+					       entry);
+		}
+	}
+}
+
+static void
+progress_ui_handler_update_unity_launcher (NemoProgressUIHandler *self,
+                                           NemoProgressInfo *info,
+                                           gboolean added)
+{
+	g_return_if_fail (self);
+	GList *l;
+
+	if (!self->priv->unity_quicklist_handler) {
+		self->priv->unity_quicklist_handler = unity_quicklist_handler_get_singleton ();
+		if (!self->priv->unity_quicklist_handler)
+			return;
+
+		progress_ui_handler_build_unity_quicklist (self);
+	}
+
+	for (l = unity_quicklist_get_launcher_entries (self->priv->unity_quicklist_handler); l; l = l->next) {
+		UnityLauncherEntry *entry = l->data;
+		progress_ui_handler_update_unity_launcher_entry (self, info, entry);
+	}
+
+	if (added) {
+		g_signal_connect (info, "progress-changed",
+				  (GCallback) progress_ui_handler_unity_progress_changed,
+				  self);
+	}
+}
+#endif
+
 static gboolean
 progress_window_delete_event (GtkWidget *widget,
 			      GdkEvent *event,
@@ -301,6 +523,10 @@ progress_info_finished_cb (NemoProgressInfo *info,
 			progress_ui_handler_show_complete_notification (self);
 		}
 	}
+
+#ifdef HAVE_UNITY
+	progress_ui_handler_update_unity_launcher (self, info, FALSE);
+#endif
 }
 
 static void
@@ -372,6 +598,10 @@ handle_new_progress_info (NemoProgressUIHandler *self,
             gtk_window_present (GTK_WINDOW (self->priv->progress_window));
         }
 	}
+
+#ifdef HAVE_UNITY
+	progress_ui_handler_update_unity_launcher (self, info, TRUE);
+#endif
 }
 
 typedef struct {
diff --git a/src/unity-bookmarks-handler.c b/src/unity-bookmarks-handler.c
new file mode 100644
index 0000000..85c2c39
--- /dev/null
+++ b/src/unity-bookmarks-handler.c
@@ -0,0 +1,141 @@
+/*unity-bookmarks-handler.c: handle Unity bookmark for quicklist
+ *
+ * Copyright (C) 2012 Canonical
+ *
+ * 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., 59 Temple Place - Suite 330,
+ * Boston, MA 02111-1307, USA.
+ *
+ * Authors: Didier Roche <didrocks@ubuntu.com>
+ *
+ */
+
+#include <config.h>
+
+#include "unity-bookmarks-handler.h"
+
+#include <libdbusmenu-glib/dbusmenu-glib.h>
+#include "unity-quicklist-handler.h"
+
+#include "nemo-application.h"
+#include "nemo-bookmark-list.h"
+
+#include <eel/eel-string.h>
+#include <gdk/gdkx.h>
+
+static UnityQuicklistHandler* unity_quicklist_handler = NULL;
+static NemoBookmarkList* bookmarks = NULL;
+
+static void
+activate_bookmark_by_quicklist (DbusmenuMenuitem *menu,
+								guint timestamp,
+								NemoBookmark *bookmark)
+{
+	g_return_if_fail (NEMO_IS_BOOKMARK (bookmark));
+
+	GFile *locations[2];
+	GList *l;
+	NemoApplication *application;
+
+	locations[0] = nemo_bookmark_get_location (bookmark);
+	locations[1] = NULL;
+
+	application = NEMO_APPLICATION (g_application_get_default ());
+
+	/* Make sure that the application timestamp matches the event */
+	for (l = gtk_application_get_windows (GTK_APPLICATION (application)); l; l = l->next) {
+		GdkWindow *gdk_window = gtk_widget_get_window (GTK_WIDGET (l->data));
+		gdk_x11_window_set_user_time (gdk_window, timestamp);
+	}
+
+	g_application_open (G_APPLICATION (application), locations, 1, "");
+
+	g_object_unref (locations[0]);
+}
+
+static void
+unity_bookmarks_handler_remove_bookmark_quicklists (void) {
+
+	GList *children, *l;
+
+	/* remove unity quicklist bookmarks to launcher entries */
+	for (l = unity_quicklist_get_launcher_entries (unity_quicklist_handler); l; l = l->next) {
+		UnityLauncherEntry *entry = l->data;
+		DbusmenuMenuitem *ql = unity_launcher_entry_get_quicklist (entry);
+		if (!ql)
+			break;
+
+		children = dbusmenu_menuitem_get_children (ql);
+		while (children) {
+			DbusmenuMenuitem *child = children->data;
+			children = children->next;
+			if (unity_quicklist_handler_menuitem_is_bookmark_item (child)) {
+				g_signal_handlers_disconnect_matched (child, G_SIGNAL_MATCH_FUNC, 0, 0, 0, (GCallback) activate_bookmark_by_quicklist, 0);
+				dbusmenu_menuitem_child_delete (ql, child);
+				g_object_unref(child);
+			}
+		}
+	}
+}
+
+static void
+unity_bookmarks_handler_update_bookmarks () {
+
+	NemoBookmark *bookmark;
+	guint bookmark_count;
+	guint index;
+	GList *l;
+
+	/* append new set of bookmarks */
+	bookmark_count = nemo_bookmark_list_length (bookmarks);
+	for (index = 0; index < bookmark_count; ++index) {
+
+		bookmark = nemo_bookmark_list_item_at (bookmarks, index);
+
+		for (l = unity_quicklist_get_launcher_entries (unity_quicklist_handler); l; l = l->next) {
+			UnityLauncherEntry *entry = l->data;
+
+			DbusmenuMenuitem* menuitem = dbusmenu_menuitem_new();
+			gchar *bookmark_name_dbusmenu = eel_str_replace_substring (nemo_bookmark_get_name (bookmark), "_", "__");
+			dbusmenu_menuitem_property_set (menuitem, "label", bookmark_name_dbusmenu);
+			g_free (bookmark_name_dbusmenu);
+			g_signal_connect (menuitem, DBUSMENU_MENUITEM_SIGNAL_ITEM_ACTIVATED,
+										 (GCallback) activate_bookmark_by_quicklist,
+										 bookmark);
+
+			unity_quicklist_handler_append_menuitem (entry, menuitem);
+		}
+	}
+}
+
+static void
+unity_bookmarks_handler_refresh_bookmarks (void)
+{
+	unity_bookmarks_handler_remove_bookmark_quicklists ();
+	unity_bookmarks_handler_update_bookmarks ();
+}
+
+void
+unity_bookmarks_handler_initialize ()
+{
+	unity_quicklist_handler = unity_quicklist_handler_get_singleton ();
+	// get the singleton
+	bookmarks = nemo_bookmark_list_get_default ();
+	unity_bookmarks_handler_refresh_bookmarks ();
+
+    /* Recreate dynamic part of menu if bookmark list changes */
+	g_signal_connect (bookmarks, "changed",
+						G_CALLBACK (unity_bookmarks_handler_refresh_bookmarks), 0);
+}
+
diff --git a/src/unity-bookmarks-handler.h b/src/unity-bookmarks-handler.h
new file mode 100644
index 0000000..a887648
--- /dev/null
+++ b/src/unity-bookmarks-handler.h
@@ -0,0 +1,31 @@
+/* -*- Mode: C; indent-tabs-mode: t; c-basic-offset: 8; tab-width: 8 -*- */
+/*
+ * unity-bookmarks-handler.h: handle Unity bookmark for quicklist
+ *
+ * Copyright (C) 2012 Canonical
+ *
+ * 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., 59 Temple Place - Suite 330,
+ * Boston, MA 02111-1307, USA.
+ *
+ * Authors: Didier Roche <didrocks@ubuntu.com>
+ *
+ */
+
+#ifndef __UNITY_BOOKMARKS_HANDLER_H__
+#define __UNITY_BOOKMARKS_HANDLER_H__
+
+void unity_bookmarks_handler_initialize (void);
+
+#endif /* __UNITY_BOOKMARKS_HANDLER_H__*/
diff --git a/src/unity-quicklist-handler.c b/src/unity-quicklist-handler.c
new file mode 100644
index 0000000..363459a
--- /dev/null
+++ b/src/unity-quicklist-handler.c
@@ -0,0 +1,152 @@
+/*unity-quicklist-handler.c: handle Unity quicklists
+ *
+ * Copyright (C) 2012 Canonical
+ *
+ * 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., 59 Temple Place - Suite 330,
+ * Boston, MA 02111-1307, USA.
+ *
+ * Authors: Didier Roche <didrocks@ubuntu.com>
+ *
+ */
+
+#include <config.h>
+
+#include "unity-quicklist-handler.h"
+
+struct _UnityQuicklistHandlerPriv {
+    GList *launcher_entries;
+};
+
+G_DEFINE_TYPE (UnityQuicklistHandler, unity_quicklist_handler, G_TYPE_OBJECT);
+
+static UnityQuicklistHandler *unity_quicklist_handler_singleton = NULL;
+
+GList *
+unity_quicklist_get_launcher_entries (UnityQuicklistHandler *self)
+{
+	return self->priv->launcher_entries;
+}
+
+gboolean
+unity_quicklist_handler_menuitem_is_progress_item (DbusmenuMenuitem *ql)
+{
+	g_return_val_if_fail(ql, FALSE);
+	return dbusmenu_menuitem_property_get_bool (ql, UNITY_QUICKLIST_PROGRESS_ITEM);
+}
+
+gboolean
+unity_quicklist_handler_menuitem_is_bookmark_item (DbusmenuMenuitem *ql)
+{
+	g_return_val_if_fail(ql, FALSE);
+	return (!unity_quicklist_handler_menuitem_is_progress_item(ql));
+}
+
+void
+unity_quicklist_handler_append_menuitem (UnityLauncherEntry *entry, DbusmenuMenuitem *elem)
+{
+	g_return_if_fail (entry);
+
+	GList *children, *l;
+	int position = 0;
+	DbusmenuMenuitem *ql = unity_launcher_entry_get_quicklist (entry);
+
+	gboolean is_bookmark = unity_quicklist_handler_menuitem_is_bookmark_item (elem);
+	gboolean is_progress = unity_quicklist_handler_menuitem_is_progress_item (elem);
+
+	if (!ql) {
+		ql = dbusmenu_menuitem_new ();
+		unity_launcher_entry_set_quicklist (entry, ql);
+	}
+
+	children = dbusmenu_menuitem_get_children (ql);
+	for (l = children; l; l = l->next) {
+		DbusmenuMenuitem *child = l->data;
+		/* set quicklist groups together, and bookmarks group after progress group.
+		   bookmarks elements are ordered alphabetically */
+		if ((is_bookmark && unity_quicklist_handler_menuitem_is_bookmark_item (child) &&
+                (g_strcmp0 (dbusmenu_menuitem_property_get (child, DBUSMENU_MENUITEM_PROP_LABEL), dbusmenu_menuitem_property_get (elem, DBUSMENU_MENUITEM_PROP_LABEL)) < 0)) ||
+			(is_progress && unity_quicklist_handler_menuitem_is_progress_item (child)) ||
+			(is_progress && unity_quicklist_handler_menuitem_is_bookmark_item (child)))
+			position++;
+		else
+			break;
+	}
+
+	dbusmenu_menuitem_child_add_position (ql, elem, position);
+}
+
+static void
+unity_quicklist_handler_dispose (GObject *obj)
+{
+	UnityQuicklistHandler *self = UNITY_QUICKLIST_HANDLER (obj);
+
+	if (self->priv->launcher_entries) {
+		g_list_free_full (self->priv->launcher_entries, g_object_unref);
+		self->priv->launcher_entries = NULL;
+	}
+
+	G_OBJECT_CLASS (unity_quicklist_handler_parent_class)->dispose (obj);
+}
+
+static void
+unity_quicklist_handler_launcher_entry_add (UnityQuicklistHandler *self,
+                                            const gchar *entry_id)
+{
+	GList **entries;
+	UnityLauncherEntry *entry;
+
+	entries = &(self->priv->launcher_entries);
+	entry = unity_launcher_entry_get_for_desktop_id (entry_id);
+
+	if (entry) {
+		*entries = g_list_prepend (*entries, entry);
+	}
+}
+
+static void
+unity_quicklist_handler_init (UnityQuicklistHandler *self)
+{
+	self->priv = G_TYPE_INSTANCE_GET_PRIVATE (self, UNITY_TYPE_QUICKLIST_HANDLER,
+	                                          UnityQuicklistHandlerPriv);
+
+	unity_quicklist_handler_launcher_entry_add (self, "nemo.desktop");
+	g_return_if_fail (g_list_length (self->priv->launcher_entries) != 0);
+}
+
+static void
+unity_quicklist_handler_class_init (UnityQuicklistHandlerClass *klass)
+{
+	GObjectClass *oclass;
+
+	oclass = G_OBJECT_CLASS (klass);
+	oclass->dispose = unity_quicklist_handler_dispose;
+
+	g_type_class_add_private (klass, sizeof (UnityQuicklistHandlerPriv));
+}
+
+UnityQuicklistHandler *
+unity_quicklist_handler_get_singleton (void)
+{
+	if (!unity_quicklist_handler_singleton)
+		unity_quicklist_handler_singleton = unity_quicklist_handler_new ();
+	return unity_quicklist_handler_singleton;
+}
+
+UnityQuicklistHandler *
+unity_quicklist_handler_new (void)
+{
+	return g_object_new (UNITY_TYPE_QUICKLIST_HANDLER, NULL);
+}
+
diff --git a/src/unity-quicklist-handler.h b/src/unity-quicklist-handler.h
new file mode 100644
index 0000000..52caa1e
--- /dev/null
+++ b/src/unity-quicklist-handler.h
@@ -0,0 +1,75 @@
+/* -*- Mode: C; indent-tabs-mode: t; c-basic-offset: 8; tab-width: 8 -*- */
+/*
+ * unity-quicklist.h: handle unity quicklists.
+ *
+ * Copyright (C) 2012 Canonical
+ *
+ * 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., 59 Temple Place - Suite 330,
+ * Boston, MA 02111-1307, USA.
+ *
+ * Authors: Didier Roche <didrocks@ubuntu.com>
+ *
+ */
+
+#ifndef __UNITY_QUICKLIST_HANDLER_H__
+#define __UNITY_QUICKLIST_HANDLER_H__
+
+#include <glib-object.h>
+#include <glib/gi18n.h>
+
+#include <libdbusmenu-glib/dbusmenu-glib.h>
+#include <unity.h>
+
+#define UNITY_TYPE_QUICKLIST_HANDLER unity_quicklist_handler_get_type()
+#define UNITY_QUICKLIST_HANDLER(obj) \
+  (G_TYPE_CHECK_INSTANCE_CAST ((obj), UNITY_TYPE_QUICKLIST_HANDLER, UnityQuicklistHandler))
+#define UNITY_QUICKLIST_HANDLER_CLASS(klass) \
+  (G_TYPE_CHECK_CLASS_CAST ((klass), UNITY_TYPE_QUICKLIST_HANDLER, UnityQuicklistHandlerClass))
+#define UNITY_IS_QUICKLIST_HANDLER(obj) \
+  (G_TYPE_CHECK_INSTANCE_TYPE ((obj), UNITY_TYPE_QUICKLIST_HANDLER))
+#define UNITY_IS_QUICKLIST_HANDLER_CLASS(klass) \
+  (G_TYPE_CHECK_CLASS_TYPE ((klass), UNITY_TYPE_QUICKLIST_HANDLER))
+#define UNITY_QUICKLIST_HANDLER_GET_CLASS(obj) \
+  (G_TYPE_INSTANCE_GET_CLASS ((obj), UNITY_TYPE_QUICKLIST_HANDLER, UnityQuicklistHandlerClass))
+
+typedef struct _UnityQuicklistHandlerPriv UnityQuicklistHandlerPriv;
+
+#define UNITY_QUICKLIST_PROGRESS_ITEM "unity-quicklist-progress-item"
+
+typedef struct {
+  GObject parent;
+
+  /* private */
+  UnityQuicklistHandlerPriv *priv;
+} UnityQuicklistHandler;
+
+typedef struct {
+  GObjectClass parent_class;
+} UnityQuicklistHandlerClass;
+
+GType unity_quicklist_handler_get_type (void);
+
+UnityQuicklistHandler * unity_quicklist_handler_new (void);
+UnityQuicklistHandler * unity_quicklist_handler_get_singleton (void);
+
+GList * unity_quicklist_get_launcher_entries (UnityQuicklistHandler *unity_quicklist_handler);
+gboolean unity_quicklist_handler_menuitem_is_progress_item (DbusmenuMenuitem *ql);
+gboolean unity_quicklist_handler_menuitem_is_bookmark_item (DbusmenuMenuitem *ql);
+void unity_quicklist_handler_append_menuitem (UnityLauncherEntry *entry, DbusmenuMenuitem *elem);
+
+#define UNITY_QUICKLIST_SHOW_COPY_DIALOG _("Show Copy Dialog")
+#define UNITY_QUICKLIST_CANCEL_COPY _("Cancel All In-progress Actions")
+
+#endif /* __UNITY_QUICKLIST_HANDLER_H__ */
-- 
2.35.1

