~mhr3/awn-extras/zeitgeist-applet

« back to all changes in this revision

Viewing changes to applets/maintained/related/related-applet.vala

  • Committer: Michal Hruby
  • Date: 2010-05-07 07:33:16 UTC
  • Revision ID: michal.mhr@gmail.com-20100507073316-hdwv91bgjycao9b1
Adding also the main vala file is probably a good idea

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
/*
 
2
 * Copyright (C) 2010 Michal Hruby <michal.mhr@gmail.com>
 
3
 *
 
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.
 
8
 *
 
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.
 
13
 *
 
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.
 
17
 *
 
18
 * Author : Michal Hruby <michal.mhr@gmail.com>
 
19
 */
 
20
 
 
21
using Awn;
 
22
using DesktopAgnostic;
 
23
using Zeitgeist;
 
24
 
 
25
[DBus (name = "org.wncksync.Matcher")]
 
26
interface WnckSyncMatcher : GLib.Object
 
27
{
 
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;
 
32
}
 
33
 
 
34
struct DesktopFileInfo
 
35
{
 
36
  string name;
 
37
  string[] mimetypes;
 
38
}
 
39
 
 
40
class RelatedApplet : AppletSimple
 
41
{
 
42
  private unowned Wnck.Screen wnck_screen;
 
43
 
 
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;
 
49
 
 
50
  private Awn.Dialog dialog;
 
51
  private Gtk.VBox vbox;
 
52
  private string? current_desktop_file_path;
 
53
 
 
54
  public RelatedApplet (string canonical_name, string uid, int panel_id)
 
55
  {
 
56
    Object (canonical_name: canonical_name, uid: uid, panel_id: panel_id);
 
57
 
 
58
    desktop_file_info = new HashTable<string, DesktopFileInfo?> (
 
59
      str_hash, str_equal
 
60
    );
 
61
 
 
62
    // setup wnck stuff
 
63
    wnck_screen = Wnck.Screen.get_default ();
 
64
    Wnck.set_client_type (Wnck.ClientType.PAGER);
 
65
 
 
66
    wnck_screen.window_opened.connect (this.window_opened);
 
67
    wnck_screen.active_window_changed.connect (this.window_changed);
 
68
 
 
69
    // overlay setup
 
70
    this.add_overlay (throbber);
 
71
    this.add_overlay (star_overlay);
 
72
 
 
73
    this.set_icon_name ("zeitgeist-logo");
 
74
    this.clicked.connect (this.on_clicked);
 
75
 
 
76
    // dialog setup
 
77
    this.dialog = new Awn.Dialog.for_widget (this);
 
78
  }
 
79
 
 
80
  construct
 
81
  {
 
82
    star_overlay.active = false;
 
83
    star_overlay.gravity = Gdk.Gravity.SOUTH_EAST;
 
84
    (star_overlay as OverlayThemedIcon).scale = 0.3;
 
85
 
 
86
    throbber.gravity = Gdk.Gravity.SOUTH_WEST;
 
87
    (throbber as OverlayThrobber).scale = 0.3;
 
88
  }
 
89
 
 
90
  private void window_opened (Wnck.Window window)
 
91
  {
 
92
    string? desktop_file = lookup.search_by_wnck_window (window);
 
93
    if (desktop_file == null)
 
94
    {
 
95
      // try wncksync
 
96
      try
 
97
      {
 
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");
 
103
      
 
104
        string df = matcher.desktop_file_for_xid ((uint32) window.get_xid ());
 
105
        if (df != null && df != "")
 
106
        {
 
107
          debug ("wncksync found match: %s", df);
 
108
          desktop_file = df;
 
109
        }
 
110
      }
 
111
      catch (GLib.Error err)
 
112
      {
 
113
        // ignore that we don't have wncksync
 
114
      }
 
115
    }
 
116
 
 
117
    // get mimetypes this desktop file supports
 
118
    if (desktop_file != null)
 
119
    {
 
120
      unowned DesktopFileInfo? df_data;
 
121
      df_data = desktop_file_info.lookup (desktop_file);
 
122
      if (df_data == null)
 
123
      {
 
124
        var keyfile = new KeyFile ();
 
125
        string[] mimetypes = {};
 
126
        string app_name = "";
 
127
        try
 
128
        {
 
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");
 
132
        }
 
133
        catch (GLib.Error err)
 
134
        {
 
135
        }
 
136
        var w = DesktopFileInfo ();
 
137
        w.name = (owned) app_name;
 
138
        w.mimetypes = (owned) mimetypes;
 
139
        desktop_file_info.insert(desktop_file, w);
 
140
      }
 
141
    }
 
142
    window.set_data ("desktop-file-path", desktop_file);
 
143
  }
 
144
 
 
145
  private void window_changed (Wnck.Window? old_window)
 
146
  {
 
147
    Wnck.Window? active = wnck_screen.get_active_window ();
 
148
    if (active != null)
 
149
    {
 
150
      /*
 
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)
 
154
      {
 
155
        return;
 
156
      }
 
157
      */
 
158
 
 
159
      string? desktop_file = active.get_data ("desktop-file-path");
 
160
      if (desktop_file != null)
 
161
      {
 
162
        update_star (desktop_file);
 
163
      }
 
164
      else
 
165
      {
 
166
        star_overlay.active = false;
 
167
      }
 
168
    }
 
169
  }
 
170
 
 
171
  private async void update_star (string desktop_file)
 
172
  {
 
173
    var ptr_array = new PtrArray ();
 
174
    unowned string actor = desktop_file.rchr (-1, '/').offset (1);
 
175
 
 
176
    var event = new Event ();
 
177
    var event2 = new Event ();
 
178
    event.set_actor ("application://" + actor);
 
179
    ptr_array.add (event);
 
180
 
 
181
    unowned DesktopFileInfo? df_data;
 
182
    df_data = desktop_file_info.lookup (desktop_file);
 
183
    if (df_data != null && df_data.mimetypes.length > 0)
 
184
    {
 
185
      foreach (unowned string mimetype in df_data.mimetypes)
 
186
      {
 
187
        var subject = new Subject ();
 
188
        subject.set_mimetype (mimetype);
 
189
        event2.add_subject (subject);
 
190
      }
 
191
      ptr_array.add (event2);
 
192
    }
 
193
 
 
194
    // anything for these?
 
195
    var events = yield zg_log.find_event_ids (new TimeRange.to_now (),
 
196
                                              (owned) ptr_array,
 
197
                                              StorageState.ANY, 1,
 
198
                                              ResultType.MOST_POPULAR_SUBJECTS,
 
199
                                              null);
 
200
 
 
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
 
204
    );
 
205
  }
 
206
 
 
207
  private async bool get_recent_by_mimetype (string[] mimetypes)
 
208
  {
 
209
    var ptr_array = new PtrArray ();
 
210
    var event = new Event ();
 
211
    foreach (unowned string mimetype in mimetypes)
 
212
    {
 
213
      var subject = new Subject ();
 
214
      subject.set_mimetype (mimetype);
 
215
      event.add_subject (subject);
 
216
    }
 
217
    ptr_array.add (event);
 
218
 
 
219
    var events = yield zg_log.find_events (new TimeRange.to_now (),
 
220
                                           (owned) ptr_array,
 
221
                                           StorageState.ANY, 16,
 
222
                                           ResultType.MOST_RECENT_SUBJECTS,
 
223
                                           null);
 
224
 
 
225
    int results_pushed = 0;
 
226
    for (uint i=0; i<events.len; i++)
 
227
    {
 
228
      unowned Event e = (Event) events.index (i);
 
229
      if (e.num_subjects () > 0)
 
230
      {
 
231
        // process results
 
232
        Subject s = e.get_subject (0);
 
233
        if (results_pushed < 3 && push_result (e, s)) results_pushed++;
 
234
      }
 
235
    }
 
236
 
 
237
    return events.len > 0;
 
238
  }
 
239
 
 
240
  private async bool get_events_for_actor (string? actor)
 
241
  {
 
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 (),
 
247
                                           (owned) ptr_array,
 
248
                                           StorageState.ANY, 16,
 
249
                                           ResultType.MOST_POPULAR_SUBJECTS,
 
250
                                           null);
 
251
 
 
252
    int results_pushed = 0;
 
253
    for (uint i=0; i<events.len; i++)
 
254
    {
 
255
      unowned Event e = (Event) events.index (i);
 
256
      if (e.num_subjects () > 0)
 
257
      {
 
258
        // process results
 
259
        Subject s = e.get_subject (0);
 
260
        if (results_pushed < 4 && push_result (e, s)) results_pushed++;
 
261
      }
 
262
    }
 
263
 
 
264
    return events.len > 0;
 
265
  }
 
266
 
 
267
  private async void build_dialog (string? desktop_file)
 
268
  {
 
269
    if (this.vbox != null) this.vbox.destroy ();
 
270
    this.vbox = new Gtk.VBox (false, 3);
 
271
    this.dialog.add (this.vbox);
 
272
 
 
273
    bool found1 = false;
 
274
    bool found2 = false;
 
275
 
 
276
    current_desktop_file_path = desktop_file;
 
277
    throbber.active = true;
 
278
 
 
279
    if (desktop_file != null)
 
280
    {
 
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)
 
285
      {
 
286
        found1 = yield get_recent_by_mimetype (df_data.mimetypes);
 
287
      }
 
288
    }
 
289
    else
 
290
    {
 
291
      found1 = yield get_recent_by_mimetype ({});
 
292
    }
 
293
 
 
294
    // separator
 
295
    if (found1) vbox.add (new Gtk.HSeparator ());
 
296
 
 
297
    // get items by app
 
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);
 
301
 
 
302
    if (desktop_file != null && !found1 && !found2)
 
303
    {
 
304
      build_dialog (null);
 
305
      return;
 
306
    }
 
307
 
 
308
    throbber.active = false;
 
309
 
 
310
    if (!found1 && !found2)
 
311
    {
 
312
      var l = new Gtk.Label ("There are no items to display...");
 
313
      vbox.add (l);
 
314
    }
 
315
    this.dialog.show_all ();
 
316
  }
 
317
 
 
318
  private void on_clicked ()
 
319
  {
 
320
    var dialog_flags = dialog.get_flags () & Gtk.WidgetFlags.VISIBLE;
 
321
    if (dialog_flags != 0)
 
322
    {
 
323
      dialog.hide ();
 
324
      return;
 
325
    }
 
326
    dialog.hide_on_unfocus = true;
 
327
 
 
328
    Wnck.Window? active = wnck_screen.get_active_window ();
 
329
 
 
330
    string? desktop_file = active.get_data ("desktop-file-path");
 
331
    build_dialog (desktop_file);
 
332
  }
 
333
 
 
334
  private bool push_result (Zeitgeist.Event event, Zeitgeist.Subject subject)
 
335
  {
 
336
    var f = File.new_for_uri (subject.get_uri ());
 
337
    if (f.is_native () && !f.query_exists (null)) return false;
 
338
 
 
339
    string? text = subject.get_text ();
 
340
    if (text == null) text = f.get_basename ();
 
341
 
 
342
    GLib.Icon icon;
 
343
    if (f.is_native ())
 
344
    {
 
345
      var fi = f.query_info (FILE_ATTRIBUTE_STANDARD_ICON, 0, null);
 
346
      icon = fi.get_icon ();
 
347
    }
 
348
    else
 
349
    {
 
350
      icon = g_content_type_get_icon (subject.get_mimetype ());
 
351
    }
 
352
 
 
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);
 
359
    label.xalign = 0.0f;
 
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);
 
364
    button.add (hbox);
 
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)
 
370
      {
 
371
        AppInfo.launch_default_for_uri (f.get_uri (), context);
 
372
      }
 
373
      else
 
374
      {
 
375
        var app_info = new DesktopAppInfo.from_filename (desktop_file);
 
376
        if (app_info.supports_uris ())
 
377
        {
 
378
          var l = new List<string> ();
 
379
          l.append (f.get_uri ());
 
380
          app_info.launch_uris (l, context);
 
381
        }
 
382
        else
 
383
        {
 
384
          var l = new List<File> ();
 
385
          l.append (f);
 
386
          app_info.launch (l, context);
 
387
        }
 
388
      }
 
389
      this.dialog.hide ();
 
390
    });
 
391
 
 
392
    this.vbox.add (button);
 
393
 
 
394
    return true;
 
395
  }
 
396
}
 
397
 
 
398
public Applet?
 
399
awn_applet_factory_initp (string canonical_name, string uid, int panel_id)
 
400
{
 
401
  return new RelatedApplet (canonical_name, uid, panel_id);
 
402
}
 
403