On Thu, 2005-10-20 at 15:44 +0200, Mikkel Kamstrup Erlandsen wrote: > Just so you have the "latest and greatest". I still have uri-launching > problems and occasional segfaults. > Attached a version that fixes IM viewing using beagle tool, and fixes URI launching. All in all, if it wasn't a hack, i'd really love it :) This proof of concept could be added as an experimental file, just like debug ones, not installed but shipped as EXTRA_DIST.. I'm seeing if i can make a proper libbeagle binding.. Raf.
import os, sys, signal import popen2 from os.path import expanduser, exists, join from gettext import gettext as _ import gnome, gnome.ui import gtk from gobject import spawn_async, SPAWN_SEARCH_PATH icon_theme = gtk.icon_theme_get_default() thumb_factory = gnome.ui.ThumbnailFactory(16) import deskbar from deskbar.handler import AsyncHandler, Match import deskbar.defs QUERY_DELAY = 0.6 MAX_HITS=2 # Max hits *per backend* def _check_requirements(): return (True, None) HANDLERS = { "BeagleLiveHandler" : { "name": _("Live Beagle Queries"), "description": _("Query Beagle automatically as you type."), "requirements" : _check_requirements } } class UnknownBeagleTask (Exception): pass class NoBeagleDaemon (Exception): pass class BeagleProcess (popen2.Popen3): """ """ # Mental note to self: The beagle-query --max-hits switch sets #max number of hits *per backend*. # Task definitions GET_DAEMON_VERSION = 0 QUERY = 1 def __init__ (self, task, query=None, max_hits=MAX_HITS): if task is self.GET_DAEMON_VERSION: popen2.Popen3.__init__(self, "beagle-info --daemon-version",) return elif task is self.QUERY and query is not None: print "Querying for", query popen2.Popen3.__init__(self, "beagle-query %s --verbose --max-hits %s" % (query, MAX_HITS)) else: raise UnknownBeagleTask() # The TYPES dict contains Beagle HitTypes as keys with # templates for the valid fields. # # A Hit Type consists of: # "name" : Template used to find a user-displayable name # "action" : Command to execute # "icon" : The *name* of the icon to be sued for this type. Set to None for no icon. # "description" : A short description. %(*)s may refer to *=name,uri,action or any field in "extra" (see below) # # Optionally: # "extra" : A dict containing key:template pairs to search for. You can use %(key)s in "description". TYPES = { "Contact" : { "name" : "fixme:FileAs = ", "action": "evolution", "icon" : "stock_contact", "description": "Contact: %(name)s" }, "MailMessage" : { "name" :"dc:title = ", "action": "evolution", "icon" : "stock_mail", "extra": {"sender":"from_name = "}, "description": "Email from %(sender)s: %(name)s" }, "File" : { "name" : "beagle:ExactFilename = ", "action": "gnome-open", "icon" : None, "description": "%(name)s" }, "FeedItem" : { "name" :"dc:title = ", "action": "gnome-open", "icon" : "stock_news", "description": "News: %(name)s",# There don't seem to be a good "sender" template :( }, "Note" : { "name" : "dc:title = ", "action": "tomboy", "action_args": "--open-note", "icon" :"stock_notes", "description": "Note: %(name)s" }, "IMLog" : { "name" : "fixme:speakingto = ", "action": "beagle-imlogviewer", "icon" : "im", "description": "Conversation with %(name)s" } } class BeagleMatch (Match): """ """ def __init__(self, handler, result): """ result: a dict containing: "name" : a name sensible to display for this match "uri": the uri of the match as provided by the beagled 'Uri: '-field "type": One of the types listed in the TYPES dict -- and optionally extra fields as provided by the corresponding entry in TYPES. Fx. "MailMessage". has an extra "sender" entry. """ if result["type"] == "File": try: icon_name, num = gnome.ui.icon_lookup(icon_theme, thumb_factory, file_uri=result["uri"], custom_icon="") icon = icon_theme.load_icon(icon_name, deskbar.ICON_SIZE, gtk.ICON_LOOKUP_USE_BUILTIN) if icon: Match.__init__ (self, handler, result["name"], icon) else: raise Exception() except Exception: Match.__init__ (self, handler, result["name"], handler.ICONS["File"]) print >> sys.stderr, "BeagleLive: Failed to load icon for file %s" % result["uri"] else: # We are not a file. Just use an icon from the ICON table Match.__init__ (self, handler, result["name"], handler.ICONS[result["type"]]) self.__result = result def get_name (self, text=None): # We use the result dict itself to look up words return self.__result def get_verb(self): # Fetch the "description" template from TYPES return TYPES[self.__result["type"]]["description"] def action(self, text=None): # FIXME FIXME FIXME # This looks like it should work, but somehow doesn't. # Retrieve the associated action action = TYPES[self.__result["type"]]["action"] args = [action] # If the result requires additional arguments # prepend them to args. if TYPES[self.__result["type"]].has_key ("action_args"): args.append(TYPES[self.__result["type"]]["action_args"]) # Gross hack, imlogviewer doesn't take URI as args, but a filename if action == "beagle-imlogviewer": # Strip the file:// args.append (self.__result["uri"][7:]) else: args.append (self.__result["uri"]) print "BeagleLive spawning:", action, args os.spawnvp(os.P_NOWAIT, action, args) class BeagleLiveHandler (AsyncHandler): """ """ def __init__ (self): AsyncHandler.__init__ (self) self.__process = None def initialize (self): """ Loads needed icons into memory, also try to connect to the beagle daemon an get its' version number. This method raises a NoBeagleDaemon exception if the beagle daemon is not running. """ # Load icons here. This method is called from the main loop, so it's ok. self.ICONS = self.__load_icons() # Check if beagled is running and get the version process = BeagleProcess (BeagleProcess.GET_DAEMON_VERSION) process.wait() self.__daemon_version = process.fromchild.readline() idx = self.__daemon_version.find(":") if idx == -1: print >> sys.stderr, "BeagleLive: Unable to connect to Beagle daemon!" raise NoBeagleDaemon() self.__daemon_version = self.__daemon_version[idx+2:] print "BeagleLive: Using Beagle daemon version %s"% self.__daemon_version def query (self, qstring, qmax=5): # Delay query self.check_query_changed (timeout=QUERY_DELAY) print "BeagleLive: Querying for %s" % qstring self.__process = BeagleProcess (BeagleProcess.QUERY, qstring, qmax) self.parse (self.__process.fromchild) self.__process.wait() self.__process = None print "BeagleLive: query for '%s' completed" % qstring def __clean_up (self): # If we have a running beagle query, send it a SIGINT if self.__process: os.kill (self.__process.pid, signal.SIGINT) self.__process.wait() self.__process = None print "BeagleLive: Beagle query interupted." def parse (self, pipe): """ All the work is being done here. We basically walk line by line through the beagle-query output and match each line against known keywords. """ result = {} res_type = None # Since the "type" will be really handy, keep a direct ref to it res_extra = None # If the result being parsed need "extra" information parsed. line = "a" while True: line = pipe.readline() if line == "": break line = line.strip() if line.startswith("Uri: ") or line == "": # A new hit is starting or we've hit EOF # Do we have a qualified match? if ( result.has_key("name") and result.has_key("uri") and result.has_key("type")): # We have all we need. # Emit "query-ready" to main loop # if the query is still valid self.check_query_changed(self.__clean_up) match = BeagleMatch(self, result) self.emit_query_ready ([match]) # Reset our result result = {} res_type = None res_extra = None if not result.has_key("uri"): if line.startswith ("Uri: "): result ["uri"] = line[5:] continue if not result.has_key("type"): # Lots of stuff require us to have the 'Type: '-field # at hand. Do all this stuff here, so we don't have to check # for has_key("type") all over the place. if line.startswith ("Type: "): result["type"] = line[6:] res_type = result["type"] # This is for handy reference if TYPES[res_type].has_key ("extra"): res_extra = TYPES[res_type]["extra"] continue else: # Lots of stuff require us to have the 'Type: '-field # at hand. Do all this stuff here, so we don't have to check # for has_key("type") all over the place. if not result.has_key("name"): # Try and look up a name pattern by our type # Since there's no common field for a display_String # or the like we have to heuristically determine one. # Look up a name pattern from NAME_PATTERNS idx = line.find (TYPES[res_type]["name"]) if idx != -1: result["name"] = line[idx+len(TYPES[res_type]["name"]):] continue if res_extra: # This hit type requires us to scan for extra info for extra in res_extra.iterkeys(): if not result.has_key (extra): idx = line.find (res_extra[extra]) if idx != -1: result[extra] = line[idx+len(res_extra[extra]):] def __load_icons (self): res = {} for t in TYPES.iterkeys(): icon_file = TYPES[t]["icon"] if not icon_file: continue try: res[t] = icon_theme.load_icon(icon_file, deskbar.ICON_SIZE, gtk.ICON_LOOKUP_USE_BUILTIN) except Exception: res[t] = None print >> sys.stderr, "BeagleLive: Unable to load icon for %s" % t return res
Attachment:
signature.asc
Description: This is a digitally signed message part