summaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
authorMichal Hruby <michal.mhr@gmail.com>2010-08-03 16:09:03 +0200
committerMichal Hruby <michal.mhr@gmail.com>2010-08-03 16:09:03 +0200
commitce769e4c7824041bf3144b3a583ef9b98d17d116 (patch)
treefc9d70d72eb097148757a3c690190a29f0dc9591 /src
parentcc7e2f86680c65bcc390c7e5819c2b40bff8f111 (diff)
Added autofoo
Diffstat (limited to 'src')
-rw-r--r--src/Makefile.am18
-rw-r--r--src/configuration.vala30
-rw-r--r--src/data-provider.vala51
-rw-r--r--src/recent-manager-provider.vala263
-rw-r--r--src/zeitgeist-datahub.vala232
5 files changed, 594 insertions, 0 deletions
diff --git a/src/Makefile.am b/src/Makefile.am
new file mode 100644
index 0000000..3874aa2
--- /dev/null
+++ b/src/Makefile.am
@@ -0,0 +1,18 @@
+VALAFLAGS = \
+ --pkg dbus-glib-1 \
+ --pkg gtk+-2.0 \
+ --pkg zeitgeist-1.0 \
+ $(NULL)
+
+datahubdir = $(bindir)
+
+datahub_PROGRAMS = zeitgeist-datahub
+
+zeitgeist_datahub_CFLAGS = $(DATAHUB_MODULES_CFLAGS) $(GTK_CFLAGS)
+zeitgeist_datahub_LDADD = $(DATAHUB_MODULES_LIBS) $(GTK_LIBS)
+zeitgeist_datahub_SOURCES = \
+ data-provider.vala \
+ recent-manager-provider.vala \
+ zeitgeist-datahub.vala \
+ $(NULL)
+
diff --git a/src/configuration.vala b/src/configuration.vala
new file mode 100644
index 0000000..94b4c09
--- /dev/null
+++ b/src/configuration.vala
@@ -0,0 +1,30 @@
+/*
+ * Zeitgeist
+ *
+ * Copyright (C) 2010 Michal Hruby <michal.mhr@gmail.com>
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License as published by
+ * the Free Software Foundation, either version 3 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 Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ *
+ * Authored by Michal Hruby <michal.mhr@gmail.com>
+ *
+ */
+
+using Zeitgeist;
+
+[DBus (name = "org.gnome.zeitgeist.datahub")]
+interface DataProviderService : Object
+{
+ public abstract string[] get_data_providers () throws DBus.Error;
+}
+
diff --git a/src/data-provider.vala b/src/data-provider.vala
new file mode 100644
index 0000000..3c16b11
--- /dev/null
+++ b/src/data-provider.vala
@@ -0,0 +1,51 @@
+/*
+ * Zeitgeist
+ *
+ * Copyright (C) 2010 Michal Hruby <michal.mhr@gmail.com>
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License as published by
+ * the Free Software Foundation, either version 3 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 Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ *
+ * Authored by Michal Hruby <michal.mhr@gmail.com>
+ *
+ */
+
+using Zeitgeist;
+
+public abstract class DataProvider : Object
+{
+ public abstract string unique_id { get; construct set; }
+ public abstract string name { get; construct set; }
+ public abstract string description { get; construct set; }
+
+ public abstract DataHub datahub { get; construct set; }
+ public abstract bool enabled { get; set; default = true; }
+ public abstract bool register { get; construct set; default = true; }
+ public int64 last_timestamp { get; set; }
+
+ public virtual void start ()
+ {
+ items_available ();
+ }
+
+ protected abstract List<Event> _get_items ();
+ // DataHub will call get_items when items_available is emitted
+ public List<Event> get_items ()
+ {
+ if (enabled) return _get_items ();
+ return new List<Event> ();
+ }
+
+ public signal void items_available ();
+}
+
diff --git a/src/recent-manager-provider.vala b/src/recent-manager-provider.vala
new file mode 100644
index 0000000..9952893
--- /dev/null
+++ b/src/recent-manager-provider.vala
@@ -0,0 +1,263 @@
+/*
+ * Zeitgeist
+ *
+ * Copyright (C) 2010 Michal Hruby <michal.mhr@gmail.com>
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License as published by
+ * the Free Software Foundation, either version 3 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 Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ *
+ * Authored by Michal Hruby <michal.mhr@gmail.com>
+ *
+ */
+
+using Zeitgeist;
+
+public class RecentManagerGtk : DataProvider
+{
+ public RecentManagerGtk (DataHub datahub)
+ {
+ GLib.Object (unique_id: "com.zeitgeist-project,datahub,recent",
+ name: "Recently Used Documents",
+ description: "Logs events from GtkRecentlyUsed",
+ datahub: datahub);
+ }
+
+ // if vala didn't have bug in construct-only properties, the properties
+ // would be construct-only
+ public override string unique_id { get; construct set; }
+ public override string name { get; construct set; }
+ public override string description { get; construct set; }
+
+ public override DataHub datahub { get; construct set; }
+ public override bool enabled { get; set; default = true; }
+ public override bool register { get; construct set; default = true; }
+
+ private unowned Gtk.RecentManager recent_manager;
+ private HashTable<string, string> app_to_desktop_file;
+ private uint idle_id = 0;
+
+ construct
+ {
+ app_to_desktop_file = new HashTable<string, string> (str_hash, str_equal);
+
+ recent_manager = Gtk.RecentManager.get_default ();
+ recent_manager.set_limit (-1);
+
+ recent_manager.changed.connect (this.items_changed);
+ }
+
+ private void items_changed ()
+ {
+ if (idle_id == 0)
+ {
+ idle_id = Idle.add (() =>
+ {
+ items_available ();
+ idle_id = 0;
+ return false;
+ });
+ }
+ }
+
+ protected override List<Event> _get_items ()
+ {
+ List<Event> events = new List<Event> ();
+
+ if (!enabled) return events;
+
+ int64 signal_time = Timestamp.now ();
+
+ foreach (Gtk.RecentInfo ri in recent_manager.get_items ())
+ {
+ unowned string uri = ri.get_uri ();
+ if (!ri.exists () || ri.get_private_hint () ||
+ uri.has_prefix ("file:///tmp/"))
+ {
+ continue;
+ }
+
+ var last_app = ri.last_application ().strip ();
+ unowned string exec_str;
+ uint count;
+ ulong time_;
+ bool registered = ri.get_application_info (last_app, out exec_str,
+ out count, out time_);
+ if (!registered)
+ {
+ warning ("%s was not registered in RecentInfo item %p", last_app, ri);
+ continue;
+ }
+
+ string[] exec = exec_str.split_set (" \t\n", 2);
+
+ string? desktop_file;
+ if (exec[0] == "soffice" || exec[0] == "ooffice")
+ {
+ // special case OpenOffice... since it must do everything differently
+ desktop_file = get_ooo_desktop_file_for_mimetype (ri.get_mime_type ());
+ }
+ else
+ {
+ desktop_file = find_desktop_file_for_app (exec[0]);
+ }
+
+ if (desktop_file == null)
+ {
+ warning ("Desktop file for %s was not found", exec[0]);
+ continue; // this makes us sad panda
+ }
+
+ var actor = "application://%s".printf (Path.get_basename (desktop_file));
+ unowned string? right_sep = ri.get_uri ().rstr (Path.DIR_SEPARATOR_S);
+ string origin = right_sep != null ?
+ ri.get_uri ().ndup ((char*) right_sep - (char*) ri.get_uri ()) :
+ ri.get_uri ();
+ var subject =
+ new Subject.full (ri.get_uri (),
+ interpretation_for_mimetype (ri.get_mime_type ()),
+ manifestation_for_uri (ri.get_uri ()),
+ ri.get_mime_type (),
+ origin,
+ ri.get_display_name (),
+ ""); // FIXME: storage?!
+
+ Event event;
+ int64 timestamp;
+
+ // zeitgeist checks for duplicated events, so we can do this
+ event = new Event.full (ZG_CREATE_EVENT,
+ ZG_USER_ACTIVITY,
+ actor,
+ subject, null);
+ event.set_timestamp (ri.get_added () * 1000);
+ timestamp = event.get_timestamp ();
+ if (timestamp > last_timestamp && timestamp > 0)
+ {
+ events.prepend ((owned) event);
+ }
+
+ event = new Event.full (ZG_MODIFY_EVENT,
+ ZG_USER_ACTIVITY,
+ actor,
+ subject, null);
+ event.set_timestamp (ri.get_modified () * 1000);
+ timestamp = event.get_timestamp ();
+ if (timestamp > last_timestamp && timestamp > 0)
+ {
+ events.prepend ((owned) event);
+ }
+
+ event = new Event.full (ZG_ACCESS_EVENT,
+ ZG_USER_ACTIVITY,
+ actor,
+ subject, null);
+ event.set_timestamp (ri.get_visited () * 1000);
+ timestamp = event.get_timestamp ();
+ if (timestamp > last_timestamp && timestamp > 0)
+ {
+ events.prepend ((owned) event);
+ }
+
+ }
+
+ last_timestamp = signal_time;
+
+ events.reverse ();
+ return events;
+ }
+
+ private string? get_ooo_desktop_file_for_mimetype (string mimetype)
+ {
+ return find_desktop_file_for_app ("ooffice", mimetype);
+ }
+
+ private string? find_desktop_file_for_app (string app_name,
+ string? mimetype = null)
+ {
+ string hash_name = mimetype != null ?
+ "%s::%s".printf (app_name, mimetype) : app_name;
+ unowned string? in_cache = app_to_desktop_file.lookup (hash_name);
+ if (in_cache != null)
+ {
+ return in_cache;
+ }
+
+ string[] data_dirs = Environment.get_system_data_dirs ();
+ data_dirs += Environment.get_user_data_dir ();
+
+ foreach (unowned string dir in data_dirs)
+ {
+ var p = Path.build_filename (dir, "applications",
+ "%s.desktop".printf (app_name),
+ null);
+ var f = File.new_for_path (p);
+ if (f.query_exists (null))
+ {
+ app_to_desktop_file.insert (hash_name, p);
+ // FIXME: we're not checking mimetype here!
+ return p;
+ }
+ }
+
+ foreach (unowned string dir in data_dirs)
+ {
+ var p = Path.build_filename (dir, "applications", null);
+ var app_dir = File.new_for_path (p);
+ if (!app_dir.query_exists (null)) continue;
+
+ try
+ {
+ var enumerator =
+ app_dir.enumerate_children (FILE_ATTRIBUTE_STANDARD_NAME, 0, null);
+ FileInfo fi = enumerator.next_file (null);
+ while (fi != null)
+ {
+ if (fi.get_name ().has_suffix (".desktop"))
+ {
+ string contents;
+ var desktop_file = Path.build_filename (p, fi.get_name (), null);
+ var f = File.new_for_path (desktop_file);
+ try
+ {
+ if (f.load_contents (null, out contents, null, null))
+ {
+ if ("Exec=%s".printf (app_name) in contents)
+ {
+ if (mimetype == null || mimetype in contents)
+ {
+ app_to_desktop_file.insert (hash_name, desktop_file);
+ return desktop_file;
+ }
+ }
+ }
+ }
+ catch (GLib.Error err)
+ {
+ warning ("%s", err.message);
+ }
+ }
+ fi = enumerator.next_file (null);
+ }
+
+ enumerator.close (null);
+ }
+ catch (GLib.Error err)
+ {
+ warning ("%s", err.message);
+ }
+ }
+
+ return null;
+ }
+}
+
diff --git a/src/zeitgeist-datahub.vala b/src/zeitgeist-datahub.vala
new file mode 100644
index 0000000..033f154
--- /dev/null
+++ b/src/zeitgeist-datahub.vala
@@ -0,0 +1,232 @@
+/*
+ * Zeitgeist
+ *
+ * Copyright (C) 2010 Michal Hruby <michal.mhr@gmail.com>
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License as published by
+ * the Free Software Foundation, either version 3 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 Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ *
+ * Authored by Michal Hruby <michal.mhr@gmail.com>
+ *
+ */
+
+using Zeitgeist;
+
+[DBus (name = "org.gnome.zeitgeist.datahub")]
+public interface DataHubService : Object
+{
+ public abstract string[] get_data_providers () throws DBus.Error;
+}
+
+public class DataHub : Object, DataHubService
+{
+ private Zeitgeist.Log zg_log;
+ private Zeitgeist.DataSourceRegistry registry;
+ private MainLoop main_loop;
+ private List<DataProvider> providers;
+ private List<DataSource> sources_info; // list got from ZG's Registry
+ private List<Event> queued_events;
+ private uint idle_id = 0;
+
+ public DataHub ()
+ {
+ GLib.Object ();
+ }
+
+ construct
+ {
+ providers = new List<DataProvider> ();
+ sources_info = new List<DataSource> ();
+ queued_events = new List<Event> ();
+ main_loop = new MainLoop ();
+
+ zg_log = new Zeitgeist.Log ();
+ zg_log.notify["connected"].connect (() =>
+ {
+ if (!zg_log.is_connected ())
+ {
+ debug ("Zeitgeist-daemon disappeared from the bus, exitting...");
+ quit ();
+ }
+ });
+
+ registry = new DataSourceRegistry ();
+ start_data_providers ();
+ }
+
+ private async void start_data_providers ()
+ {
+ try
+ {
+ var sources = yield registry.get_data_sources (null);
+ for (uint i=0; i<sources.len; i++)
+ {
+ sources_info.prepend (sources.index (i) as DataSource);
+ }
+ }
+ catch (GLib.Error err)
+ {
+ warning ("%s", err.message);
+ }
+ // TODO: load all datasources once we do them as modules
+ /*
+ foreach (var datasource in datasources)
+ {
+ providers.prepend (datasource.run ());
+ }
+ */
+ providers.prepend (new RecentManagerGtk (this));
+
+ foreach (unowned DataProvider prov in providers)
+ {
+ bool enabled = true;
+ if (prov.register)
+ {
+ var ds = new DataSource.full (prov.unique_id,
+ prov.name,
+ prov.description,
+ new PtrArray ()); // FIXME: templates!
+ try
+ {
+ enabled = yield registry.register_data_source (ds, null);
+ }
+ catch (GLib.Error reg_err)
+ {
+ warning ("%s", reg_err.message);
+ }
+ }
+ prov.items_available.connect (this.items_available);
+ if (enabled)
+ {
+ int64 timestamp = 0;
+ foreach (var src in sources_info)
+ {
+ if (src.get_unique_id () == prov.unique_id)
+ {
+ timestamp = src.get_timestamp ();
+ break;
+ }
+ }
+ prov.last_timestamp = timestamp;
+ prov.start ();
+ }
+ }
+ }
+
+ private void items_available (DataProvider prov)
+ {
+ queued_events.concat (prov.get_items ());
+ if (queued_events != null && idle_id == 0)
+ {
+ idle_id = Idle.add (() =>
+ {
+ insert_events ();
+ idle_id = 0;
+ return false;
+ });
+ }
+ }
+
+ private void insert_events ()
+ {
+ debug ("Inserting %u events", queued_events.length ());
+
+ batch_insert_events ();
+
+ queued_events = new List<Event> ();
+ }
+
+ protected async void batch_insert_events ()
+ {
+ // copy the events to GenericArray (with a ref on them)
+ GenericArray<Event> all_events = new GenericArray<Event> ();
+ foreach (var e in queued_events)
+ {
+ all_events.add (e);
+ }
+
+ while (all_events.length > 0)
+ {
+ uint elements_pushed = uint.min ((uint) all_events.length, 100);
+ PtrArray ptr_arr = new PtrArray.with_free_func (Object.unref);
+ // careful here, the ptr array does ref_sink on the events
+ // inside Log.insert_events
+ for (uint i=0; i<elements_pushed; i++) ptr_arr.add (all_events[i]);
+
+ try
+ {
+ yield zg_log.insert_events_from_ptrarray ((owned) ptr_arr, null);
+ }
+ catch (GLib.Error err)
+ {
+ warning ("Error during inserting events: %s", err.message);
+ }
+
+ all_events.remove_range (0, elements_pushed);
+ }
+ }
+
+ protected void run ()
+ {
+ try
+ {
+ var connection = DBus.Bus.get (DBus.BusType.SESSION);
+ dynamic DBus.Object bus =
+ connection.get_object ("org.freedesktop.DBus",
+ "/org/freedesktop/DBus",
+ "org.freedesktop.DBus");
+
+ uint request_name_result =
+ bus.request_name ("org.gnome.zeitgeist.datahub",
+ (uint) DBus.NameFlag.DO_NOT_QUEUE);
+
+ if (request_name_result == DBus.RequestNameReply.PRIMARY_OWNER)
+ {
+ connection.register_object ("/org/gnome/zeitgeist/datahub", this);
+ main_loop.run ();
+ }
+ else
+ {
+ warning ("Unable to get name \"org.gnome.zeitgeist.datahub\"" +
+ " on the bus!");
+ }
+ }
+ catch (GLib.Error err)
+ {
+ warning ("%s", err.message);
+ }
+ }
+
+ protected void quit ()
+ {
+ // dispose all providers
+ providers = new List<DataProvider> ();
+ main_loop.quit ();
+ }
+
+ public string[] get_data_providers () throws DBus.Error
+ {
+ string [] arr = {};
+ foreach (var provider in providers)
+ {
+ arr += provider.unique_id;
+ }
+ return arr;
+ }
+
+ public static void main (string[] args)
+ {
+ var hub = new DataHub ();
+ hub.run ();
+ }
+}