2
* Copyright (C) 2010 Michal Hruby <michal.mhr@gmail.com>
4
* This program is free software; you can redistribute it and/or modify
5
* it under the terms of the GNU General Public License as published by
6
* the Free Software Foundation; either version 2 of the License, or
7
* (at your option) any later version.
9
* This program is distributed in the hope that it will be useful,
10
* but WITHOUT ANY WARRANTY; without even the implied warranty of
11
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12
* GNU General Public License for more details.
14
* You should have received a copy of the GNU General Public License
15
* along with this program; if not, write to the Free Software
16
* Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
18
* Author : Michal Hruby <michal.mhr@gmail.com>
22
using DesktopAgnostic;
25
[DBus (name = "org.wncksync.Matcher")]
26
interface WnckSyncMatcher : GLib.Object
28
public abstract string desktop_file_for_xid (uint32 xid) throws DBus.Error;
29
public abstract bool window_match_is_ready (uint32 xid) throws DBus.Error;
30
public abstract void register_desktop_file_for_pid (string filename, int32 pid) throws DBus.Error;
31
public abstract uint32[] xids_for_desktop_file (string filename) throws DBus.Error;
34
struct DesktopFileInfo
40
class RelatedApplet : AppletSimple
42
private unowned Wnck.Screen wnck_screen;
44
private DesktopLookupCached lookup = new DesktopLookupCached ();
45
private Zeitgeist.Log zg_log = new Zeitgeist.Log ();
46
private Overlay throbber = new OverlayThrobber ();
47
private Overlay star_overlay = new OverlayThemedIcon (Gtk.STOCK_ABOUT);
48
private HashTable<string, DesktopFileInfo?> desktop_file_info;
50
private Awn.Dialog dialog;
51
private Gtk.VBox vbox;
52
private string? current_desktop_file_path;
54
public RelatedApplet (string canonical_name, string uid, int panel_id)
56
Object (canonical_name: canonical_name, uid: uid, panel_id: panel_id);
58
desktop_file_info = new HashTable<string, DesktopFileInfo?> (
63
wnck_screen = Wnck.Screen.get_default ();
64
Wnck.set_client_type (Wnck.ClientType.PAGER);
66
wnck_screen.window_opened.connect (this.window_opened);
67
wnck_screen.active_window_changed.connect (this.window_changed);
70
this.add_overlay (throbber);
71
this.add_overlay (star_overlay);
73
this.set_icon_name ("zeitgeist-logo");
74
this.clicked.connect (this.on_clicked);
77
this.dialog = new Awn.Dialog.for_widget (this);
82
star_overlay.active = false;
83
star_overlay.gravity = Gdk.Gravity.SOUTH_EAST;
84
(star_overlay as OverlayThemedIcon).scale = 0.3;
86
throbber.gravity = Gdk.Gravity.SOUTH_WEST;
87
(throbber as OverlayThrobber).scale = 0.3;
90
private void window_opened (Wnck.Window window)
92
string? desktop_file = lookup.search_by_wnck_window (window);
93
if (desktop_file == null)
98
DBus.Connection con = DBus.Bus.get (DBus.BusType.SESSION);
99
var matcher = (WnckSyncMatcher) con.get_object (
100
"org.wncksync.Matcher",
101
"/org/wncksync/Matcher",
102
"org.wncksync.Matcher");
104
string df = matcher.desktop_file_for_xid ((uint32) window.get_xid ());
105
if (df != null && df != "")
107
debug ("wncksync found match: %s", df);
111
catch (GLib.Error err)
113
// ignore that we don't have wncksync
117
// get mimetypes this desktop file supports
118
if (desktop_file != null)
120
unowned DesktopFileInfo? df_data;
121
df_data = desktop_file_info.lookup (desktop_file);
124
var keyfile = new KeyFile ();
125
string[] mimetypes = {};
126
string app_name = "";
129
keyfile.load_from_file (desktop_file, KeyFileFlags.NONE);
130
app_name = keyfile.get_locale_string ("Desktop Entry", "Name", null);
131
mimetypes = keyfile.get_string_list ("Desktop Entry", "MimeType");
133
catch (GLib.Error err)
136
var w = DesktopFileInfo ();
137
w.name = (owned) app_name;
138
w.mimetypes = (owned) mimetypes;
139
desktop_file_info.insert(desktop_file, w);
142
window.set_data ("desktop-file-path", desktop_file);
145
private void window_changed (Wnck.Window? old_window)
147
Wnck.Window? active = wnck_screen.get_active_window ();
151
// don't update star after activating our dialog
152
//var dialog_flags = dialog.get_flags () & Gtk.WidgetFlags.VISIBLE;
153
if (dialog.get_visible ()) //(dialog_flags != 0)
159
string? desktop_file = active.get_data ("desktop-file-path");
160
if (desktop_file != null)
162
update_star (desktop_file);
166
star_overlay.active = false;
171
private async void update_star (string desktop_file)
173
var ptr_array = new PtrArray ();
174
unowned string actor = desktop_file.rchr (-1, '/').offset (1);
176
var event = new Event ();
177
var helper_event_list = new List<Event> ();
178
event.set_actor ("application://" + actor);
179
ptr_array.add (event);
181
unowned DesktopFileInfo? df_data;
182
df_data = desktop_file_info.lookup (desktop_file);
183
if (df_data != null && df_data.mimetypes.length > 0)
185
foreach (unowned string mimetype in df_data.mimetypes)
187
var mime_event = new Event ();
188
var subject = new Subject ();
189
subject.set_mimetype (mimetype);
190
mime_event.add_subject (subject);
191
// helper with references
192
helper_event_list.prepend (mime_event);
193
ptr_array.add (mime_event);
197
// anything for these?
198
var events = yield zg_log.find_event_ids (new TimeRange.to_now (),
201
ResultType.MOST_RECENT_EVENTS,
204
star_overlay.active = events.length > 0;
205
this.set_tooltip_text (events.length > 0 ?
206
"Show items related to %s".printf (df_data.name) : null
210
private async bool get_recent_by_mimetype (string[] mimetypes)
212
var helper_event_list = new List<Event> ();
213
var ptr_array = new PtrArray ();
214
foreach (unowned string mimetype in mimetypes)
216
var event = new Event ();
217
var subject = new Subject ();
218
subject.set_mimetype (mimetype);
219
event.add_subject (subject);
221
// helper with references
222
helper_event_list.prepend (event);
223
ptr_array.add (event);
226
var events = yield zg_log.find_events (new TimeRange.to_now (),
228
StorageState.ANY, 16,
229
ResultType.MOST_RECENT_SUBJECTS,
232
int results_pushed = 0;
233
foreach (unowned Event e in events)
235
if (e.num_subjects () > 0)
238
Subject s = e.get_subject (0);
239
if (results_pushed < 3 && push_result (e, s)) results_pushed++;
243
return events.size () > 0;
246
private async bool get_events_for_actor (string? actor)
248
var ptr_array = new PtrArray ();
249
var event = new Event ();
250
if (actor != null) event.set_actor ("application://" + actor);
251
ptr_array.add (event);
252
var events = yield zg_log.find_events (new TimeRange.to_now (),
254
StorageState.ANY, 16,
255
ResultType.MOST_POPULAR_SUBJECTS,
258
int results_pushed = 0;
259
foreach (unowned Event e in events)
261
if (e.num_subjects () > 0)
264
Subject s = e.get_subject (0);
265
if (results_pushed < 4 && push_result (e, s)) results_pushed++;
269
return events.size () > 0;
272
private async void build_dialog (string? desktop_file)
274
if (this.vbox != null) this.vbox.destroy ();
275
this.vbox = new Gtk.VBox (false, 3);
276
this.dialog.add (this.vbox);
281
current_desktop_file_path = desktop_file;
282
throbber.active = true;
284
if (desktop_file != null)
286
// get items by mimetype
287
unowned DesktopFileInfo? df_data;
288
df_data = desktop_file_info.lookup (desktop_file);
289
if (df_data != null && df_data.mimetypes.length > 0)
291
found1 = yield get_recent_by_mimetype (df_data.mimetypes);
296
found1 = yield get_recent_by_mimetype ({});
300
if (found1) vbox.add (new Gtk.HSeparator ());
303
unowned string actor = null;
304
if (desktop_file != null) actor = desktop_file.rchr (-1, '/').offset (1);
305
found2 = yield get_events_for_actor (actor);
307
if (desktop_file != null && !found1 && !found2)
313
throbber.active = false;
315
if (!found1 && !found2)
317
var l = new Gtk.Label ("There are no items to display...");
320
this.dialog.show_all ();
323
private void on_clicked ()
325
var dialog_flags = dialog.get_flags () & Gtk.WidgetFlags.VISIBLE;
326
if (dialog_flags != 0)
331
dialog.hide_on_unfocus = true;
333
Wnck.Window? active = wnck_screen.get_active_window ();
335
string? desktop_file = active.get_data ("desktop-file-path");
336
build_dialog (desktop_file);
339
private bool push_result (Zeitgeist.Event event, Zeitgeist.Subject subject)
341
var f = File.new_for_uri (subject.get_uri ());
342
if (f.is_native () && !f.query_exists (null)) return false;
344
string? text = subject.get_text ();
345
if (text == null) text = f.get_basename ();
350
var fi = f.query_info (FILE_ATTRIBUTE_STANDARD_ICON, 0, null);
351
icon = fi.get_icon ();
355
icon = g_content_type_get_icon (subject.get_mimetype ());
358
var button = new Gtk.Button ();
359
var hbox = new Gtk.HBox (false, 6);
360
var image = new Gtk.Image.from_gicon (icon, Gtk.IconSize.BUTTON);
361
var label = new Gtk.Label (text);
362
label.set_ellipsize (Pango.EllipsizeMode.MIDDLE);
363
label.set_max_width_chars (35);
365
hbox.pack_start (image, false, true, 0);
366
hbox.pack_start (label, true, true, 0);
367
button.set_relief (Gtk.ReliefStyle.NONE);
368
button.set_focus_on_click (false);
370
button.set_tooltip_text (f.is_native () ? f.get_path () : f.get_uri ());
371
var desktop_file = this.current_desktop_file_path;
372
button.clicked.connect (() => {
373
var context = new AppLaunchContext ();
374
if (desktop_file == null)
376
AppInfo.launch_default_for_uri (f.get_uri (), context);
380
var app_info = new DesktopAppInfo.from_filename (desktop_file);
381
if (app_info.supports_uris ())
383
var l = new List<string> ();
384
l.append (f.get_uri ());
385
app_info.launch_uris (l, context);
389
var l = new List<File> ();
391
app_info.launch (l, context);
397
this.vbox.add (button);
404
awn_applet_factory_initp (string canonical_name, string uid, int panel_id)
406
return new RelatedApplet (canonical_name, uid, panel_id);