>From 23d604dd929e5b9019313ff045b881b2f18a7158 Mon Sep 17 00:00:00 2001 From: Juan A. Suarez Romero Date: Thu, 25 Mar 2010 14:18:54 +0000 Subject: [PATCH 1/6] Revert "deskbar: Removed, was unmaintained and out of date" This reverts commit ebacdff25adffc89e14dc286d164b5f08b45dcd2. --- configure.ac | 61 +++ src/plugins/Makefile.am | 2 +- src/plugins/deskbar/Makefile.am | 18 + src/plugins/deskbar/tracker-handler-static.py | 71 ++++ src/plugins/deskbar/tracker-handler.py | 403 ++++++++++++++++++++ src/plugins/deskbar/tracker-module.py | 495 +++++++++++++++++++++++++ 6 files changed, 1049 insertions(+), 1 deletions(-) create mode 100644 src/plugins/deskbar/Makefile.am create mode 100644 src/plugins/deskbar/tracker-handler-static.py create mode 100644 src/plugins/deskbar/tracker-handler.py create mode 100644 src/plugins/deskbar/tracker-module.py diff --git a/configure.ac b/configure.ac index 05d6d9c..40ec861 100644 --- a/configure.ac +++ b/configure.ac @@ -876,6 +876,65 @@ fi AM_CONDITIONAL(USING_MINER_RSS, test "x$enable_miner_rss" = "xyes") #################################################################### +# Deskbar Applet Handler/Module +#################################################################### + +AC_ARG_ENABLE([deskbar_applet], + AS_HELP_STRING([--enable-deskbar-applet], + [enable support for Deskbar applet [[default=auto]]]),, + [enable_deskbar_applet=auto]) + +if test "x$enable_deskbar_applet" != "xno" ; then + if pkg-config --atleast-version=2.19 deskbar-applet ; then + have_deskbar_applet="module" + elif pkg-config --atleast-version=2.16 deskbar-applet ; then + have_deskbar_applet="handler" + else + have_deskbar_applet="no" + fi +fi + +if test "x$enable_deskbar_applet" = "xyes"; then + if test "x$have_deskbar_applet" = "xno"; then + AC_MSG_ERROR([Couldn't find deskbar-applet >= 2.16.]) + fi +fi + +AM_CONDITIONAL(HAVE_DESKBAR_APPLET_HANDLER, test "x$have_deskbar_applet" = "xhandler") +AM_CONDITIONAL(HAVE_DESKBAR_APPLET_MODULE, test "x$have_deskbar_applet" = "xmodule") + +AC_ARG_WITH([deskbar_applet_dir], + AS_HELP_STRING([--with-deskbar-applet-dir], + [Path to Deskbar handler/module directory [[default=pkgconfig]]])) + +if test "x$have_deskbar_applet" = "xhandler"; then + if test -z "x$with_deskbar_applet_dir"; then + deskbar_applet_dir="`pkg-config --variable handlersdir deskbar-applet`" + else + deskbar_applet_dir="$with_deskbar_applet_dir" + fi + + if test -z "$deskbar_applet_dir"; then + deskbar_applet_dir="\$(libdir)/deskbar-applet/handlers" + fi +fi + +if test "x$have_deskbar_applet" = "xmodule"; then + if test -z "$with_deskbar_applet_dir"; then + deskbar_applet_dir="`pkg-config --variable modulesdir deskbar-applet`" + else + deskbar_applet_dir="$with_deskbar_applet_dir" + fi + + if test -z "$deskbar_applet_dir"; then + deskbar_applet_dir="\$(libdir)/deskbar-applet/modules-2.20-compatible" + fi +fi + +DESKBAR_APPLET_DIR="$deskbar_applet_dir" +AC_SUBST(DESKBAR_APPLET_DIR) + +#################################################################### # Application and Vala requirements #################################################################### @@ -1692,6 +1751,7 @@ AC_CONFIG_FILES([ src/tracker-utils/Makefile src/tracker-writeback/Makefile src/plugins/Makefile + src/plugins/deskbar/Makefile src/plugins/evolution/Makefile src/plugins/kmail/Makefile src/plugins/nautilus/Makefile @@ -1804,6 +1864,7 @@ Data Miners: Plugins: Nautilus: (tagging widget) $have_nautilus_extension + Deskbar: $have_deskbar_applet Writeback: diff --git a/src/plugins/Makefile.am b/src/plugins/Makefile.am index a549966..c8a0b83 100644 --- a/src/plugins/Makefile.am +++ b/src/plugins/Makefile.am @@ -1,6 +1,6 @@ include $(top_srcdir)/Makefile.decl -SUBDIRS = +SUBDIRS = deskbar if USING_KMAIL_MINER SUBDIRS += kmail diff --git a/src/plugins/deskbar/Makefile.am b/src/plugins/deskbar/Makefile.am new file mode 100644 index 0000000..8cb988d --- /dev/null +++ b/src/plugins/deskbar/Makefile.am @@ -0,0 +1,18 @@ +include $(top_srcdir)/Makefile.decl + +if HAVE_DESKBAR_APPLET_HANDLER +handlerdir = $(DESKBAR_APPLET_DIR) +handler_DATA = \ + tracker-handler.py \ + tracker-handler-static.py +endif + +if HAVE_DESKBAR_APPLET_MODULE +moduledir = $(DESKBAR_APPLET_DIR) +module_DATA = tracker-module.py +endif + +EXTRA_DIST = \ + tracker-handler.py \ + tracker-handler-static.py \ + tracker-module.py diff --git a/src/plugins/deskbar/tracker-handler-static.py b/src/plugins/deskbar/tracker-handler-static.py new file mode 100644 index 0000000..5f79732 --- /dev/null +++ b/src/plugins/deskbar/tracker-handler-static.py @@ -0,0 +1,71 @@ +# -*- coding: utf-8 -*- +# This handler was originaly created by Mikkel Kamstrup (c) 2006 and updated by Eugenio Cutolo (eulin) +# +# The static search Handler was splitted to a separate file by Marcus Fritzsch +# +# This program can be distributed under the terms of the GNU GPL version 2 or later. +# See the file COPYING. +# + +import sys +import os.path +import gnome +import gobject + +import gettext +gettext.install('tracker') + +import deskbar.Handler +import deskbar.Match + + + + +class TrackerSearchToolMatch (deskbar.Match.Match): + + def __init__(self, backend, **args): + deskbar.Match.Match.__init__(self, backend, **args) + self._icon = deskbar.Utils.load_icon ('tracker') + + def action(self, text=None): + try: + gobject.spawn_async(['tracker-search-tool', self.name], flags=gobject.SPAWN_SEARCH_PATH) + except gobject.GError, e: + print >> sys.stderr, "*** Error when executing tracker-search-tool:", e + + def get_verb(self): + return _('Search for %s with Tracker Search Tool') % ('%(name)s') + + def get_category (self): + return 'actions' + + def get_hash (self, text=None): + return 'tst-more-hits-action-'+self.name + + + + +class TrackerSearchToolHandler(deskbar.Handler.Handler): + + def __init__(self): + deskbar.Handler.Handler.__init__(self, 'tracker') + + def query(self, query): + return [TrackerSearchToolMatch(self, name=query)] + + @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 seems not to be installed properly.', None) + + + + +HANDLERS = { + 'TrackerSearchToolHandler': { + 'name': 'Search for files using Tracker Search Tool', + 'description': _('Search all of your documents with Tracker Search Tool'), + 'requirements': TrackerSearchToolHandler.requirements, # XXX makes deskbar 2.18.1 not load the handler!! + }, +} diff --git a/src/plugins/deskbar/tracker-handler.py b/src/plugins/deskbar/tracker-handler.py new file mode 100644 index 0000000..aac5880 --- /dev/null +++ b/src/plugins/deskbar/tracker-handler.py @@ -0,0 +1,403 @@ +# -*- coding: utf-8 -*- +# This handler was originaly created by Mikkel Kamstrup (c) 2006 and updated by Eugenio Cutolo (eulin) +# +# The handler was rewritten and splitted into live and static search by Marcus Fritzsch +# +# This program can be distributed under the terms of the GNU GPL version 2 or later. +# See the file COPYING. +# + +# Notes on URL escaping and quoting: +# * Fields displayed in the deskbar applet should be escaped +# * There _MUST_ be an unescaped 'uri' field in order to open it +# (see also 'escqped_uri' - this URI however should urllib quoted +# - Marcus Fritzsch, fritschy googlemail com, 2007-08-13 + +import re +import cgi +import sys +import os.path +import time +import urllib +import string +import gnome +import gnomedesktop +import gobject + +import gettext +gettext.install('tracker') + +import deskbar, deskbar.Utils +import deskbar.Handler +import deskbar.Match + +# For now description param it's not used +TYPES = { + 'Applications': { + 'description': (_('Launch %s (%s)') % ('%(name)s', '%(app_name)s')), + 'category': 'actions', + }, + + 'GaimConversations': { + 'description': (_('See %s conversation\n%s %s\nfrom %s') % ('%(proto)s', '%(channel)s', '%(conv_to)s', '%(time)s')), + 'category': 'conversations', + 'icon': 'stock_people', + }, + + 'Emails': { + 'description': (_('Email from %s') % '%(publisher)s' ) + '\n%(title)s', + 'category': 'emails', + 'action': { # more actions for different MUAs + 'key': 'mua', # see TrackerLiveSearchMatch.action for a demo + 'Evolution/Email': 'evolution %(uri)s', + 'Modest/Email': 'modest-open %(uri)s', + 'Thunderbird/Email': 'thunderbird -viewtracker %(uri)s', + 'KMail/Email': 'kmail --view %(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', + 'icon': 'image', + }, + + 'Videos': { + 'description': _('Watch video %s\nin %s') % ('%(base)s', '%(dir)s'), + 'category': 'videos', + 'icon': 'video', + }, + + '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': 'places', + 'icon': 'stock_folder', + }, +} + + + + +class TrackerLiveSearchMatch (deskbar.Match.Match): + + def __init__(self, handler,result=None, **args): + deskbar.Match.Match.__init__ (self, handler,name=result['name'], **args) + self.result = result + self.init_names(result['escaped_uri']) + + # Set the match icon + try: + self._icon = deskbar.Utils.load_icon(TYPES[result['type']]['icon']) + except: + 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): + return self.result + + def get_verb(self): + try: + return TYPES[self.result['type']]['description'] + except: + return _('Open file %s\nin %s') % ('%(base)s', '%(dir)s') + + def get_hash(self, text=None): + 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'] + + def action(self, text=None): + try: + if TYPES[self.result['type']].has_key('action'): + if isinstance (TYPES[self.result['type']]['action'], dict): + try: + key = TYPES[self.result['type']]['action']['key'] + cmd = TYPES[self.result['type']]['action'][self.result[key]] + except: + print >> sys.stderr, "Unknown action for URI %s (Error: %s)" % (self.result['uri'], sys.exc_info()[1]) + return + else: + 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 + try: + # deskbar >= 2.17 + deskbar.Utils.spawn_async(cmd) + except AttributeError: + # deskbar <= 2.16 + gobject.spawn_async(cmd, flags=gobject.SPAWN_SEARCH_PATH) + else: + if 'desktop' in self.result: + self.result['desktop'].launch([]) + else: + try: + # deskbar >= 2.17 + deskbar.Utils.url_show ('file://'+self.result['uri']) + except AttributeError: + gnome.url_show('file://'+self.result['uri']) + print 'Opening Tracker hit:', self.result['uri'] + except: + print >> sys.stderr, '*** Could not open URL %s: %s' % (self.result['uri'], sys.exc_info ()[1]) + + def get_category (self): + try: + return TYPES[self.result['type']]['category'] + except: + return 'files' + + def init_names (self, fullpath): + dirname, filename = os.path.split(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.result['base'] = filename + self.result['dir'] = dirname + + + + +class TrackerLiveSearchHandler(deskbar.Handler.SignallingHandler): + + def __init__(self): + deskbar.Handler.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): + if len (info) < 5: + print >> sys.stderr, "*** Hit for Service Emails had incomplete data, ignoring (%s)" % info[0] + return 0 + output['title'] = cgi.escape(info[3]) + output['publisher'] = cgi.escape(info[4]) + output['mua'] = info[2] + if output['mua'] == 'Thunderbird/Email': + output['uri'] = info[0] + return 1 + + def handle_conversation_hits (self, info, output): + m = self.conv_re.match (output['escaped_uri']) + output['channel']=_('with') + output['proto']=output['conv_from']=output['conv_to']=output['time']='' # XXX, never happened during tests + if m: + output['proto'] = cgi.escape (m.group (1)) + output['conv_from'] = urllib.unquote (cgi.escape (m.group (2))) + output['conv_to'] = urllib.unquote (cgi.escape (m.group (3))) + output['time'] = cgi.escape (time_from_purple_log (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]) + + 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 + if len (info) < 6: + print >> sys.stderr, "*** Hit for Service Applications had incomplete data, ignoring (%s)" % info[0] + return 0 + 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'] = info [5] # no escaping, as it is not displayed as a string + + desktop = parse_desktop_file (info[0]) + if not desktop: + print >> sys.stderr, '*** Could not read .desktop file: %s' % info[0] + else: + output['desktop'] = desktop + return 1 + + def receive_hits (self, qstring, hits, max): + matches = [] + + for info in hits: + output = {} + + if len (info) < 2: + print >> sys.stderr, "*** Hit had incomplete data, ignoring" + continue + + info = [str (i) for i in info] + + output['escaped_uri'] = cgi.escape (info[0]) + output['uri'] = url_quote (info[0], ';?:@&=+$,./') + output['name'] = os.path.basename(output['escaped_uri']) + output['type'] = info[1] + + if not TYPES.has_key(output['type']): + output['type'] = 'Files' + + if output['type'] == 'Emails': + if not self.handle_email_hits (info, output): + continue + + elif output['type'] in ('GaimConversations', 'Conversations'): + if not self.handle_conversation_hits (info, output): + continue + + elif output['type'] == 'Applications': + if not self.handle_application_hits (info, output): + continue + + # applications are launched by .desktop file, if not readable: exclude + if output['type'] != 'Applications' or output.has_key ('desktop'): + matches.append(TrackerLiveSearchMatch (self, output)) + + if len (matches): + self.emit_query_ready(qstring, matches) + print 'Tracker response for query "%s" (service %s); %s hits returned, %s shown' % (qstring, hits[0][1], len(hits), len(matches)) + + def recieve_error (self, error): + print >> sys.stderr, '*** Tracker dbus error:', error + + def query (self, qstring, max): + if self.tracker: + try: self.tracker.GetStatus () + except: self.tracker = None # reconnect + if not self.tracker: + try: + print "Connecting to Tracker (first search or tracker-store restarted)" + import dbus + bus = dbus.SessionBus() + self.tracker = bus.get_object('org.freedesktop.Tracker1','/org/freedesktop/Tracker1') + self.search_iface = dbus.Interface(self.tracker, 'org.freedesktop.Tracker1.Search') + self.keywords_iface = dbus.Interface(self.tracker, 'org.freedesktop.Tracker1.Keywords') + self.files_iface = dbus.Interface(self.tracker, 'org.freedesktop.Tracker1.Files') + except: + print >> sys.stderr, '*** DBus connection to tracker failed, check your settings.' + return + for service in TYPES.iterkeys (): + self.search_iface.TextDetailed (-1, service, qstring, 0, max, \ + reply_handler = lambda hits: self.receive_hits (qstring, hits, max), \ + error_handler = self.recieve_error) + print 'Tracker query:', qstring + + @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.Tracker1' 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 = gnomedesktop.item_new_from_file(desktop, 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() != gnomedesktop.TYPE_APPLICATION: + return None + if only_if_visible and desktop.get_boolean(gnomedesktop.KEY_NO_DISPLAY): + return None + return desktop + + + + +def time_from_purple_log (instr): + try: + if instr.find ('+') != -1: # new kind of log timestamp... + return time.strftime ('%c', time.strptime (re.sub (r'\+\d{4}', '', instr), '%Y-%m-%d.%H%M%S%Z')) + else: # ...from ancient times + return time.strftime ('%c', time.strptime (instr, '%Y-%m-%d.%H%M%S')) + except: + print >> sys.stderr, '*** time parsing for purple chat log failed: %s' % sys.exc_info ()[1] + return instr + + + + +def url_quote (instr, safe = '/'): + """A unicode capable quote, see http://bugs.python.org/issue1712522""" + return ''.join (map (lambda x: x in (safe+string.letters+string.digits) and x or ('%%%02X' % ord(x)), instr.encode ('utf-8'))) + + + + + +HANDLERS = { + 'TrackerLiveSearchHandler': { + 'name': 'Search for files using Tracker', + 'description': _('Search all of your documents, as you type'), + 'requirements': TrackerLiveSearchHandler.requirements, + 'categories': { + 'develop': { + 'name': _('Development Files'), + }, + 'music': { + 'name': _('Music'), + }, + 'images': { + 'name': _('Images'), + }, + 'videos': { + 'name': _('Videos'), + }, + }, + }, +} diff --git a/src/plugins/deskbar/tracker-module.py b/src/plugins/deskbar/tracker-module.py new file mode 100644 index 0000000..330ff20 --- /dev/null +++ b/src/plugins/deskbar/tracker-module.py @@ -0,0 +1,495 @@ +# This deskbar module was ported from deskbar <= 2.18 handler by Marcus Fritzsch + +import gnome +import gnomedesktop +import gobject +import re +import sys +import urllib +import string +import time +import cgi +import os.path +import deskbar +import deskbar.core.Utils +import deskbar.interfaces.Module +import deskbar.interfaces.Match +import deskbar.interfaces.Action +from deskbar.core.Utils import is_program_in_path, spawn_async +from deskbar.handlers.actions.OpenWithApplicationAction import \ + OpenWithApplicationAction +from deskbar.handlers.actions.OpenDesktopFileAction import \ + OpenDesktopFileAction +from deskbar.handlers.actions.ShowUrlAction import \ + ShowUrlAction +from deskbar.handlers.actions.ActionsFactory import \ + get_actions_for_uri + +import gettext +gettext.install('tracker') + + +MAX_RESULTS = 10 +HANDLERS = ['TrackerSearchToolHandler', 'TrackerLiveSearchHandler'] + + +class TrackerSearchToolMatch (deskbar.interfaces.Match): + + def __init__(self, **kwargs): + deskbar.interfaces.Match.__init__(self, **kwargs) + self.add_action (TrackerSearchToolAction (self.get_name ())) + self._pixbuf = deskbar.core.Utils.load_icon ('tracker') + + def get_hash (self, text=None): + return 'tst-more-hits-action-'+self.get_name () + + def get_category (self): + return 'actions' + + + + +class TrackerSearchToolAction (deskbar.interfaces.Action): + def __init__(self, name): + deskbar.interfaces.Action.__init__ (self, name) + self.name = name + + def activate(self, text=None): + try: + gobject.spawn_async(['tracker-search-tool', self.name], \ + flags=gobject.SPAWN_SEARCH_PATH) + except gobject.GError, e: + print >> sys.stderr, "*** Error when executing tracker-search-tool:", e + + def get_verb(self): + return _('Search for %s with Tracker Search Tool') % '%(name)s' + + def get_hash (self): + return 't-s-t:'+self.name + + def get_category (self): + return 'actions' + + + + +class TrackerSearchToolHandler(deskbar.interfaces.Module): + INFOS = { + 'icon': deskbar.core.Utils.load_icon ('tracker'), + 'name': _('Tracker Search'), + 'description': _('Search with Tracker Search Tool'), + 'version': '0.6.91', + } + + def __init__(self): + deskbar.interfaces.Module.__init__(self) + + def query(self, query): + self._emit_query_ready (query, [TrackerSearchToolMatch(name=query, priority=self.get_priority ())]) + + @staticmethod + def has_requirements (): + return is_program_in_path ('tracker-search-tool') + + + + +#For now description param it's not used +TYPES = { + 'Applications': { + 'description': (_('Launch %s (%s)') % ('%(name)s', '%(app_name)s') ), + 'category': 'actions', + }, + + 'GaimConversations': { + 'description': (_('See %s conversation\n%s %s\nfrom %s') % ('%(proto)s', '%(channel)s', '%(conv_to)s', '%(time)s')), + 'category': 'conversations', + }, + + 'Emails': { + 'description': (_('Email from %s') % '%(publisher)s' ) + '\n%(title)s', + 'category': 'emails', + 'action': { # more actions for different MUAs + 'key': 'mua', # see TrackerLiveSearchAction.action for a demo + 'Evolution/Email': 'evolution %(uri)s', + 'Modest/Email': 'modest-open %(uri)s', + 'Thunderbird/Email': 'thunderbird -viewtracker %(uri)s', + 'KMail/Email': 'kmail --view %(uri)s', + }, + }, + + 'Music': { + 'description': _('Listen to music %s\nin %s') % ('%(base)s', '%(dir)s'), + 'category': 'music', + #'icon': 'audio-x-generic', + }, + + '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', + 'icon': 'image', + }, + + 'Videos': { + 'description': _('Watch video %s\nin %s') % ('%(base)s', '%(dir)s'), + 'category': 'videos', + #'icon': 'video-x-generic', + }, + + 'Folders': { + 'description': _('Open folder %s\n%s') % ('%(name)s', '%(dir)s/%(name)s'), + 'category': 'places', + }, + + 'Files': { + 'description': _('Open file %s\nin %s') % ('%(base)s', '%(dir)s'), + 'category': 'files', + }, + + 'Extra': { + 'description': _('Search for %s with Tracker Search Tool') % ('%(name)s'), + }, +} + + + + +class TrackerLiveSearchMatch (deskbar.interfaces.Match): + + def __init__(self, result, **args): + deskbar.interfaces.Match.__init__ (self) + self.result = result + try: + desktop = result['desktop'] + del result['desktop'] + except: + desktop = None + + # Set the match icon + try: + self._pixbuf = deskbar.core.Utils.load_icon(TYPES[result['type']]['icon']) + except: + if self.result.has_key ('icon'): + self._pixbuf = deskbar.core.Utils.load_icon_for_desktop_icon (result ['icon']) + else: + if not self.result['type'] in ('GaimConversations', 'Emails'): + try: + self._pixbuf = deskbar.core.Utils.load_icon ('file://'+result['quoted_uri']) + except: + pass # some icons cannot be loaded... (e.g. for non existent file or illegal URI) + + self.add_action (TrackerLiveSearchAction (result, desktop)) + + # Add extra default actions where it makes sense + if not result['type'] in ["Emails", "Applications", "GaimConversations"]: + try: + self.add_all_actions (get_actions_for_uri(result['quoted_uri'])) + except: + print >> sys.stderr, "*** Error when adding all actions for hit %s: %s" % (self.result['uri'], sys.exc_info()[1]) + + def get_name (self, text = None): + return self.get_verb() % self.result + + def get_verb(self): + try: + return TYPES[self.result['type']]['description'] + except: + return _('Open file %s\nin %s') % ('%(base)s', '%(dir)s') + + def get_hash(self, text=None): + return self.result['uri'] + + def get_category (self): + try: + return TYPES[self.result['type']]['category'] + except: + return 'files' + + + + +class TrackerLiveSearchAction (deskbar.interfaces.Action): + + def __init__ (self, result, desktop): + deskbar.interfaces.Action (self) + self.name = result['name'] + self.desktop = desktop + self.result = result + self.init_names (result['uri']) + + def get_name(self, text=None): + return self.result + + def get_hash(self, text=None): + 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'] + + def get_verb(self): + try: + return TYPES[self.result['type']]['description'] + except: + return _('Open file %s\nin %s') % ('%(base)s', '%(dir)s') + + def activate (self, text=None): + try: + if TYPES[self.result['type']].has_key('action'): + if isinstance (TYPES[self.result['type']]['action'], dict): + try: + key = TYPES[self.result['type']]['action']['key'] + cmd = TYPES[self.result['type']]['action'][self.result[key]] + except: + print >> sys.stderr, "Unknown action for URI %s (Error: %s)" % (self.result['uri'], sys.exc_info()[1]) + return + else: + 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 + deskbar.core.Utils.spawn_async(cmd) + else: + if self.desktop: + self.desktop.launch ([]) + else: + deskbar.core.Utils.url_show ('file://'+self.result['quoted_uri']) + print 'Opening Tracker hit:', self.result['quoted_uri'] + except: + print >> sys.stderr, "*** Could not activate Hit %s: %s" % (self.result['uri'], sys.exc_info()[1]) + + def init_names (self, fullpath): + dirname, filename = os.path.split(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.result ['base'] = filename + self.result ['dir'] = dirname + + + + +class TrackerLiveSearchHandler(deskbar.interfaces.Module): + + INFOS = { + 'icon': deskbar.core.Utils.load_icon ('tracker'), + 'name': _('Tracker Live Search'), + 'description': _('Search with Tracker, as you type'), + 'version': '0.6.91', + 'categories': { + 'develop': { + 'name': _('Development Files'), + }, + 'music': { + 'name': _('Music'), + }, + 'images': { + 'name': _('Images'), + }, + 'videos': { + 'name': _('Videos'), + }, + }, + } + + @staticmethod + def has_prerequisites (): + 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.Tracker1' in activatables: + TrackerLiveSearchHandler.INSTRUCTIONS = ('Tracker is not activatable via dbus') + return False + except: + TrackerLiveSearchHandler.INSTRUCTIONS = ('Python dbus.glib bindings not found.') + return False + return True + except: + TrackerLiveSearchHandler.INSTRUCTIONS = ('Python dbus bindings not found.') + return False + + def __init__(self): + deskbar.interfaces.Module.__init__(self) + # initing on search request, see self.query + self.tracker = self.tracker_search = self.tracker_keywords = self.tracker_files = self.search_iface = self.keywords_iface = self.files_iface = None + self.conv_re = re.compile (r'^.*?/logs/([^/]+)/([^/]+)/([^/]+)/(.+?)\.(:?txt|html)$') # all, proto, account, to-whom, time + + def handle_email_hits (self, info, output): + if len (info) < 5: + print >> sys.stderr, "*** Hit for Service Emails had incomplete data, ignoring (%s)" % info[0] + return 0 + output['title'] = info[3] + output['publisher'] = info[4] + output['mua'] = info[2] + return 1 + + 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'] = urllib.unquote (m.group (2)) + output['conv_to'] = urllib.unquote (m.group (3)) + output['time'] = time_from_purple_log (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]) + return 1 + + 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 + if len (info) < 6: + print >> sys.stderr, "*** Hit for Service Applications had incomplete data, ignoring (%s)" % info[0] + return 0 + output['app_name'] = re.sub(r'%\w+', '', info [4]).strip () + output['app_basename'] = os.path.basename (output['app_name']) + output['app_name'] = output['app_name'] + if output['app_basename'] == '': # strange // in app_name, e.g. nautilus burn:/// + output['app_basename'] = output['app_name'] + output['name'] = info [3] + output['icon'] = info [5] + desktop = parse_desktop_file (output['uri']) + if desktop: + output['desktop'] = desktop + return 1 + + def receive_hits (self, qstring, hits, max): + matches = [] + + for info in hits: + output = {} + + if len (info) < 2: + print >> sys.stderr, "*** Hit had incomplete data, ignoring" + continue + + info = [str (i) for i in info] + + output['uri'] = info[0] + output['name'] = os.path.basename(output['uri']) + output['type'] = info[1] + output['quoted_uri'] = url_quote (info[0], ';?@&=+$,./') + + if not TYPES.has_key(output['type']): + output['type'] = 'Files' + + if output['type'] == 'Emails': + if not self.handle_email_hits (info, output): + continue + + elif output['type'] == 'GaimConversations': + if not self.handle_conversation_hits (info, output): + continue + + elif output['type'] == 'Applications': + if not self.handle_application_hits (info, output): + continue + + # applications are launched by .desktop file, if not readable: exclude + if output['type'] != 'Applications' or output.has_key ('desktop'): + matches.append(TrackerLiveSearchMatch (output)) + + if len (matches): + self._emit_query_ready (qstring, matches) + print 'Tracker response for %s; %d hits returned, %d shown' % \ + (qstring, len(hits), len(matches)) + + def receive_error (self, error): + print >> sys.stderr, '*** Tracker dbus error:', error + + def query (self, qstring): + max = MAX_RESULTS + + if self.tracker: + try: self.tracker.GetStatus () + except: self.tracker = None # reconnect + if not self.tracker: + try: + print "Connecting to Tracker (first search or tracker-store restarted)" + import dbus + bus = dbus.SessionBus() + self.tracker = bus.get_object('org.freedesktop.Tracker1', '/org/freedesktop/Tracker1') + self.tracker_search = bus.get_object('org.freedesktop.Tracker1', '/org/freedesktop/Tracker1/Search') + self.search_iface = dbus.Interface(self.tracker_search, 'org.freedesktop.Tracker1.Search') + self.tracker_keywords = bus.get_object('org.freedesktop.Tracker1', '/org/freedesktop/Tracker1/Keywords') + self.keywords_iface = dbus.Interface(self.tracker_keywords, 'org.freedesktop.Tracker1.Keywords') + self.tracker_files = bus.get_object('org.freedesktop.Tracker1', '/org/freedesktop/Tracker1/Files') + self.files_iface = dbus.Interface(self.tracker_files, 'org.freedesktop.Tracker1.Files') + except: + print >> sys.stderr, 'DBus connection to tracker failed, check your settings.' + return + for service in [key for key in TYPES.iterkeys () if key != 'Extra']: + print 'Searching %s' % service + self.search_iface.TextDetailed (-1, service, qstring, 0, max, \ + reply_handler = lambda hits: self.receive_hits(qstring, hits, max), + error_handler = self.receive_error) + print 'Tracker query:', qstring + + + + +# this code is stolen from the programs handler of deskbar +def parse_desktop_file(desktop, only_if_visible=False): + try: + desktop = gnomedesktop.item_new_from_file(desktop, 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() != gnomedesktop.TYPE_APPLICATION: + return None + if only_if_visible and desktop.get_boolean(gnomedesktop.KEY_NO_DISPLAY): + return None + return desktop + +def time_from_purple_log (instr): + try: + if instr.find ('+') != -1: # new kind of log timestamp... + return time.strftime ('%c', time.strptime (re.sub (r'\+\d{4}', '', instr), '%Y-%m-%d.%H%M%S%Z')) + else: # ...from ancient times + return time.strftime ('%c', time.strptime (instr, '%Y-%m-%d.%H%M%S')) + except: + print >> sys.stderr, '*** time parsing for purple chat log failed: %s' % sys.exc_info ()[1] + return instr + +def url_quote (instr, safe = '/'): + """A unicode capable quote, see http://bugs.python.org/issue1712522""" + return ''.join (map (lambda x: x in (safe+string.letters+string.digits) and x or ('%%%02X' % ord(x)), instr.encode ('utf-8'))) -- 1.6.3.3