import cgi
import deskbar
import gnome
import gobject
import os.path
import re
import sys
import time
import urllib
from gettext import gettext as _
# do the version dance!
import deskbar
_get_deskbar_version = lambda x: sum(map(lambda x: (10**(2*x[0]))*int(x[1]),zip((4,3,2,1),x.split('.'))))
try:
DESKBAR_VERSION = _get_deskbar_version (deskbar.VERSION)
except:
import deskbar.defs
DESKBAR_VERSION = _get_deskbar_version (deskbar.defs.VERSION)
MAX_RESULTS = 10
def dummy_escape (s):
return s
#return cgi.escape (s)
# 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
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
class TSTHandlerImpl (object):
def __init__(self):
pass
@staticmethod
def has_requirements ():
if DESKBAR_VERSION < _get_deskbar_version ('2.19'):
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)
else:
return deskbar.core.Utils.is_program_in_path ('tracker-search-tool')
class TSTMatchImpl (object):
def __init__ (self):
pass
def get_category (self):
return 'actions'
def get_hash (self, text=None):
return 'tst-more-hits-action-'+self.name
class LiveMatchImpl (object):
def __init__ (self):
pass
def init_names (self):
#print "Parsing ..." % 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
def wrap_get_name (self, text = None):
return self.result
def wrap_get_verb(self):
try:
return TYPES[self.result['type']]['description']
except:
return _('Open file %s\nin %s') % ('%(base)s', '%(dir)s')
def wrap_get_category (self):
try:
return TYPES[self.result['type']]['category']
except:
return 'files'
def wrap_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']
class LiveHandlerImpl (object):
def __init__ (self):
self.tracker = 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
try:
self.have_tst = deskbar.Utils.is_program_in_path ('tracker-search-tool')
except:
self.have_tst = deskbar.core.Utils.is_program_in_path ('tracker-search-tool')
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 ['name'] = output ['name'].replace ('%2b', '+')
output ['uri'] = info [0]
uri = info[0]
m = self.conv_re.match (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])
output['uri'] = urllib.quote (output['uri'])
# 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 receive_hits (self, qstring, hits, max):
matches = []
for info in hits:
output = {}
output['name'] = os.path.basename(info[0])
output['uri'] = cgi.escape(str (info[0]))
output['type'] = str (info[1])
if not TYPES.has_key(output['type']):
output['type'] = 'Files'
if output['type'] == 'Emails':
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'):
if DESKBAR_VERSION < _get_deskbar_version ('2.19'):
matches.append(TrackerLiveSearchMatch218 (self, output))
else:
matches.append(TrackerLiveSearchMatch220 (output))
if len (matches):
self.wrap_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 wrap_emit_query_ready (self, qstring, matches):
try:
self._emit_query_ready (qstring, matches)
except:
self.emit_query_ready (qstring, matches)
def wrap_query (self, qstring, max=MAX_RESULTS):
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
for service in [key for key in TYPES.iterkeys () if key != 'Extra']:
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
if self.have_tst:
if DESKBAR_VERSION < _get_deskbar_version ('2.19'):
self.wrap_emit_query_ready (qstring, [TrackerSearchToolMatch218 (self, name=qstring)])
else:
self.wrap_emit_query_ready (qstring, [TrackerSearchToolMatch220 (name=qstring, priority=self.get_priority ())])
TYPES = {
'Applications': {
'description': (_('Launch %s (%s)') % ('%(name)s', '%(app_name)s')),
'category': 'actions',
#'icon': 'stock_run',
},
'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': 'evolution %(uri)s',
'icon': 'stock_mail',
},
'Music': {
'description': _('Listen to music %s\nin %s') % ('%(base)s', '%(dir)s'),
'category': 'music',
#'icon': 'audio',
},
'Documents': {
'description': _('See document %s\nin %s') % ('%(base)s', '%(dir)s'),
'category': 'documents',
#'icon': 'file',
},
'Development': {
'description': _('Open file %s\nin %s') % ('%(base)s', '%(dir)s'),
'category': 'develop',
#'icon': 'file',
},
'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',
#'icon': 'file',
},
'Folders': {
'description': _('Open folder %s\n%s') % ('%(name)s', '%(dir)s/%(name)s'),
'category': 'places',
'icon': 'stock_folder',
},
'Extra': {
'description': _('Search for %s with Tracker Search Tool') % ('%(name)s'),
},
}
#
#
# BEGIN DESKBAR < 2.19 CLASSES
#
#
if DESKBAR_VERSION < _get_deskbar_version ('2.19'):
import deskbar, deskbar.Utils, deskbar.gnomedesktop
import deskbar.Handler
import deskbar.Match
class TrackerSearchToolMatch218 (deskbar.Match.Match, TSTMatchImpl):
def __init__(self, backend, **args):
deskbar.Match.Match.__init__(self, backend, **args)
TSTMatchImpl.__init__ (self)
def action(self, text=None):
gobject.spawn_async(['tracker-search-tool', self.name], flags=gobject.SPAWN_SEARCH_PATH)
def get_verb(self):
return _('Search for %s with Tracker Search Tool') % '%(name)s'
class TrackerSearchToolHandler218 (deskbar.Handler.Handler, TSTHandlerImpl):
def __init__(self):
deskbar.Handler.Handler.__init__(self, 'tracker')
TSTHandlerImpl.__init__ (self)
def query(self, query):
return [TrackerSearchToolMatch218 (self, name=query)]
class TrackerLiveSearchMatch218 (deskbar.Match.Match, LiveMatchImpl):
def __init__(self, handler,result=None, **args):
deskbar.Match.Match.__init__ (self, handler,name=result['name'], **args)
LiveMatchImpl.__init__ (self)
self.result = result
self.fullpath = result['uri']
self.init_names()
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'])
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 action(self, text=None):
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
try:
# deskbar >= 2.17
deskbar.Utils.spawn_async(cmd)
except AttributeError:
# deskbar <= 2.16
gobject.spawn_async(args, flags=gobject.SPAWN_SEARCH_PATH)
else:
if self.result.has_key ('desktop'):
self.result['desktop'].launch([])
else:
try: # catch errors on gnome.url_show()
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']
except:
print >> sys.stderr, '*** Could not open URL %s: %s' % (self.result['uri'], sys.exc_info ()[1])
def get_name (self, text = None):
return self.wrap_get_name (text)
def get_verb(self):
return self.wrap_get_verb ()
def get_category (self):
return self.wrap_get_category ()
def get_hash(self, text=None):
return self.wrap_get_hash (text)
class TrackerLiveSearchHandler218 (deskbar.Handler.SignallingHandler, LiveHandlerImpl):
def __init__(self):
deskbar.Handler.SignallingHandler.__init__(self, 'tracker')
LiveHandlerImpl.__init__ (self)
# initing on search request, see self.query
self.set_delay (500)
def query (self, qstring, max):
self.wrap_query (qstring, max)
@staticmethod
def has_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)
HANDLERS = {
'TrackerSearchToolHandler218': {
'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!!
},
'TrackerLiveSearchHandler218': {
'name': 'Search for files using Tracker',
'description': _('Search all of your documents, as you type'),
'requirements': TrackerLiveSearchHandler218.has_requirements,
'categories': {
'develop': {
'name': _('Development Files'),
},
'music': {
'name': _('Music'),
},
'images': {
'name': _('Images'),
},
'videos': {
'name': _('Videos'),
},
'conversations': {
'name': _('Conversations'),
},
'applications': {
'name': _('Applications'),
},
'places': {
'name': _('Places'),
},
'actions': {
'name': _('Actions'),
},
},
},
}
#
#
# BEGIN DESKBAR >= 2.19 CLASSES
#
#
else:
import deskbar.core.Utils
import deskbar.core.gnomedesktop
import deskbar.interfaces.Module
import deskbar.interfaces.Match
import deskbar.interfaces.Action
from gettext import gettext as _
HANDLERS = ['TrackerSearchToolHandler220', 'TrackerLiveSearchHandler220']
class TrackerSearchToolMatch220 (deskbar.interfaces.Match, TSTMatchImpl):
def __init__(self, **kwargs):
deskbar.interfaces.Match.__init__(self, **kwargs)
TSTMatchImpl.__init__ (self)
self.name = kwargs['name']
self.add_action (TrackerSearchToolAction220 (kwargs['name']))
class TrackerSearchToolAction220 (deskbar.interfaces.Action):
def __init__(self, name):
deskbar.interfaces.Action.__init__ (self, name)
self.name = name
def activate(self, text=None):
gobject.spawn_async(['tracker-search-tool', self.name], \
flags=gobject.SPAWN_SEARCH_PATH)
def get_verb(self):
return _('Search for %s with Tracker Search Tool') % '%(name)s'
def get_hash (self):
return 't-s-t:'+self.name
class TrackerSearchToolHandler220 (deskbar.interfaces.Module, TSTHandlerImpl):
INFOS = {
'icon': deskbar.core.Utils.load_icon ('tracker'),
'name': _('Tracker Search'),
'description': _('Search with Tracker Search Tool'),
}
def __init__(self):
deskbar.interfaces.Module.__init__(self)
TSTHandlerImpl.__init__ (self)
def query(self, query):
self._emit_query_ready (query, [TrackerSearchToolMatch220(name=query, priority=self.get_priority ())])
class TrackerLiveSearchMatch220 (deskbar.interfaces.Match, LiveMatchImpl):
def __init__(self, result, **args):
deskbar.interfaces.Match.__init__ (self)
LiveMatchImpl.__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 self.result['type'] != 'Conversations':
try:
self._pixbuf = deskbar.core.Utils.load_icon ('file://'+result['uri'])
except:
pass # some icons cannot be loaded... (e.g. for non existent file or illegal URI)
self.add_action (TrackerLiveSearchAction220 (result, desktop))
#if result['type'] == 'Images':
# for prg in ('gimp', 'gthumb', 'gwenview'):
# if is_program_in_path (prg):
# self.add_action (OpenWithApplicationAction (result['name'], prg, [result['uri']]))
def get_name (self, text = None):
return self.result ['name']
def get_verb(self):
return self.wrap_get_verb ()
def get_category (self):
return self.wrap_get_category ()
def get_hash(self, text=None):
return self.wrap_get_hash (text)
class TrackerLiveSearchAction220 (deskbar.interfaces.Action, LiveMatchImpl):
def __init__ (self, result, desktop):
deskbar.interfaces.Action (self)
LiveMatchImpl.__init__ (self)
self.name = result['name']
self.desktop = desktop
self.result = result
self.fullpath = result['uri']
self.init_names()
self.result['base'] = self.base
self.result['dir'] = self.dir
def get_name(self, text=None):
return self.wrap_get_name (text)
def get_hash(self, text=None):
return self.wrap_get_hash (text)
def get_verb(self):
return self.wrap_get_verb ()
def activate (self, text=None):
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
deskbar.core.Utils.spawn_async(cmd)
else:
if self.desktop:
self.desktop.launch ([])
else:
deskbar.core.Utils.url_show ('file://'+dummy_escape(self.result['uri']))
print 'Opening Tracker hit:', self.result['uri']
class TrackerLiveSearchHandler220 (deskbar.interfaces.Module, LiveHandlerImpl):
INFOS = {
'icon': deskbar.core.Utils.load_icon ('tracker'),
'name': _('Tracker Live Search'),
'description': _('Search with Tracker, as you type'),
'categories': {
'develop': {
'name': _('Development Files'),
},
'music': {
'name': _('Music'),
},
'images': {
'name': _('Images'),
},
'videos': {
'name': _('Videos'),
},
'folders': {
'name': _('Folders'),
},
},
}
def __init__(self):
deskbar.interfaces.Module.__init__(self)
LiveHandlerImpl.__init__ (self)
def query (self, qstring):
return self.wrap_query (qstring, MAX_RESULTS)
@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.Tracker' in activatables:
TrackerLiveSearchHandler220.INSTRUCTIONS = ('Tracker is not activatable via dbus')
return False
except:
TrackerLiveSearchHandler220.INSTRUCTIONS = ('Python dbus.glib bindings not found.')
return False
return True
except:
TrackerLiveSearchHandler220.INSTRUCTIONS = ('Python dbus bindings not found.')
return False