=== modified file 'configure.ac'
--- configure.ac	2015-11-03 12:01:37 +0000
+++ configure.ac	2016-01-30 20:46:42 +0000
@@ -265,6 +265,34 @@
 fi
 AM_CONDITIONAL([HAVE_DBUSMENU], [test "x$enable_dbusmenu" = "xyes"])
 
+# Support suggested applications provided with zeitgeist
+ZEITGEIST_MIN_VERSION=0.3
+ZEITGEIST_PKGS="zeitgeist-1.0 >= $ZEITGEIST_MIN_VERSION"
+ZEITGEIST2_PKGS="zeitgeist-2.0 >= $ZEITGEIST_MIN_VERSION"
+AC_ARG_ENABLE([zeitgeist],
+              AS_HELP_STRING([--enable-zeitgeist],
+                             [Enable suggested applications provided with zeitgeist-1.0]),
+              [enable_zeitgeist=$enableval], [enable_zeitgeist=no])
+AC_ARG_ENABLE([zeitgeist2],
+              AS_HELP_STRING([--enable-zeitgeist2],
+                             [Enable suggested applications provided with zeitgeist-2.0]),
+              [enable_zeitgeist2=$enableval],
+              [PKG_CHECK_MODULES(ZEITGEIST, $ZEITGEIST2_PKGS, [enable_zeitgeist2=yes], [enable_zeitgeist2=no])])
+if test "x$enable_zeitgeist" = "xyes" -a "x$enable_zeitgeist2" = "xyes" ; then
+    AC_MSG_ERROR([Not possible to build against both, zeitgeist-1.0 and zeitgeist-2.0.])
+fi
+if test "x$enable_zeitgeist" = "xyes" -a "x$enable_zeitgeist2" = "xno" ; then
+    PLANK_CORE_OPTIONAL_PKGS="$PLANK_CORE_OPTIONAL_PKGS $ZEITGEIST_PKGS"
+    PLANK_CORE_VALA_PKGS="$PLANK_CORE_VALA_PKGS --pkg zeitgeist-1.0"
+    VALAFLAGS="$VALAFLAGS --define HAVE_ZEITGEIST"
+fi
+if test "x$enable_zeitgeist" = "xno" -a "x$enable_zeitgeist2" = "xyes" ; then
+    PLANK_CORE_OPTIONAL_PKGS="$PLANK_CORE_OPTIONAL_PKGS $ZEITGEIST2_PKGS"
+    PLANK_CORE_VALA_PKGS="$PLANK_CORE_VALA_PKGS --pkg zeitgeist-2.0"
+    VALAFLAGS="$VALAFLAGS --define HAVE_ZEITGEIST2"
+    enable_zeitgeist=yes
+fi
+AM_CONDITIONAL([HAVE_ZEITGEIST], [test "x$enable_zeitgeist" = "xyes" -o "x$enable_zeitgeist2" = "xyes"])
 
 
 AC_SUBST(PLANK_CORE_OPTIONAL_PKGS)
@@ -426,6 +454,7 @@
     Use gee-0.8.................:  ${enable_gee_0_8}
     Dbusmenu support............:  ${enable_dbusmenu}
     HiDPI support...............:  ${enable_hidpi}
+    Zeitgeist support...........:  ${enable_zeitgeist}
     XInput Barriers support.....:  ${enable_barriers}
 
     Apport support..............:  ${enable_apport}

=== modified file 'lib/Items/ApplicationDockItemProvider.vala'
--- lib/Items/ApplicationDockItemProvider.vala	2016-01-30 12:08:11 +0000
+++ lib/Items/ApplicationDockItemProvider.vala	2016-01-30 20:46:42 +0000
@@ -34,6 +34,11 @@
 		bool delay_items_monitor_handle = false;
 		Gee.ArrayList<GLib.File> queued_files;
 		
+#if HAVE_ZEITGEIST || HAVE_ZEITGEIST2
+		Zeitgeist.Log zglog = Zeitgeist.Log.get_default();
+		Gee.ArrayList<TransientDockItem> suggested_app_items = new Gee.ArrayList<TransientDockItem> ();
+#endif
+		
 		/**
 		 * Creates a new container for dock items.
 		 *
@@ -61,12 +66,32 @@
 			} catch (Error e) {
 				critical ("Unable to watch the launchers directory. (%s)", e.message);
 			}
+			
+#if HAVE_ZEITGEIST || HAVE_ZEITGEIST2
+			// FIXME Workaround to have an initialized PositionManager
+			Idle.add (() => {
+				// FIXME
+				//controller.position_manager.notify["MaxItemCount"].connect (update_suggested_apps);
+				update_suggested_apps ();
+				return false;
+			});
+			
+			// FIXME Update suggested apps in a "proper" interval
+			Timeout.add_seconds (3600, () => {
+				update_suggested_apps ();
+				return true;
+			});
+#endif
 		}
 		
 		~ApplicationDockItemProvider ()
 		{
 			queued_files = null;
 			
+#if HAVE_ZEITGEIST || HAVE_ZEITGEIST2
+			// FIXME
+			//controller.position_manager.notify["MaxItemCount"].disconnect (update_suggested_apps);
+#endif
 			Matcher.get_default ().application_opened.disconnect (app_opened);
 			
 			if (items_monitor != null) {
@@ -198,6 +223,84 @@
 			return item_list.to_array ();
 		}
 		
+#if HAVE_ZEITGEIST || HAVE_ZEITGEIST2
+		public void update_suggested_apps ()
+		{
+			Logger.verbose ("ApplicationDockItemProvider.update_suggested_apps ()");
+			
+			// FIXME
+			//var max_item_count = controller.position_manager.MaxItemCount;
+			var max_item_count = 15;
+			//var max_item_count = controller.position_manager.MaxItemCount;
+			var num_events = 3;
+			if (get_n_items (true, suggested_app_items) >= max_item_count - num_events)
+				return;
+			
+			Logger.verbose ("ApplicationDockItemProvider.update_suggested_apps ().run (%i)", num_events);
+			
+#if HAVE_ZEITGEIST
+			var templates = new PtrArray ();
+#else
+			var templates = new GenericArray<Zeitgeist.Event> ();
+#endif
+			// Consider used applications within the last 2 months
+			var now = new DateTime.now_local ().to_unix () * 1000;
+			var start = now - 60 * 86400000;
+			var time_range = new Zeitgeist.TimeRange (start, now);
+			
+			var event = new Zeitgeist.Event ();
+#if HAVE_ZEITGEIST
+			var subject = new Zeitgeist.Subject ();
+			subject.set_uri ("application://*");
+#else
+			var subject = new Zeitgeist.Subject.full ("application://*");
+#endif
+			event.add_subject (subject);
+			templates.add (event);
+			
+			zglog.find_events.begin (time_range, templates, Zeitgeist.StorageState.ANY, num_events,
+				Zeitgeist.ResultType.MOST_POPULAR_SUBJECTS, null, (obj, res) => {
+				Zeitgeist.ResultSet events = zglog.find_events.end (res);
+				var new_suggested_app_items = new Gee.ArrayList<TransientDockItem> ();
+				foreach (var e in events) {
+#if HAVE_ZEITGEIST
+					var app_uri = e.get_subject (0).get_uri ();
+#else
+					var app_uri = e.subjects[0].uri;
+#endif
+					
+					// Find a matching desktop-file and create new TransientDockItem
+					var desktop_file = desktop_file_for_application_uri (app_uri);
+					if (desktop_file == null)
+						continue;
+					
+					var launcher_uri = desktop_file.get_uri ();
+					DockItem? current_item = item_for_uri (launcher_uri);
+					if (current_item == null) {
+						current_item = new TransientDockItem.with_launcher (launcher_uri);
+						add (current_item);
+						Logger.verbose ("SuggestedApp added: %s[%s, %i]", current_item.Text, current_item.Launcher, (int)current_item);
+						new_suggested_app_items.add (current_item as TransientDockItem);
+					} else if (current_item is TransientDockItem) {
+						Logger.verbose ("SuggestedApp confirmed: %s[%s, %i]", current_item.Text, current_item.Launcher, (int)current_item);
+						new_suggested_app_items.add (current_item as TransientDockItem);
+					}
+				}
+				
+				suggested_app_items.remove_all (new_suggested_app_items);
+				foreach (var item in suggested_app_items) {
+					if (!can_remove_transient_item (item))
+						return;
+					
+					Logger.verbose ("SuggestedApp removed: %s[%s, %i]", item.Text, item.Launcher, (int)item);
+					remove (item);
+				}
+				
+				suggested_app_items = new_suggested_app_items;
+			});
+		}
+#endif
+		
 		protected virtual void app_opened (Bamf.Application app)
 		{
 			// Make sure internal window-list of Wnck is most up to date
@@ -260,6 +363,12 @@
 			}
 			
 			queued_files.clear ();
+			
+#if HAVE_ZEITGEIST || HAVE_ZEITGEIST2
+			// Update suggested applications while it is possible the
+			// added application(s) were
+			update_suggested_apps ();
+#endif
 		}
 		
 		[CCode (instance_pos = -1)]
@@ -291,6 +400,37 @@
 				process_queued_files ();
 		}
 		
+		public bool can_remove_transient_item (TransientDockItem item)
+		{
+			return (item != null
+				&& item.App == null
+#if HAVE_ZEITGEIST || HAVE_ZEITGEIST2
+				&& !suggested_app_items.contains (item)
+#endif
+				&& !item.has_unity_info ());
+		}
+		
+#if HAVE_ZEITGEIST || HAVE_ZEITGEIST2
+		int get_n_items (bool visible_only, Gee.List<DockElement>? ignore = null)
+		{
+			unowned Gee.List<DockElement> elements = (visible_only ? visible_elements : internal_elements);
+			var elements_count = elements.size;
+			
+			//foreach (var item in items)
+			//	if (item.is_virtual ());
+			//		items_count--;
+			
+			if (ignore == null)
+				return elements_count;
+			
+			foreach (var element in ignore)
+				if (elements.contains (element))
+					elements_count--;
+			
+			return elements_count;
+		}
+#endif
+		
 		protected override void connect_element (DockElement element)
 		{
 			base.connect_element (element);
@@ -332,7 +472,7 @@
 				// Remove item which only exists because of the presence of
 				// this removed LauncherEntry interface
 				unowned TransientDockItem? transient_item = item as TransientDockItem;
-				if (transient_item != null && transient_item.App == null)
+				if (can_remove_transient_item (transient_item))
 					remove (transient_item);
 				
 				break;

=== modified file 'lib/Items/DefaultApplicationDockItemProvider.vala'
--- lib/Items/DefaultApplicationDockItemProvider.vala	2015-11-03 10:37:19 +0000
+++ lib/Items/DefaultApplicationDockItemProvider.vala	2016-01-30 20:46:42 +0000
@@ -1,5 +1,6 @@
 //
 //  Copyright (C) 2013 Rico Tzschichholz
+//  Copyright (C) 2013 Seif Lotfy
 //
 //  This file is part of Plank.
 //
@@ -124,8 +125,9 @@
 		
 		void app_closed (DockItem item)
 		{
-			if (item is TransientDockItem
-				&& !(((TransientDockItem) item).has_unity_info ()))
+			unowned TransientDockItem transient_item = (item as TransientDockItem);
+			transient_item.App = null;
+			if (can_remove_transient_item (transient_item))
 				remove (item);
 		}
 		
@@ -222,7 +224,14 @@
 				app = ((ApplicationDockItem) item).App;
 			
 			if (app == null || !app.is_running ()) {
+				// TODO Replace the item if it is a suggested appliction
 				remove (item);
+				
+#if HAVE_ZEITGEIST || HAVE_ZEITGEIST2
+				// Update suggested applications while it is possible the
+				// removed application is one
+				update_suggested_apps ();
+#endif
 				return;
 			}
 			

=== modified file 'lib/Items/TransientDockItem.vala'
--- lib/Items/TransientDockItem.vala	2015-11-03 10:37:19 +0000
+++ lib/Items/TransientDockItem.vala	2016-01-30 20:46:42 +0000
@@ -95,6 +95,11 @@
 			return false;
 		}
 		
+		public bool is_virtual ()
+		{
+			return (App == null);
+		}
+		
 		/**
 		 * {@inheritDoc}
 		 */

=== modified file 'lib/libplank.symbols'
--- lib/libplank.symbols	2016-01-30 12:08:11 +0000
+++ lib/libplank.symbols	2016-01-30 20:46:42 +0000
@@ -49,6 +49,7 @@
 plank_application_dock_item_new_with_dockitem_filename
 plank_application_dock_item_parse_launcher
 plank_application_dock_item_provider_app_opened
+plank_application_dock_item_provider_can_remove_transient_item
 plank_application_dock_item_provider_construct
 plank_application_dock_item_provider_delay_items_monitor
 plank_application_dock_item_provider_get_item_list_string
@@ -57,6 +58,7 @@
 plank_application_dock_item_provider_item_for_application
 plank_application_dock_item_provider_new
 plank_application_dock_item_provider_resume_items_monitor
+plank_application_dock_item_provider_update_suggested_apps
 plank_application_dock_item_set_urgent
 plank_application_dock_item_unity_reset
 plank_application_dock_item_unity_update
@@ -679,6 +681,7 @@
 plank_transient_dock_item_construct
 plank_transient_dock_item_construct_with_launcher
 plank_transient_dock_item_get_type
+plank_transient_dock_item_is_virtual
 plank_transient_dock_item_new
 plank_transient_dock_item_new_with_launcher
 plank_unity_add_client

