diff --git a/python/deskbar-handler/tracker-handler.py b/python/deskbar-handler/tracker-handler.py index 72fb72f..13a56d2 100644 --- a/python/deskbar-handler/tracker-handler.py +++ b/python/deskbar-handler/tracker-handler.py @@ -9,168 +9,157 @@ import gnome import gobject from gettext import gettext as _ -import re, cgi +import re, cgi, sys import os.path -import dbus -import deskbar +import deskbar, deskbar.Utils, deskbar.gnomedesktop from deskbar.Handler import SignallingHandler from deskbar.Match import Match #Edit this var for change the numer of output results -MAX_RESULTS = 10 +MAX_RESULTS = 20 -def _check_requirements (): - try: - import dbus - try : - if getattr(dbus, 'version', (0,0,0)) >= (0,41,0): - import dbus.glib - - # Check that Tracker can be started via dbus activation, we will have trouble if it's not - bus = dbus.SessionBus() - proxy_obj = bus.get_object('org.freedesktop.DBus', '/org/freedesktop/DBus') - dbus_iface = dbus.Interface(proxy_obj, 'org.freedesktop.DBus') - activatables = dbus_iface.ListActivatableNames() - if not "org.freedesktop.Tracker" in activatables: - return (deskbar.Handler.HANDLER_IS_NOT_APPLICABLE, "Tracker is not activatable via dbus", None) - - except: - return (deskbar.Handler.HANDLER_IS_NOT_APPLICABLE, "Python dbus.glib bindings not found.", None) - return (deskbar.Handler.HANDLER_IS_HAPPY, None, None) - except: - return (deskbar.Handler.HANDLER_IS_NOT_APPLICABLE, "Python dbus bindings not found.", None) +# XXX: HANDLERS defined at end of file -HANDLERS = { - "TrackerSearchHandler" : { - "name": "Search for files using Tracker Search Tool", - "description": _("Search all of your documents (using Tracker), as you type"), - "requirements" : _check_requirements, +# For now description param it's not used +TYPES = { + 'Applications': { + 'description': (_('Launch %s (%s)') % ('%(name)s', '%(app_name)s')), + 'category': 'applications', }, - "TrackerLiveSearchHandler" : { - "name": "Search for files using Tracker(live result)", - "description": _("Search all of your documents (using Tracker live), as you type"), - "requirements" : _check_requirements, - "categories" : { - "develop" : { - "name": _("Development Files"), - }, - "music" : { - "name": _("Music"), - }, - "images" : { - "name": _("Images"), - }, - "videos" : { - "name": _("Videos"), - }, - }, + + 'GaimConversations': { + 'description': (_('See %s conversation\n%s %s\nfrom %s') % ('%(proto)s', '%(channel)s', '%(conv_to)s', '%(time)s')), + 'category': 'conversations', }, -} -#For now description param it's not used -TYPES = { - "Conversations" : { - "description": (_("See conversations %s") % "%(publisher)s" ) + "\n%(base)s", - "category": "conversations", - }, - "Email" : { - "description": (_("Email from %s") % "%(publisher)s" ) + "\n%(title)s", - "category": "emails", - "action" : "evolution %(uri)s", - "icon" : "stock_mail", - }, - "Music" : { - "description": _("Listen to music %s\nin %s") % ("%(base)s", "%(dir)s"), - "category": "music", - }, - "Documents" : { - "description": _("See document %s\nin %s") % ("%(base)s", "%(dir)s"), - "category": "documents", - }, - "Development Files" : { - "description": _("Open file %s\nin %s") % ("%(base)s", "%(dir)s"), - "category": "develop", - }, - "Images" : { - "description": _("View image %s\nin %s") % ("%(base)s", "%(dir)s"), - "category": "images", - }, - "Videos" : { - "description": _("Watch video %s\nin %s") % ("%(base)s", "%(dir)s"), - "category": "videos", - }, - "Other Files" : { - "description": _("Open file %s\nin %s") % ("%(base)s", "%(dir)s"), - "category": "files", - }, - "Extra" : { - "description": _("See more result with t-s-t"), - }, + 'Email': { + 'description': (_('Email from %s') % '%(publisher)s' ) + '\n%(title)s', + 'category': 'emails', + 'action': 'evolution %(uri)s', + 'icon': 'stock_mail', + }, + + 'Music': { + 'description': _('Listen to music %s\nin %s') % ('%(base)s', '%(dir)s'), + 'category': 'music', + }, + + 'Documents': { + 'description': _('See document %s\nin %s') % ('%(base)s', '%(dir)s'), + 'category': 'documents', + }, + + 'Development': { + 'description': _('Open file %s\nin %s') % ('%(base)s', '%(dir)s'), + 'category': 'develop', + }, + + 'Images': { + 'description': _('View image %s\nin %s') % ('%(base)s', '%(dir)s'), + 'category': 'images', + }, + + 'Videos': { + 'description': _('Watch video %s\nin %s') % ('%(base)s', '%(dir)s'), + 'category': 'videos', + }, + + 'Other Files': { + 'description': _('Open file %s\nin %s') % ('%(base)s', '%(dir)s'), + 'category': 'files', + }, + + 'Folders': { + 'description': _('Open folder %s\n%s') % ('%(name)s', '%(dir)s/%(name)s'), + 'category': 'folders', + }, + + 'Extra': { + 'description': _('See more result with Tracker Search Tool'), + }, } -#STATIC HANDLER--------------------------------- + class TrackerFileMatch (Match): + def __init__(self, backend, **args): deskbar.Match.Match.__init__(self, backend, **args) - + def action(self, text=None): - gobject.spawn_async(["tracker-search-tool", self.name], flags=gobject.SPAWN_SEARCH_PATH) - + gobject.spawn_async(['tracker-search-tool', self.name], flags=gobject.SPAWN_SEARCH_PATH) + def get_verb(self): - return _("Search "+self.name+" with Tracker Search Tool") - + return _('Search %s with Tracker Search Tool') % (''+self.name+'') + def get_category (self): - return "actions" + return 'actions' + + + class TrackerSearchHandler(deskbar.Handler.Handler): + def __init__(self): - deskbar.Handler.Handler.__init__(self, ("system-search", "tracker")) - + deskbar.Handler.Handler.__init__(self, ('system-search', 'tracker')) + def query(self, query): return [TrackerFileMatch(self, name=query)] -#LIVE HANDLER--------------------------------- + @staticmethod + def requirements (): + if deskbar.Utils.is_program_in_path ('tracker-search-tool'): + return (deskbar.Handler.HANDLER_IS_HAPPY, None, None) + return (deskbar.Handler.HANDLER_IS_NOT_APPLICABLE, 'tracker-search-tool not found', None) + + + class TrackerMoreMatch (Match): - def __init__(self, backend, qstring, category="files", **args): + + def __init__(self, backend, qstring, category='files', **args): Match.__init__(self, backend, **args) - self._icon = deskbar.Utils.load_icon("tracker") + self._icon = deskbar.Utils.load_icon('tracker') self.qstring = qstring self.category = category def get_verb(self): - return TYPES["Extra"]["description"] + return TYPES['Extra']['description'] def get_category (self): try: - return TYPES[self.category]["category"] + return TYPES[self.category]['category'] except: - pass + return "files" def action(self, text=None): - gobject.spawn_async(["tracker-search-tool", self.qstring], flags=gobject.SPAWN_SEARCH_PATH) - + gobject.spawn_async(['tracker-search-tool', self.qstring], flags=gobject.SPAWN_SEARCH_PATH) + + + + class TrackerLiveFileMatch (Match): + def __init__(self, handler,result=None, **args): - Match.__init__ (self, handler,name=result["name"], **args) + Match.__init__ (self, handler,name=result['name'], **args) self.result = result self.fullpath = result['uri'] self.init_names() - - self.result["base"] = self.base - self.result["dir"] = self.dir - + + self.result['base'] = self.base + self.result['dir'] = self.dir + # Set the match icon try: - self._icon = deskbar.Utils.load_icon(TYPES[result['type']]["icon"]) + self._icon = deskbar.Utils.load_icon(TYPES[result['type']]['icon']) except: - self._icon = deskbar.Utils.load_icon_for_file(result['uri']) - - print result + if self.result.has_key ('icon'): + self._icon = deskbar.Utils.load_icon_for_desktop_icon (result ['icon']) + else: + self._icon = deskbar.Utils.load_icon_for_file(result['uri']) def get_name(self, text=None): try: @@ -180,21 +169,25 @@ class TrackerLiveFileMatch (Match): def get_verb(self): try: - return TYPES[self.result["type"]]["description"] + return TYPES[self.result['type']]['description'] except: - return _("Open file %s\nin %s") % ("%(base)s", "%(dir)s") - + return _('Open file %s\nin %s') % ('%(base)s', '%(dir)s') + def get_hash(self, text=None): try: + if self.result ['type'] == 'Applications': + # return a name that matches the one returned by the Program handler of deskbar + return 'generic_' + self.result ['app_basename'] return self.result['uri'] except: pass def action(self, text=None): - if TYPES[self.result["type"]].has_key("action"): - cmd = TYPES[self.result["type"]]["action"] + if TYPES[self.result['type']].has_key('action'): + cmd = TYPES[self.result['type']]['action'] cmd = map(lambda arg : arg % self.result, cmd.split()) # we need this to handle spaces correctly - print "Opening Tracker hit with command:", cmd + + print 'Opening Tracker hit with command:', cmd try: # deskbar >= 2.17 deskbar.Utils.spawn_async(cmd) @@ -202,84 +195,237 @@ class TrackerLiveFileMatch (Match): # deskbar <= 2.16 gobject.spawn_async(args, flags=gobject.SPAWN_SEARCH_PATH) else: - try: - # deskbar >= 2.17 - deskbar.Utils.url_show ("file://"+cgi.escape(self.result['uri'])) - except AttributeError: - gnome.url_show("file://"+cgi.escape(self.result['uri'])) - print "Opening Tracker hit:", self.result['uri'] - + if self.result.has_key ('desktop'): + self.result['desktop'].launch([]) + else: + try: + # deskbar >= 2.17 + deskbar.Utils.url_show ('file://'+cgi.escape(self.result['uri'])) + except AttributeError: + gnome.url_show('file://'+cgi.escape(self.result['uri'])) + print 'Opening Tracker hit:', self.result['uri'] + def get_category (self): try: - return TYPES[self.result["type"]]["category"] + return TYPES[self.result['type']]['category'] except: - return "files" + return 'files' def init_names (self): #print "Parsing «%r»" % self.fullpath dirname, filename = os.path.split(self.fullpath) if filename == '': #We had a trailing slash dirname, filename = os.path.split(dirname) - + #Reverse-tilde-expansion home = os.path.normpath(os.path.expanduser('~')) regexp = re.compile(r'^%s(/|$)' % re.escape(home)) dirname = re.sub(regexp, r'~\1', dirname) - + self.dir = dirname self.base = filename + + class TrackerLiveSearchHandler(SignallingHandler): + def __init__(self): - SignallingHandler.__init__(self, "tracker") - bus = dbus.SessionBus() - self.tracker = bus.get_object('org.freedesktop.Tracker','/org/freedesktop/tracker') - self.search_iface = dbus.Interface(self.tracker, 'org.freedesktop.Tracker.Search') - self.keywords_iface = dbus.Interface(self.tracker, 'org.freedesktop.Tracker.Keywords') - self.files_iface = dbus.Interface(self.tracker, 'org.freedesktop.Tracker.Files') + SignallingHandler.__init__(self, 'tracker') + # initing on search request, see self.query + self.tracker = self.search_iface = self.keywords_iface = self.files_iface = None self.set_delay (500) - + self.conv_re = re.compile (r'^.*?/logs/([^/]+)/([^/]+)/([^/]+)/(.+?)\.(:?txt|html)$') # all, proto, account, to-whom, time + + def handle_email_hits (self, info, output): + output['title'] = cgi.escape(info[3]) + output['publisher'] = cgi.escape(info[4]) + + def handle_conversation_hits (self, info, output): + output ['uri'] = info [0] + m = self.conv_re.match (output['uri']) + output['channel']=_('with') + output['proto']=output['conv_from']=output['conv_to']=output['time']='' # XXX, never happened during tests + if m: + output['proto'] = m.group (1) + output['conv_from'] = m.group (2) + output['conv_to'] = m.group (3) + output['time'] = m.group (4) + if output['conv_to'].endswith ('.chat'): + output['channel'] = _('in channel') + output['conv_to'] = output['conv_to'].replace ('.chat','') + if output['proto'] == 'irc': + nick_server = output['conv_from'].split ('@') + if len (nick_server) > 1: + output['conv_to'] = '%s on %s' % (output['conv_to'], nick_server[1]) + # escape those entities, purple uses this to escape / on jabber channel/user conversations + output['uri'] = output['uri'].replace ('%', '%25') + # escape irc channel prefixes, else the path name parsing of stops at '#' (this holds also for the icon search) + output['uri'] = output['uri'].replace ('#', '%23') + + def handle_application_hits (self, info, output): + # print info + # dbus.Array( + # [ + # dbus.String(u'/usr/share/applications/gksu.desktop'), # TrackerUri 0 + # dbus.String(u'Applications'), # TrackerType 1 + # dbus.String(u'Application'), # DesktopType 2 + # dbus.String(u'Root Terminal'), # DesktopName 3 + # dbus.String(u'gksu /usr/bin/x-terminal-emulator'), # DesktopExec 4 + # dbus.String(u'gksu-root-terminal') # DesktopIcon 5 + # ], + # signature=dbus.Signature('s')) + # Strip %U or whatever arguments in Exec field + output['app_name'] = re.sub(r'%\w+', '', info [4]).strip () + output['app_basename'] = cgi.escape (os.path.basename (output['app_name'])) + output['app_name'] = cgi.escape (output['app_name']) + if output['app_basename'] == '': # strange // in app_name, e.g. nautilus burn:/// + output['app_basename'] = output['app_name'] + output['name'] = cgi.escape (info [3]) + output['icon'] = cgi.escape (info [5]) + + desktop = parse_desktop_file (output['uri']) + if not desktop: + print >> sys.stderr, '*** Could not read .desktop file: %s' % info[0] + else: + output['desktop'] = desktop + def recieve_hits (self, qstring, hits, max): matches = [] - self.results = {} - + results = {} + for info in hits: - output = {} + output = {} + output['name'] = os.path.basename(info[0]) output['uri'] = str(cgi.escape(info[0])) output['type'] = info[1] - if TYPES.has_key(output['type']) == 0: - output['type'] = "Other Files" - try: - self.results[output['type']].append(output) - except: - self.results[output['type']] = [output] - - if output["type"] == "Email": - output["title"] = cgi.escape(info[3]) - output["publisher"] = cgi.escape(info[4]) - - for key in self.results.keys(): - for res in self.results[key][0:MAX_RESULTS]: - matches.append(TrackerLiveFileMatch(self,res)) - #if len(self.results[key]) > MAX_RESULTS: - # matches.append( TrackerMoreMatch(self,qstring,key) ) + + if not TYPES.has_key(output['type']): + output['type'] = 'Other Files' + + if output['type'] == 'Email': + self.handle_email_hits (info, output) + + elif output['type'] in ('GaimConversations', 'Conversations'): + self.handle_conversation_hits (info, output) + + elif output['type'] == 'Applications': + self.handle_application_hits (info, output) + + # applications are launched by .desktop file, if not readable: exclude + if output['type'] != 'Applications' or output.has_key ('desktop'): + try: + results[output['type']].append(output) + except: + results[output['type']] = [output] + + for key in results.iterkeys (): + for res in results[key][0:MAX_RESULTS]: + matches.append(TrackerLiveFileMatch(self,res)) + self.emit_query_ready(qstring, matches) - print "Tracker response for %s, - %s hits returned, %s shown" % (qstring, len(hits), len(matches)) - + print 'Tracker response for %s; %d hits returned, %d shown' % (qstring, len(hits), len(matches)) + def recieve_error (self, error): - print "*** Tracker dbus error:", error - + print >> sys.stderr, '*** Tracker dbus error:', error + def query (self, qstring, max): - if qstring.count("tag:") == 0: - self.search_iface.TextDetailed (-1, "Files", qstring, 0,10, reply_handler=lambda hits : self.recieve_hits(qstring, hits, max), error_handler=self.recieve_error) - self.search_iface.TextDetailed (-1, "Emails", qstring, 0,10, reply_handler=lambda hits : self.recieve_hits(qstring, hits, max), error_handler=self.recieve_error) - print "Tracker query:", qstring + if not self.tracker: + try: + import dbus + bus = dbus.SessionBus() + self.tracker = bus.get_object('org.freedesktop.Tracker','/org/freedesktop/tracker') + self.search_iface = dbus.Interface(self.tracker, 'org.freedesktop.Tracker.Search') + self.keywords_iface = dbus.Interface(self.tracker, 'org.freedesktop.Tracker.Keywords') + self.files_iface = dbus.Interface(self.tracker, 'org.freedesktop.Tracker.Files') + except: + print >> sys.stderr, 'DBus connection to tracker failed, check your settings.' + return + if qstring.count('tag:') == 0: + for service in ('Files', 'Emails', 'Conversations', 'Applications'): + self.search_iface.TextDetailed (-1, service, qstring, 0,10, reply_handler=lambda hits : self.recieve_hits(qstring, hits, max), error_handler=self.recieve_error) + print 'Tracker query:', qstring else: if self.tracker.GetVersion() == 502: - self.search_iface.Query(-1,"Files",["File.Format"],"",qstring.replace("tag:",""),"",False,0,100, reply_handler=lambda hits : self.recieve_hits(qstring, hits, max), error_handler=self.recieve_error) + self.search_iface.Query(-1,'Files',['File.Format'],'',qstring.replace('tag:',''),'',False,0,100, reply_handler=lambda hits : self.recieve_hits(qstring, hits, max), error_handler=self.recieve_error) elif self.tracker.GetVersion() == 503: - self.search_iface.Query(-1,"Files",["File:Mime"],"",qstring.replace("tag:",""),"",False,0,100, reply_handler=lambda hits : self.recieve_hits(qstring, hits, max), error_handler=self.recieve_error) - print "Tracker tag query:", qstring.replace("tag:","") + self.search_iface.Query(-1,'Files',['File:Mime'],'',qstring.replace('tag:',''),'',False,0,100, reply_handler=lambda hits : self.recieve_hits(qstring, hits, max), error_handler=self.recieve_error) + print 'Tracker tag query:', qstring.replace('tag:','') + + @staticmethod + def requirements (): + try: + import dbus + try : + if getattr(dbus, 'version', (0,0,0)) >= (0,41,0): + import dbus.glib + + # Check that Tracker can be started via dbus activation, we will have trouble if it's not + bus = dbus.SessionBus() + proxy_obj = bus.get_object('org.freedesktop.DBus', '/org/freedesktop/DBus') + dbus_iface = dbus.Interface(proxy_obj, 'org.freedesktop.DBus') + activatables = dbus_iface.ListActivatableNames() + if not 'org.freedesktop.Tracker' in activatables: + return (deskbar.Handler.HANDLER_IS_NOT_APPLICABLE, 'Tracker is not activatable via dbus', None) + except: + return (deskbar.Handler.HANDLER_IS_NOT_APPLICABLE, 'Python dbus.glib bindings not found.', None) + return (deskbar.Handler.HANDLER_IS_HAPPY, None, None) + except: + return (deskbar.Handler.HANDLER_IS_NOT_APPLICABLE, 'Python dbus bindings not found.', None) + + + + +# this code is stolen from the programs handler of deskbar +def parse_desktop_file(desktop, only_if_visible=False): + try: + desktop = deskbar.gnomedesktop.item_new_from_file(desktop, deskbar.gnomedesktop.LOAD_ONLY_IF_EXISTS) + except Exception, e: + print 'Couldn\'t read desktop file:%s:%s' % (desktop, e) + return None + if desktop == None or desktop.get_entry_type() != deskbar.gnomedesktop.TYPE_APPLICATION: + return None + if only_if_visible and desktop.get_boolean(deskbar.gnomedesktop.KEY_NO_DISPLAY): + return None + return desktop + + + +HANDLERS = { + 'TrackerSearchHandler': { + 'name': 'Search for files using Tracker Search Tool', + 'description': _('Search all of your documents with Tracker Search Tool'), + 'requirements': TrackerSearchHandler.requirements, + }, + + 'TrackerLiveSearchHandler': { + 'name': 'Search for files using Tracker(live result)', + 'description': _('Search all of your documents (using Tracker live), as you type'), + 'requirements': TrackerLiveSearchHandler.requirements, + 'categories': { + 'develop': { + 'name': _('Development Files'), + }, + 'music': { + 'name': _('Music'), + }, + 'images': { + 'name': _('Images'), + }, + 'videos': { + 'name': _('Videos'), + }, + 'conversations': { + 'name': _('Conversations'), + }, + 'applications': { + 'name': _('Applications'), + }, + 'folders': { + 'name': _('Folders'), + }, + }, + }, +}