~ubuntu-branches/ubuntu/maverick/awn-extras-applets/maverick

« back to all changes in this revision

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

  • Committer: Bazaar Package Importer
  • Author(s): Julien Lavergne
  • Date: 2010-08-29 14:29:52 UTC
  • mto: This revision was merged to the branch mainline in revision 21.
  • Revision ID: james.westby@ubuntu.com-20100829142952-rhvuetyms9bv5uu7
Tags: upstream-0.4.0+bzr1372
ImportĀ upstreamĀ versionĀ 0.4.0+bzr1372

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 helper_event_list = new List<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 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);
 
194
      }
 
195
    }
 
196
 
 
197
    // anything for these?
 
198
    var events = yield zg_log.find_event_ids (new TimeRange.to_now (),
 
199
                                              (owned) ptr_array,
 
200
                                              StorageState.ANY, 1,
 
201
                                              ResultType.MOST_RECENT_EVENTS,
 
202
                                              null);
 
203
 
 
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
 
207
    );
 
208
  }
 
209
 
 
210
  private async bool get_recent_by_mimetype (string[] mimetypes)
 
211
  {
 
212
    var helper_event_list = new List<Event> ();
 
213
    var ptr_array = new PtrArray ();
 
214
    foreach (unowned string mimetype in mimetypes)
 
215
    {
 
216
      var event = new Event ();
 
217
      var subject = new Subject ();
 
218
      subject.set_mimetype (mimetype);
 
219
      event.add_subject (subject);
 
220
 
 
221
      // helper with references
 
222
      helper_event_list.prepend (event);
 
223
      ptr_array.add (event);
 
224
    }
 
225
 
 
226
    var events = yield zg_log.find_events (new TimeRange.to_now (),
 
227
                                           (owned) ptr_array,
 
228
                                           StorageState.ANY, 16,
 
229
                                           ResultType.MOST_RECENT_SUBJECTS,
 
230
                                           null);
 
231
 
 
232
    int results_pushed = 0;
 
233
    foreach (unowned Event e in events)
 
234
    {
 
235
      if (e.num_subjects () > 0)
 
236
      {
 
237
        // process results
 
238
        Subject s = e.get_subject (0);
 
239
        if (results_pushed < 3 && push_result (e, s)) results_pushed++;
 
240
      }
 
241
    }
 
242
 
 
243
    return events.size () > 0;
 
244
  }
 
245
 
 
246
  private async bool get_events_for_actor (string? actor)
 
247
  {
 
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 (),
 
253
                                           (owned) ptr_array,
 
254
                                           StorageState.ANY, 16,
 
255
                                           ResultType.MOST_POPULAR_SUBJECTS,
 
256
                                           null);
 
257
 
 
258
    int results_pushed = 0;
 
259
    foreach (unowned Event e in events)
 
260
    {
 
261
      if (e.num_subjects () > 0)
 
262
      {
 
263
        // process results
 
264
        Subject s = e.get_subject (0);
 
265
        if (results_pushed < 4 && push_result (e, s)) results_pushed++;
 
266
      }
 
267
    }
 
268
 
 
269
    return events.size () > 0;
 
270
  }
 
271
 
 
272
  private async void build_dialog (string? desktop_file)
 
273
  {
 
274
    if (this.vbox != null) this.vbox.destroy ();
 
275
    this.vbox = new Gtk.VBox (false, 3);
 
276
    this.dialog.add (this.vbox);
 
277
 
 
278
    bool found1 = false;
 
279
    bool found2 = false;
 
280
 
 
281
    current_desktop_file_path = desktop_file;
 
282
    throbber.active = true;
 
283
 
 
284
    if (desktop_file != null)
 
285
    {
 
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)
 
290
      {
 
291
        found1 = yield get_recent_by_mimetype (df_data.mimetypes);
 
292
      }
 
293
    }
 
294
    else
 
295
    {
 
296
      found1 = yield get_recent_by_mimetype ({});
 
297
    }
 
298
 
 
299
    // separator
 
300
    if (found1) vbox.add (new Gtk.HSeparator ());
 
301
 
 
302
    // get items by app
 
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);
 
306
 
 
307
    if (desktop_file != null && !found1 && !found2)
 
308
    {
 
309
      build_dialog (null);
 
310
      return;
 
311
    }
 
312
 
 
313
    throbber.active = false;
 
314
 
 
315
    if (!found1 && !found2)
 
316
    {
 
317
      var l = new Gtk.Label ("There are no items to display...");
 
318
      vbox.add (l);
 
319
    }
 
320
    this.dialog.show_all ();
 
321
  }
 
322
 
 
323
  private void on_clicked ()
 
324
  {
 
325
    var dialog_flags = dialog.get_flags () & Gtk.WidgetFlags.VISIBLE;
 
326
    if (dialog_flags != 0)
 
327
    {
 
328
      dialog.hide ();
 
329
      return;
 
330
    }
 
331
    dialog.hide_on_unfocus = true;
 
332
 
 
333
    Wnck.Window? active = wnck_screen.get_active_window ();
 
334
 
 
335
    string? desktop_file = active.get_data ("desktop-file-path");
 
336
    build_dialog (desktop_file);
 
337
  }
 
338
 
 
339
  private bool push_result (Zeitgeist.Event event, Zeitgeist.Subject subject)
 
340
  {
 
341
    var f = File.new_for_uri (subject.get_uri ());
 
342
    if (f.is_native () && !f.query_exists (null)) return false;
 
343
 
 
344
    string? text = subject.get_text ();
 
345
    if (text == null) text = f.get_basename ();
 
346
 
 
347
    GLib.Icon icon;
 
348
    if (f.is_native ())
 
349
    {
 
350
      var fi = f.query_info (FILE_ATTRIBUTE_STANDARD_ICON, 0, null);
 
351
      icon = fi.get_icon ();
 
352
    }
 
353
    else
 
354
    {
 
355
      icon = g_content_type_get_icon (subject.get_mimetype ());
 
356
    }
 
357
 
 
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);
 
364
    label.xalign = 0.0f;
 
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);
 
369
    button.add (hbox);
 
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)
 
375
      {
 
376
        AppInfo.launch_default_for_uri (f.get_uri (), context);
 
377
      }
 
378
      else
 
379
      {
 
380
        var app_info = new DesktopAppInfo.from_filename (desktop_file);
 
381
        if (app_info.supports_uris ())
 
382
        {
 
383
          var l = new List<string> ();
 
384
          l.append (f.get_uri ());
 
385
          app_info.launch_uris (l, context);
 
386
        }
 
387
        else
 
388
        {
 
389
          var l = new List<File> ();
 
390
          l.append (f);
 
391
          app_info.launch (l, context);
 
392
        }
 
393
      }
 
394
      this.dialog.hide ();
 
395
    });
 
396
 
 
397
    this.vbox.add (button);
 
398
 
 
399
    return true;
 
400
  }
 
401
}
 
402
 
 
403
public Applet?
 
404
awn_applet_factory_initp (string canonical_name, string uid, int panel_id)
 
405
{
 
406
  return new RelatedApplet (canonical_name, uid, panel_id);
 
407
}
 
408