~gnome-zeitgeist/gnome-activity-journal/minimal

« back to all changes in this revision

Viewing changes to src/widgets.py

  • Committer: tehk
  • Date: 2010-05-04 05:27:03 UTC
  • mfrom: (843.1.42 journal-rewrite)
  • Revision ID: tehk@tehk-desktop-20100504052703-pqp9o4yuewaoc9lv
new-core branch was merge

Show diffs side-by-side

added added

removed removed

Lines of Context:
1
 
# -.- coding: utf-8 -.-
2
 
#
3
 
# GNOME Activity Journal
4
 
#
5
 
# Copyright © 2009-2010 Seif Lotfy <seif@lotfy.com>
6
 
# Copyright © 2010 Siegfried Gevatter <siegfried@gevatter.com>
7
 
# Copyright © 2010 Markus Korn <thekorn@gmx.de>
8
 
# Copyright © 2010 Randal Barlow <email.tehk@gmail.com>
9
 
#
10
 
# This program is free software: you can redistribute it and/or modify
11
 
# it under the terms of the GNU General Public License as published by
12
 
# the Free Software Foundation, either version 3 of the License, or
13
 
# (at your option) any later version.
14
 
#
15
 
# This program is distributed in the hope that it will be useful,
16
 
# but WITHOUT ANY WARRANTY; without even the implied warranty of
17
 
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
18
 
# GNU General Public License for more details.
19
 
#
20
 
# You should have received a copy of the GNU General Public License
21
 
# along with this program.  If not, see <http://www.gnu.org/licenses/>.
22
 
 
23
 
from __future__ import with_statement
24
 
import os
25
 
import gtk
26
 
import gettext
27
 
import datetime
28
 
import time
29
 
import gobject
30
 
import pango
31
 
import gio
32
 
from dbus.exceptions import DBusException
33
 
try:
34
 
    import gst
35
 
except ImportError:
36
 
    gst = None
37
 
 
38
 
from zeitgeist.client import ZeitgeistClient
39
 
from zeitgeist.datamodel import Event, Subject, Interpretation, Manifestation, \
40
 
    ResultType
41
 
 
42
 
from common import shade_gdk_color, combine_gdk_color, is_command_available, \
43
 
    launch_command
44
 
from config import BASE_PATH, VERSION, settings, get_icon_path
45
 
from sources import Source, SUPPORTED_SOURCES
46
 
from gio_file import GioFile, SIZE_NORMAL, SIZE_LARGE
47
 
from bookmarker import bookmarker
48
 
try:
49
 
    from tracker_wrapper import tracker
50
 
except DBusException:
51
 
    print "Tracker disabled."
52
 
 
53
 
import content_objects
54
 
 
55
 
 
56
 
CLIENT = ZeitgeistClient()
57
 
ITEMS = []
58
 
 
59
 
class SearchBox(gtk.EventBox):
60
 
 
61
 
    __gsignals__ = {
62
 
        "clear" : (gobject.SIGNAL_RUN_FIRST,
63
 
                   gobject.TYPE_NONE,
64
 
                   ()),
65
 
        "search" : (gobject.SIGNAL_RUN_FIRST,
66
 
                    gobject.TYPE_NONE,
67
 
                    (gobject.TYPE_PYOBJECT,))
68
 
    }
69
 
 
70
 
    def __init__(self):
71
 
        gtk.EventBox.__init__(self)
72
 
 
73
 
        self.text = ""
74
 
 
75
 
        self.set_border_width(3)
76
 
        self.hbox = gtk.HBox()
77
 
        self.add(self.hbox)
78
 
 
79
 
        self.results = []
80
 
 
81
 
        self.search = SearchEntry()
82
 
 
83
 
        self.hbox.pack_start(self.search)
84
 
        self.hbox.set_border_width(6)
85
 
 
86
 
        self.category = {}
87
 
 
88
 
        for source in SUPPORTED_SOURCES.keys():
89
 
            s = SUPPORTED_SOURCES[source]._desc_pl
90
 
            self.category[s] = source
91
 
 
92
 
        self._init_combobox()
93
 
        self.show_all()
94
 
 
95
 
        def change_style(widget, style):
96
 
 
97
 
            rc_style = self.style
98
 
            color = rc_style.bg[gtk.STATE_NORMAL]
99
 
            color = shade_gdk_color(color, 102/100.0)
100
 
            self.modify_bg(gtk.STATE_NORMAL, color)
101
 
 
102
 
            color = rc_style.bg[gtk.STATE_NORMAL]
103
 
            fcolor = rc_style.fg[gtk.STATE_NORMAL]
104
 
            color = combine_gdk_color(color, fcolor)
105
 
 
106
 
            self.search.modify_text(gtk.STATE_NORMAL, color)
107
 
 
108
 
        self.hbox.connect("style-set", change_style)
109
 
        self.search.connect("search", self.set_search)
110
 
        self.search.connect("clear", self.clear)
111
 
 
112
 
    def clear(self, widget):
113
 
        if self.text.strip() != "" and self.text.strip() != self.search.default_text:
114
 
            self.text = ""
115
 
            self.results = []
116
 
            self.emit("clear")
117
 
 
118
 
    def _init_combobox(self):
119
 
 
120
 
        self.clearbtn = gtk.Button()
121
 
        #label = gtk.Label()
122
 
        #label.set_markup("<span><b>X</b></span>")
123
 
 
124
 
        img = gtk.image_new_from_stock("gtk-close", gtk.ICON_SIZE_MENU)
125
 
        self.clearbtn.add(img)
126
 
        self.clearbtn.set_focus_on_click(False)
127
 
        self.clearbtn.set_relief(gtk.RELIEF_NONE)
128
 
        self.hbox.pack_end(self.clearbtn, False, False)
129
 
        self.clearbtn.connect("clicked", lambda button: self.hide())
130
 
        self.clearbtn.connect("clicked", lambda button: self.search.set_text(""))
131
 
 
132
 
        self.combobox = gtk.combo_box_new_text()
133
 
        self.combobox.set_focus_on_click(False)
134
 
        self.hbox.pack_end(self.combobox, False, False, 6)
135
 
        self.combobox.append_text("All activities")
136
 
        self.combobox.set_active(0)
137
 
        for cat in self.category.keys():
138
 
            self.combobox.append_text(cat)
139
 
 
140
 
    def set_search(self, widget, text=None):
141
 
        if not self.text.strip() == text.strip():
142
 
            self.text = text
143
 
            def callback(results):
144
 
                self.results = [s[1] for s in results]
145
 
                self.emit("search", results)
146
 
 
147
 
            if not text:
148
 
                text = self.search.get_text()
149
 
            if text == self.search.default_text or text.strip() == "":
150
 
                pass
151
 
            else:
152
 
                cat = self.combobox.get_active()
153
 
                if cat == 0:
154
 
                    interpretation = None
155
 
                else:
156
 
                    cat = self.category[self.combobox.get_active_text()]
157
 
                    interpretation = self.category[self.combobox.get_active_text()]
158
 
            if "tracker" in globals().keys():
159
 
                tracker.search(text, interpretation, callback)
160
 
 
161
 
class SearchEntry(gtk.Entry):
162
 
 
163
 
    __gsignals__ = {
164
 
        "clear" : (gobject.SIGNAL_RUN_FIRST,
165
 
                   gobject.TYPE_NONE,
166
 
                   ()),
167
 
        "search" : (gobject.SIGNAL_RUN_FIRST,
168
 
                    gobject.TYPE_NONE,
169
 
                    (gobject.TYPE_STRING,))
170
 
    }
171
 
 
172
 
    default_text = _("Type here to search...")
173
 
 
174
 
    # The font style of the text in the entry.
175
 
    #font_style = None
176
 
 
177
 
    # TODO: What is this?
178
 
    search_timeout = 0
179
 
 
180
 
    def __init__(self, accel_group = None):
181
 
        gtk.Entry.__init__(self)
182
 
 
183
 
        self.set_width_chars(30)
184
 
        self.set_text(self.default_text)
185
 
        self.set_size_request(-1, 32)
186
 
        self.connect("changed", lambda w: self._queue_search())
187
 
        self.connect("focus-in-event", self._entry_focus_in)
188
 
        self.connect("focus-out-event", self._entry_focus_out)
189
 
        #self.connect("icon-press", self._icon_press)
190
 
        self.show_all()
191
 
 
192
 
    def _icon_press(self, widget, pos, event):
193
 
        # Note: GTK_ENTRY_ICON_SECONDARY does not seem to be bound in PyGTK.
194
 
        if int(pos) == 1 and not self.get_text() == self.default_text:
195
 
            self._entry_clear_no_change_handler()
196
 
 
197
 
    def _entry_focus_in(self, widget, x):
198
 
        if self.get_text() == self.default_text:
199
 
            self.set_text("")
200
 
            #self.modify_font(self.font_style)
201
 
 
202
 
    def _entry_focus_out(self, widget, x):
203
 
        if self.get_text() == "":
204
 
            self.set_text(self.default_text)
205
 
            #self.modify_font(self.font_style)
206
 
 
207
 
    def _entry_clear_no_change_handler(self):
208
 
        if not self.get_text() == self.default_text:
209
 
            self.set_text("")
210
 
 
211
 
    def _queue_search(self):
212
 
        if self.search_timeout != 0:
213
 
            gobject.source_remove(self.search_timeout)
214
 
            self.search_timeout = 0
215
 
 
216
 
        if self.get_text() == self.default_text or len(self.get_text()) == 0:
217
 
            self.emit("clear")
218
 
        else:
219
 
            self.search_timeout = gobject.timeout_add(200, self._typing_timeout)
220
 
 
221
 
    def _typing_timeout(self):
222
 
        if len(self.get_text()) > 0:
223
 
            self.emit("search", self.get_text())
224
 
 
225
 
        self.search_timeout = 0
226
 
        return False
227
 
 
228
 
 
229
 
class PreviewTooltip(gtk.Window):
230
 
 
231
 
    # per default we are using thumbs at a size of 128 * 128 px
232
 
    # in tooltips. For preview of text files we are using 256 * 256 px
233
 
    # which is dynamically defined in StaticPreviewTooltip.preview()
234
 
    TOOLTIP_SIZE = SIZE_NORMAL
235
 
 
236
 
    def __init__(self):
237
 
        gtk.Window.__init__(self, type=gtk.WINDOW_POPUP)
238
 
 
239
 
    def preview(self, gio_file):
240
 
        return False
241
 
 
242
 
class StaticPreviewTooltip(PreviewTooltip):
243
 
 
244
 
    def __init__(self):
245
 
        super(StaticPreviewTooltip, self).__init__()
246
 
        self.__current = None
247
 
        self.__monitor = None
248
 
 
249
 
    def replace_content(self, content):
250
 
        children = self.get_children()
251
 
        if children:
252
 
            self.remove(children[0])
253
 
            # hack to force the tooltip to have the exact same size
254
 
            # as the child image
255
 
            self.resize(1,1)
256
 
        self.add(content)
257
 
 
258
 
    def preview(self, gio_file):
259
 
        if gio_file == self.__current:
260
 
            return bool(self.__current)
261
 
        if self.__monitor is not None:
262
 
            self.__monitor.cancel()
263
 
        self.__current = gio_file
264
 
        self.__monitor = gio_file.get_monitor()
265
 
        self.__monitor.connect("changed", self._do_update_preview)
266
 
        # for text previews we are always using SIZE_LARGE
267
 
        if "text-x-generic" in gio_file.icon_names or "text-x-script" in gio_file.icon_names:
268
 
            size = SIZE_LARGE
269
 
        else:
270
 
            size = self.TOOLTIP_SIZE
271
 
        if not isinstance(gio_file, GioFile): return False
272
 
        pixbuf = gio_file.get_thumbnail(size=size, border=1)
273
 
        if pixbuf is None:
274
 
            self.__current = None
275
 
            return False
276
 
        img = gtk.image_new_from_pixbuf(pixbuf)
277
 
        img.set_alignment(0.5, 0.5)
278
 
        img.show_all()
279
 
        self.replace_content(img)
280
 
        del pixbuf, size
281
 
        return True
282
 
 
283
 
    def _do_update_preview(self, monitor, file, other_file, event_type):
284
 
        if event_type == gio.FILE_MONITOR_EVENT_CHANGES_DONE_HINT:
285
 
            if self.__current is not None:
286
 
                self.__current.refresh()
287
 
            self.__current = None
288
 
            gtk.tooltip_trigger_tooltip_query(gtk.gdk.display_get_default())
289
 
 
290
 
class VideoPreviewTooltip(PreviewTooltip):
291
 
 
292
 
    def __init__(self):
293
 
        PreviewTooltip.__init__(self)
294
 
        hbox = gtk.HBox()
295
 
        self.movie_window = gtk.DrawingArea()
296
 
        hbox.pack_start(self.movie_window)
297
 
        self.add(hbox)
298
 
        self.player = gst.element_factory_make("playbin", "player")
299
 
        bus = self.player.get_bus()
300
 
        bus.add_signal_watch()
301
 
        bus.enable_sync_message_emission()
302
 
        bus.connect("message", self.on_message)
303
 
        bus.connect("sync-message::element", self.on_sync_message)
304
 
        self.connect("hide", self._handle_hide)
305
 
        self.connect("show", self._handle_show)
306
 
        self.set_default_size(*self.TOOLTIP_SIZE)
307
 
 
308
 
    def _handle_hide(self, widget):
309
 
        self.player.set_state(gst.STATE_NULL)
310
 
 
311
 
    def _handle_show(self, widget):
312
 
        self.player.set_state(gst.STATE_PLAYING)
313
 
 
314
 
    def preview(self, gio_file):
315
 
        if gio_file.uri == self.player.get_property("uri"):
316
 
            return True
317
 
        self.player.set_property("uri", gio_file.uri)
318
 
        return True
319
 
 
320
 
    def on_message(self, bus, message):
321
 
        t = message.type
322
 
        if t == gst.MESSAGE_EOS:
323
 
            self.player.set_state(gst.STATE_NULL)
324
 
            self.hide_all()
325
 
        elif t == gst.MESSAGE_ERROR:
326
 
            self.player.set_state(gst.STATE_NULL)
327
 
            err, debug = message.parse_error()
328
 
            print "Error: %s" % err, debug
329
 
 
330
 
    def on_sync_message(self, bus, message):
331
 
        if message.structure is None:
332
 
            return
333
 
        message_name = message.structure.get_name()
334
 
        if message_name == "prepare-xwindow-id":
335
 
            imagesink = message.src
336
 
            imagesink.set_property("force-aspect-ratio", True)
337
 
            gtk.gdk.threads_enter()
338
 
            try:
339
 
                self.show_all()
340
 
                imagesink.set_xwindow_id(self.movie_window.window.xid)
341
 
            finally:
342
 
                gtk.gdk.threads_leave()
343
 
 
344
 
class Item(gtk.HBox):
345
 
 
346
 
    def __init__(self, event, allow_pin = False):
347
 
 
348
 
        gtk.HBox.__init__(self)
349
 
        self.set_border_width(2)
350
 
        self.allow_pin = allow_pin
351
 
        self.btn = gtk.Button()
352
 
        self.search_results = []
353
 
        self.in_search = False
354
 
        self.subject = event.subjects[0]
355
 
        self.content_obj = content_objects.choose_content_object(event)
356
 
        # self.content_obj = GioFile.create(self.subject.uri)
357
 
        self.time = float(event.timestamp) / 1000
358
 
        self.time =  time.strftime("%H:%M", time.localtime(self.time))
359
 
 
360
 
        if self.content_obj is not None:
361
 
            self.icon = self.content_obj.get_icon(
362
 
                can_thumb=settings.get('small_thumbnails', False), border=0)
363
 
        else:
364
 
            self.icon = None
365
 
        self.btn.set_relief(gtk.RELIEF_NONE)
366
 
        self.btn.set_focus_on_click(False)
367
 
        self.__init_widget()
368
 
        self.show_all()
369
 
        self.markup = None
370
 
 
371
 
        ITEMS.append(self)
372
 
 
373
 
    def highlight(self):
374
 
        if self.search_results != searchbox.results:
375
 
            self.search_results = searchbox.results
376
 
            rc_style = self.style
377
 
            text = self.content_obj.text.replace("&", "&amp;")
378
 
            if self.subject.uri in searchbox.results:
379
 
                self.label.set_markup("<span><b>"+text+"</b></span>")
380
 
                self.in_search = True
381
 
                color = rc_style.base[gtk.STATE_SELECTED]
382
 
                self.label.modify_fg(gtk.STATE_NORMAL, color)
383
 
            else:
384
 
                self.label.set_markup("<span>"+text+"</span>")
385
 
                self.in_search = False
386
 
                color = rc_style.text[gtk.STATE_NORMAL]
387
 
                self.label.modify_fg(gtk.STATE_NORMAL, color)
388
 
 
389
 
    def __init_widget(self):
390
 
        self.label = gtk.Label()
391
 
        text = self.content_obj.text.replace("&", "&amp;")
392
 
        self.label.set_markup(text)
393
 
        self.label.set_ellipsize(pango.ELLIPSIZE_MIDDLE)
394
 
        self.label.set_alignment(0.0, 0.5)
395
 
 
396
 
        if self.icon: img = gtk.image_new_from_pixbuf(self.icon)
397
 
        else: img = None
398
 
        hbox = gtk.HBox()
399
 
        if img: hbox.pack_start(img, False, False, 1)
400
 
        hbox.pack_start(self.label, True, True, 4)
401
 
 
402
 
        if self.allow_pin:
403
 
            # TODO: get the name "pin" from theme when icons are properly installed
404
 
            img = gtk.image_new_from_file(get_icon_path("hicolor/24x24/status/pin.png"))
405
 
            self.pin = gtk.Button()
406
 
            self.pin.add(img)
407
 
            self.pin.set_tooltip_text(_("Remove Pin"))
408
 
            self.pin.set_focus_on_click(False)
409
 
            self.pin.set_relief(gtk.RELIEF_NONE)
410
 
            self.pack_end(self.pin, False, False)
411
 
            self.pin.connect("clicked", lambda x: self.set_bookmarked(False))
412
 
        #hbox.pack_end(img, False, False)
413
 
        evbox = gtk.EventBox()
414
 
        self.btn.add(hbox)
415
 
        evbox.add(self.btn)
416
 
        self.pack_start(evbox)
417
 
 
418
 
        self.btn.connect("clicked", self.launch)
419
 
        self.btn.connect("button_press_event", self._show_item_popup)
420
 
 
421
 
        def realize_cb(widget):
422
 
            evbox.window.set_cursor(gtk.gdk.Cursor(gtk.gdk.HAND2))
423
 
 
424
 
        self.btn.connect("realize", realize_cb)
425
 
 
426
 
 
427
 
        def change_style(widget, style):
428
 
            rc_style = self.style
429
 
            color = rc_style.bg[gtk.STATE_NORMAL]
430
 
            fcolor = rc_style.fg[gtk.STATE_NORMAL]
431
 
            color = combine_gdk_color(color, fcolor)
432
 
 
433
 
            if self.in_search:
434
 
                color = rc_style.bg[gtk.STATE_SELECTED]
435
 
                self.label.modify_text(gtk.STATE_NORMAL, color)
436
 
            else:
437
 
                color = rc_style.text[gtk.STATE_NORMAL]
438
 
                self.label.modify_text(gtk.STATE_NORMAL, color)
439
 
            self.highlight()
440
 
 
441
 
            color = rc_style.bg[gtk.STATE_NORMAL]
442
 
            color = shade_gdk_color(color, 102/100.0)
443
 
            evbox.modify_bg(gtk.STATE_NORMAL, color)
444
 
 
445
 
        self.connect("style-set", change_style)
446
 
 
447
 
        self.init_multimedia_tooltip()
448
 
 
449
 
    def init_multimedia_tooltip(self):
450
 
        """add multimedia tooltip to multimedia files
451
 
        multimedia tooltip is shown for all images, all videos and pdfs
452
 
 
453
 
        TODO: make loading of multimedia thumbs async
454
 
        """
455
 
        if isinstance(self.content_obj, GioFile) and self.content_obj.has_preview():
456
 
            icon_names = self.content_obj.icon_names
457
 
            self.set_property("has-tooltip", True)
458
 
            self.connect("query-tooltip", self._handle_tooltip)
459
 
            if "video-x-generic" in icon_names and gst is not None:
460
 
                self.set_tooltip_window(VideoPreviewTooltip)
461
 
            else:
462
 
                self.set_tooltip_window(StaticPreviewTooltip)
463
 
 
464
 
    def _handle_tooltip(self, widget, x, y, keyboard_mode, tooltip):
465
 
        # nothing to do here, we always show the multimedia tooltip
466
 
        # if we like video/sound preview later on we can start them here
467
 
        tooltip_window = self.get_tooltip_window()
468
 
        return tooltip_window.preview(self.content_obj)
469
 
 
470
 
    def _show_item_popup(self, widget, ev):
471
 
        if ev.button == 3:
472
 
            items = [self.content_obj]
473
 
            ContextMenu.do_popup(ev.time, items)
474
 
 
475
 
    def set_bookmarked(self, bool_):
476
 
        uri = unicode(self.subject.uri)
477
 
        if bool_:
478
 
            bookmarker.bookmark(uri)
479
 
        else:
480
 
            bookmarker.unbookmark(uri)
481
 
 
482
 
 
483
 
    def launch(self, *discard):
484
 
        if self.content_obj is not None:
485
 
            self.content_obj.launch()
486
 
 
487
 
 
488
 
class AnimatedImage(gtk.Image):
489
 
    animating = None
490
 
    mod = 7
491
 
    i = 0
492
 
    speed = 100
493
 
    def __init__(self, uri, speed = 0):
494
 
        super(AnimatedImage, self).__init__()
495
 
        if speed: self.speed = speed
496
 
        self.frames = []
497
 
        for i in (6, 5, 4, 3, 2, 1, 0):
498
 
            self.frames.append(gtk.gdk.pixbuf_new_from_file_at_size(get_icon_path(uri % i), 16, 16))
499
 
        self.set_from_pixbuf(self.frames[0])
500
 
 
501
 
    def next(self):
502
 
        """
503
 
        Move to next frame
504
 
        """
505
 
        self.set_from_pixbuf(self.frames[self.i % self.mod])
506
 
        self.i += 1
507
 
        return True
508
 
 
509
 
    def start(self):
510
 
        """
511
 
        start the image's animation
512
 
        """
513
 
        if self.animating: gobject.source_remove(self.animating)
514
 
        self.animating = gobject.timeout_add(self.speed, self.next)
515
 
 
516
 
    def stop(self):
517
 
        """
518
 
        stop the image's animation
519
 
        """
520
 
        if self.animating: gobject.source_remove(self.animating)
521
 
        self.animating = None
522
 
        return False
523
 
 
524
 
    def animate_for_seconds(self, seconds):
525
 
        """
526
 
        :param seconds: int seconds for the amount of time when you want
527
 
        animate the throbber
528
 
        """
529
 
        self.start()
530
 
        gobject.timeout_add_seconds(seconds, self.stop)
531
 
 
532
 
 
533
 
class AboutDialog(gtk.AboutDialog):
534
 
    name = "Activity Journal"
535
 
    authors = (
536
 
        "Seif Lotfy <seif@lotfy.com>",
537
 
        "Randal Barlow <email.tehk@gmail.com>",
538
 
        "Siegfried-Angel Gevatter <siegfried@gevatter.com>",
539
 
        "Peter Lund <peterfirefly@gmail.com>",
540
 
        "Hylke Bons <hylkebons@gmail.com>",
541
 
        "Markus Korn <thekorn@gmx.de>",
542
 
        "Mikkel Kamstrup <mikkel.kamstrup@gmail.com>"
543
 
        )
544
 
    artists = (
545
 
               "Hylke Bons <hylkebons@gmail.com>",
546
 
               "Thorsten Prante <thorsten@prante.eu>"
547
 
                )
548
 
    copyright_ = "Copyright © 2009-2010 Activity Journal authors"
549
 
    comment = "A viewport into the past powered by Zeitgeist"
550
 
    version = VERSION
551
 
    def __init__(self):
552
 
        super(AboutDialog, self).__init__()
553
 
        self.set_name(self.name)
554
 
        self.set_version(self.version)
555
 
        self.set_comments(self.comment)
556
 
        self.set_copyright(self.copyright_)
557
 
        self.set_authors(self.authors)
558
 
        self.set_artists(self.artists)
559
 
 
560
 
        license = None
561
 
        for name in ("/usr/share/common-licenses/GPL",
562
 
            os.path.join(BASE_PATH, "COPYING")):
563
 
            if os.path.isfile(name):
564
 
                with open(name) as licensefile:
565
 
                    license = licensefile.read()
566
 
                    break
567
 
        if not license:
568
 
            license = "GNU General Public License, version 3 or later."
569
 
 
570
 
        self.set_license(license)
571
 
        #self.set_logo_icon_name("gnome-activity-journal")
572
 
        self.set_logo(gtk.gdk.pixbuf_new_from_file_at_size(get_icon_path(
573
 
            "hicolor/scalable/apps/gnome-activity-journal.svg"), 48, 48))
574
 
 
575
 
 
576
 
class ContextMenu(gtk.Menu):
577
 
    subjects = []# A list of Zeitgeist event uris
578
 
    informationwindow = None
579
 
    def __init__(self):
580
 
        super(ContextMenu, self).__init__()
581
 
        self.menuitems = {
582
 
            "open" : gtk.ImageMenuItem(gtk.STOCK_OPEN),
583
 
            "unpin" : gtk.MenuItem(_("Remove Pin")),
584
 
            "pin" : gtk.MenuItem(_("Pin to Today")),
585
 
            "delete" : gtk.MenuItem(_("Delete item from Journal")),
586
 
            "delete_uri" : gtk.MenuItem(_("Delete all events with this URI")),
587
 
            "info" : gtk.MenuItem(_("More Information")),
588
 
            }
589
 
        callbacks = {
590
 
            "open" : self.do_open,
591
 
            "unpin" : self.do_unbookmark,
592
 
            "pin" : self.do_bookmark,
593
 
            "delete" : self.do_delete,
594
 
            "delete_uri" : self.do_delete_events_with_shared_uri,
595
 
            "info" : self.do_show_info,
596
 
            }
597
 
        names = ["open", "unpin", "pin", "delete", "delete_uri", "info"]
598
 
        if is_command_available("nautilus-sendto"):
599
 
            self.menuitems["sendto"] = gtk.MenuItem(_("Send To..."))
600
 
            callbacks["sendto"] = self.do_send_to
601
 
            names.append("sendto")
602
 
        for name in names:
603
 
            item = self.menuitems[name]
604
 
            self.append(item)
605
 
            item.connect("activate", callbacks[name])
606
 
        self.show_all()
607
 
 
608
 
    def do_popup(self, time, subjects):
609
 
        """
610
 
        Call this method to popup the context menu
611
 
 
612
 
        :param time: the event time from the button press event
613
 
        :param subjects: a list of uris
614
 
        """
615
 
        self.subjects = subjects
616
 
        if len(subjects) == 1:
617
 
            uri = subjects[0]
618
 
            if bookmarker.is_bookmarked(uri):
619
 
                self.menuitems["pin"].hide()
620
 
                self.menuitems["unpin"].show()
621
 
            else:
622
 
                self.menuitems["pin"].show()
623
 
                self.menuitems["unpin"].hide()
624
 
 
625
 
        self.popup(None, None, None, 3, time)
626
 
 
627
 
    def do_open(self, menuitem):
628
 
        for obj in self.subjects:
629
 
            obj.launch()
630
 
 
631
 
    def do_show_info(self, menuitem):
632
 
        if self.subjects and self.informationwindow:
633
 
            self.informationwindow.set_content_object(self.subjects[0])
634
 
 
635
 
    def do_bookmark(self, menuitem):
636
 
        for obj in self.subjects:
637
 
            uri = obj.uri
638
 
            uri = unicode(uri)
639
 
            isbookmarked = bookmarker.is_bookmarked(uri)
640
 
            if not isbookmarked:
641
 
                bookmarker.bookmark(uri)
642
 
 
643
 
    def do_unbookmark(self, menuitem):
644
 
        for obj in self.subjects:
645
 
            uri = obj.uri
646
 
            uri = unicode(uri)
647
 
            isbookmarked = bookmarker.is_bookmarked(uri)
648
 
            if isbookmarked:
649
 
                bookmarker.unbookmark(uri)
650
 
 
651
 
    def do_delete(self, menuitem):
652
 
        for obj in self.subjects:
653
 
            CLIENT.delete_events([obj.event.id])
654
 
 
655
 
    def do_delete_events_with_shared_uri(self, menuitem):
656
 
        for uri in map(lambda obj: obj.uri, self.subjects):
657
 
            CLIENT.find_event_ids_for_template(
658
 
                Event.new_for_values(subject_uri=uri),
659
 
                lambda ids: CLIENT.delete_events(map(int, ids)))
660
 
 
661
 
 
662
 
    def do_send_to(self, menuitem):
663
 
        launch_command("nautilus-sendto", map(lambda obj: obj.uri, self.subjects))
664
 
 
665
 
 
666
 
searchbox = SearchBox()
667
 
if gst is not None:
668
 
    VideoPreviewTooltip = VideoPreviewTooltip()
669
 
else:
670
 
    VideoPreviewTooltip = None
671
 
StaticPreviewTooltip = StaticPreviewTooltip()
672
 
ContextMenu = ContextMenu()