Hey there.
It's already late - I succeeded at getting 2 deskbar version handlers
into one .py - the problem is, the code is rather ugly, just by
multiple inheritance the problem is not solved, one needs
Implementation classes that wrap the actual work.
I don't know if this one is better suited, it certainly gets us away
from those '2 handlers'. The other benefit from that, fixes and
updates to one handler may automagically work also for the second one
(as i had to port my recent changes from handler1 to handler2).
Cheers, Marcus
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') % ('<b>%(base)s</b>', '<i>%(dir)s</i>')
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)') % ('<b>%(name)s</b>', '%(app_name)s')),
'category': 'actions',
#'icon': 'stock_run',
},
'GaimConversations': {
'description': (_('See %s conversation\n%s %s\nfrom %s') % ('<b>%(proto)s</b>', '%(channel)s', '<b>%(conv_to)s</b>', '<i>%(time)s</i>')),
'category': 'conversations',
'icon': 'stock_people',
},
'Emails': {
'description': (_('Email from %s') % '<i>%(publisher)s</i>' ) + '\n<b>%(title)s</b>',
'category': 'emails',
'action': 'evolution %(uri)s',
'icon': 'stock_mail',
},
'Music': {
'description': _('Listen to music %s\nin %s') % ('<b>%(base)s</b>', '<i>%(dir)s</i>'),
'category': 'music',
#'icon': 'audio',
},
'Documents': {
'description': _('See document %s\nin %s') % ('<b>%(base)s</b>', '<i>%(dir)s</i>'),
'category': 'documents',
#'icon': 'file',
},
'Development': {
'description': _('Open file %s\nin %s') % ('<b>%(base)s</b>', '<i>%(dir)s</i>'),
'category': 'develop',
#'icon': 'file',
},
'Images': {
'description': _('View image %s\nin %s') % ('<b>%(base)s</b>', '<i>%(dir)s</i>'),
'category': 'images',
'icon': 'image',
},
'Videos': {
'description': _('Watch video %s\nin %s') % ('<b>%(base)s</b>', '<i>%(dir)s</i>'),
'category': 'videos',
'icon': 'video',
},
'Files': {
'description': _('Open file %s\nin %s') % ('<b>%(base)s</b>', '<i>%(dir)s</i>'),
'category': 'files',
#'icon': 'file',
},
'Folders': {
'description': _('Open folder %s\n%s') % ('<b>%(name)s</b>', '<i>%(dir)s/%(name)s</i>'),
'category': 'places',
'icon': 'stock_folder',
},
'Extra': {
'description': _('Search for %s with Tracker Search Tool') % ('<b>%(name)s</b>'),
},
}
#
#
# 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') % '<b>%(name)s</b>'
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, <b>as you type</b>'),
'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') % '<b>%(name)s</b>'
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
_______________________________________________
tracker-list mailing list
[email protected]
http://mail.gnome.org/mailman/listinfo/tracker-list