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 event2 = new 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 subject = new Subject ();
188
subject.set_mimetype (mimetype);
189
event2.add_subject (subject);
191
ptr_array.add (event2);
194
// anything for these?
195
var events = yield zg_log.find_event_ids (new TimeRange.to_now (),
198
ResultType.MOST_POPULAR_SUBJECTS,
201
star_overlay.active = events.length > 0;
202
this.set_tooltip_text (events.length > 0 ?
203
"Show items related to %s".printf (df_data.name) : null
207
private async bool get_recent_by_mimetype (string[] mimetypes)
209
var ptr_array = new PtrArray ();
210
var event = new Event ();
211
foreach (unowned string mimetype in mimetypes)
213
var subject = new Subject ();
214
subject.set_mimetype (mimetype);
215
event.add_subject (subject);
217
ptr_array.add (event);
219
var events = yield zg_log.find_events (new TimeRange.to_now (),
221
StorageState.ANY, 16,
222
ResultType.MOST_RECENT_SUBJECTS,
225
int results_pushed = 0;
226
for (uint i=0; i<events.len; i++)
228
unowned Event e = (Event) events.index (i);
229
if (e.num_subjects () > 0)
232
Subject s = e.get_subject (0);
233
if (results_pushed < 3 && push_result (e, s)) results_pushed++;
237
return events.len > 0;
240
private async bool get_events_for_actor (string? actor)
242
var ptr_array = new PtrArray ();
243
var event = new Event ();
244
if (actor != null) event.set_actor ("application://" + actor);
245
ptr_array.add (event);
246
var events = yield zg_log.find_events (new TimeRange.to_now (),
248
StorageState.ANY, 16,
249
ResultType.MOST_POPULAR_SUBJECTS,
252
int results_pushed = 0;
253
for (uint i=0; i<events.len; i++)
255
unowned Event e = (Event) events.index (i);
256
if (e.num_subjects () > 0)
259
Subject s = e.get_subject (0);
260
if (results_pushed < 4 && push_result (e, s)) results_pushed++;
264
return events.len > 0;
267
private async void build_dialog (string? desktop_file)
269
if (this.vbox != null) this.vbox.destroy ();
270
this.vbox = new Gtk.VBox (false, 3);
271
this.dialog.add (this.vbox);
276
current_desktop_file_path = desktop_file;
277
throbber.active = true;
279
if (desktop_file != null)
281
// get items by mimetype
282
unowned DesktopFileInfo? df_data;
283
df_data = desktop_file_info.lookup (desktop_file);
284
if (df_data != null && df_data.mimetypes.length > 0)
286
found1 = yield get_recent_by_mimetype (df_data.mimetypes);
291
found1 = yield get_recent_by_mimetype ({});
295
if (found1) vbox.add (new Gtk.HSeparator ());
298
unowned string actor = null;
299
if (desktop_file != null) actor = desktop_file.rchr (-1, '/').offset (1);
300
found2 = yield get_events_for_actor (actor);
302
if (desktop_file != null && !found1 && !found2)
308
throbber.active = false;
310
if (!found1 && !found2)
312
var l = new Gtk.Label ("There are no items to display...");
315
this.dialog.show_all ();
318
private void on_clicked ()
320
var dialog_flags = dialog.get_flags () & Gtk.WidgetFlags.VISIBLE;
321
if (dialog_flags != 0)
326
dialog.hide_on_unfocus = true;
328
Wnck.Window? active = wnck_screen.get_active_window ();
330
string? desktop_file = active.get_data ("desktop-file-path");
331
build_dialog (desktop_file);
334
private bool push_result (Zeitgeist.Event event, Zeitgeist.Subject subject)
336
var f = File.new_for_uri (subject.get_uri ());
337
if (f.is_native () && !f.query_exists (null)) return false;
339
string? text = subject.get_text ();
340
if (text == null) text = f.get_basename ();
345
var fi = f.query_info (FILE_ATTRIBUTE_STANDARD_ICON, 0, null);
346
icon = fi.get_icon ();
350
icon = g_content_type_get_icon (subject.get_mimetype ());
353
var button = new Gtk.Button ();
354
var hbox = new Gtk.HBox (false, 6);
355
var image = new Gtk.Image.from_gicon (icon, Gtk.IconSize.BUTTON);
356
var label = new Gtk.Label (text);
357
label.set_ellipsize (Pango.EllipsizeMode.MIDDLE);
358
label.set_max_width_chars (35);
360
hbox.pack_start (image, false, true, 0);
361
hbox.pack_start (label, true, true, 0);
362
button.set_relief (Gtk.ReliefStyle.NONE);
363
button.set_focus_on_click (false);
365
button.set_tooltip_text (f.is_native () ? f.get_path () : f.get_uri ());
366
var desktop_file = this.current_desktop_file_path;
367
button.clicked.connect (() => {
368
var context = new AppLaunchContext ();
369
if (desktop_file == null)
371
AppInfo.launch_default_for_uri (f.get_uri (), context);
375
var app_info = new DesktopAppInfo.from_filename (desktop_file);
376
if (app_info.supports_uris ())
378
var l = new List<string> ();
379
l.append (f.get_uri ());
380
app_info.launch_uris (l, context);
384
var l = new List<File> ();
386
app_info.launch (l, context);
392
this.vbox.add (button);
399
awn_applet_factory_initp (string canonical_name, string uid, int panel_id)
401
return new RelatedApplet (canonical_name, uid, panel_id);